diff --git a/.devcontainer/Dockerfile b/.devcontainer/Dockerfile index 9be15a6afa1e..d68cd2b2f9ca 100644 --- a/.devcontainer/Dockerfile +++ b/.devcontainer/Dockerfile @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/devcontainers/typescript-node:20-bookworm +FROM mcr.microsoft.com/devcontainers/typescript-node:24-bookworm@sha256:634d3ffc8c37508259a23462b838fd85a9e5bb80dcdd0b31d673aa33dc4c8592 ADD install-vscode.sh /root/ RUN /root/install-vscode.sh diff --git a/.eslint-ignore b/.eslint-ignore index 91b5117c20a8..da66a76c3960 100644 --- a/.eslint-ignore +++ b/.eslint-ignore @@ -13,6 +13,8 @@ **/extensions/notebook-renderers/renderer-out/index.js **/extensions/simple-browser/media/index.js **/extensions/terminal-suggest/src/completions/upstream/** +**/extensions/terminal-suggest/src/shell/zshBuiltinsCache.ts +**/extensions/terminal-suggest/src/shell/fishBuiltinsCache.ts **/extensions/terminal-suggest/third_party/** **/extensions/typescript-language-features/test-workspace/** **/extensions/typescript-language-features/extension.webpack.config.js diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 5ace4600a1f2..1e1402fa5aea 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -4,3 +4,594 @@ updates: directory: "/" schedule: interval: "weekly" + + + - package-ecosystem: docker + directory: /.devcontainer + schedule: + interval: daily + + - package-ecosystem: npm + directory: /.eslint-plugin-local + schedule: + interval: daily + + - package-ecosystem: npm + directory: /.vscode/extensions/vscode-selfhost-import-aid + schedule: + interval: daily + + - package-ecosystem: npm + directory: /.vscode/extensions/vscode-selfhost-test-provider + schedule: + interval: daily + + - package-ecosystem: npm + directory: /build/builtin + schedule: + interval: daily + + - package-ecosystem: npm + directory: /build/monaco + schedule: + interval: daily + + - package-ecosystem: npm + directory: /build/npm/gyp + schedule: + interval: daily + + - package-ecosystem: npm + directory: /build + schedule: + interval: daily + + - package-ecosystem: cargo + directory: /build/win32 + schedule: + interval: daily + + - package-ecosystem: cargo + directory: /cli + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/bat + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/clojure + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/coffeescript + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/configuration-editing + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/cpp + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/csharp + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/css-language-features + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/css-language-features/server + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/css + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/dart + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/debug-auto-launch + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/debug-server-ready + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/diff + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/docker + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/emmet + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/extension-editing + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/fsharp + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/git-base + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/git + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/github-authentication + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/github + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/go + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/groovy + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/grunt + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/gulp + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/handlebars + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/hlsl + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/html-language-features + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/html-language-features/server + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/html + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/ini + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/ipynb + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/jake + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/java + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/javascript + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/json-language-features + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/json-language-features/server + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/json + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/julia + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/latex + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/less + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/log + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/lua + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/make + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/markdown-basics + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/markdown-language-features + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/markdown-math + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/media-preview + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/merge-conflict + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/microsoft-authentication + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/microsoft-authentication/packageMocks/keytar + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/notebook-renderers + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/npm + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/objective-c + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/perl + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/php-language-features + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/php + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/powershell + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/pug + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/python + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/r + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/razor + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/references-view + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/restructuredtext + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/ruby + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/rust + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/scss + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/search-result + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/shaderlab + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/shellscript + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/simple-browser + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/sql + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/swift + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/terminal-suggest + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/theme-abyss + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/theme-defaults + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/theme-kimbie-dark + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/theme-monokai-dimmed + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/theme-monokai + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/theme-quietlight + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/theme-red + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/theme-seti + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/theme-solarized-dark + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/theme-solarized-light + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/theme-tomorrow-night-blue + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/tunnel-forwarding + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/typescript-basics + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/typescript-language-features + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/vb + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/vscode-api-tests + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/vscode-colorize-perf-tests + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/vscode-colorize-tests + schedule: + interval: daily + + - package-ecosystem: docker + directory: /extensions/vscode-colorize-tests/test/colorize-fixtures + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/vscode-test-resolver + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/xml + schedule: + interval: daily + + - package-ecosystem: npm + directory: /extensions/yaml + schedule: + interval: daily + + - package-ecosystem: npm + directory: / + schedule: + interval: daily + + - package-ecosystem: npm + directory: /remote + schedule: + interval: daily + + - package-ecosystem: npm + directory: /remote/web + schedule: + interval: daily + + - package-ecosystem: npm + directory: /scripts + schedule: + interval: daily + + - package-ecosystem: npm + directory: /test/automation + schedule: + interval: daily + + - package-ecosystem: npm + directory: /test/integration/browser + schedule: + interval: daily + + - package-ecosystem: npm + directory: /test/leaks + schedule: + interval: daily + + - package-ecosystem: npm + directory: /test/monaco + schedule: + interval: daily + + - package-ecosystem: npm + directory: /test + schedule: + interval: daily + + - package-ecosystem: npm + directory: /test/smoke + schedule: + interval: daily + + - package-ecosystem: npm + directory: /test/unit/node + schedule: + interval: daily diff --git a/.github/workflows/basic.yml b/.github/workflows/basic.yml deleted file mode 100644 index d5f166a4aae8..000000000000 --- a/.github/workflows/basic.yml +++ /dev/null @@ -1,176 +0,0 @@ -name: Basic checks - -on: workflow_dispatch - -# on: -# push: -# branches: -# - main -# pull_request: -# branches: -# - main - -jobs: - main: - if: github.ref != 'refs/heads/main' - name: Compilation, Unit and Integration Tests - runs-on: ubuntu-latest - timeout-minutes: 40 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - steps: - - uses: actions/checkout@v4 - - # TODO: rename azure-pipelines/linux/xvfb.init to github-actions - - name: Setup Build Environment - run: | - 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 - - - uses: actions/setup-node@v4 - with: - node-version-file: .nvmrc - - - name: Compute node modules cache key - id: nodeModulesCacheKey - run: echo "value=$(node build/azure-pipelines/common/computeNodeModulesCacheKey.js)" >> $GITHUB_OUTPUT - - name: Cache node modules - id: cacheNodeModules - uses: actions/cache@v4 - with: - path: "**/node_modules" - key: ${{ runner.os }}-cacheNodeModulesLinux-${{ steps.nodeModulesCacheKey.outputs.value }} - - name: Get npm cache directory path - id: npmCacheDirPath - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - run: echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT - - name: Cache npm directory - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - uses: actions/cache@v4 - with: - path: ${{ steps.npmCacheDirPath.outputs.dir }} - key: ${{ runner.os }}-npmCacheDir-${{ steps.nodeModulesCacheKey.outputs.value }} - restore-keys: ${{ runner.os }}-npmCacheDir- - - name: Execute npm - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - env: - PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 - ELECTRON_SKIP_BINARY_DOWNLOAD: 1 - run: npm ci - - - name: Compile and Download - run: npm exec -- npm-run-all -lp compile "electron x64" - - - name: Run Unit Tests - id: electron-unit-tests - run: DISPLAY=:10 ./scripts/test.sh - - - name: Run Integration Tests (Electron) - id: electron-integration-tests - run: DISPLAY=:10 ./scripts/test-integration.sh - - hygiene: - if: github.ref != 'refs/heads/main' - name: Hygiene and Layering - runs-on: ubuntu-latest - timeout-minutes: 40 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - steps: - - uses: actions/checkout@v4 - - - uses: actions/setup-node@v4 - with: - node-version-file: .nvmrc - - - name: Compute node modules cache key - id: nodeModulesCacheKey - run: echo "value=$(node build/azure-pipelines/common/computeNodeModulesCacheKey.js)" >> $GITHUB_OUTPUT - - name: Cache node modules - id: cacheNodeModules - uses: actions/cache@v4 - with: - path: "**/node_modules" - key: ${{ runner.os }}-cacheNodeModulesLinux-${{ steps.nodeModulesCacheKey.outputs.value }} - - name: Get npm cache directory path - id: npmCacheDirPath - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - run: echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT - - name: Cache npm directory - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - uses: actions/cache@v4 - with: - path: ${{ steps.npmCacheDirPath.outputs.dir }} - key: ${{ runner.os }}-npmCacheDir-${{ steps.nodeModulesCacheKey.outputs.value }} - restore-keys: ${{ runner.os }}-npmCacheDir- - - name: Execute npm - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - env: - PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 - ELECTRON_SKIP_BINARY_DOWNLOAD: 1 - run: npm ci - - - name: Run Hygiene Checks - run: npm run gulp hygiene - - - name: Run Valid Layers Checks - run: npm run valid-layers-check - - - name: Compile /build/ - run: npm run compile - working-directory: build - - - name: Check clean git state - run: ./.github/workflows/check-clean-git-state.sh - - - name: Run eslint - run: npm run eslint - - - name: Run vscode-dts Compile Checks - run: npm run vscode-dts-compile-check - - - name: Run Trusted Types Checks - run: npm run tsec-compile-check - - warm-cache: - name: Warm up node modules cache - if: github.ref == 'refs/heads/main' - runs-on: ubuntu-latest - timeout-minutes: 40 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - steps: - - uses: actions/checkout@v4 - - - uses: actions/setup-node@v4 - with: - node-version-file: .nvmrc - - - name: Compute node modules cache key - id: nodeModulesCacheKey - run: echo "value=$(node build/azure-pipelines/common/computeNodeModulesCacheKey.js)" >> $GITHUB_OUTPUT - - name: Cache node modules - id: cacheNodeModules - uses: actions/cache@v4 - with: - path: "**/node_modules" - key: ${{ runner.os }}-cacheNodeModulesLinux-${{ steps.nodeModulesCacheKey.outputs.value }} - - name: Get npm cache directory path - id: npmCacheDirPath - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - run: echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT - - name: Cache npm directory - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - uses: actions/cache@v4 - with: - path: ${{ steps.npmCacheDirPath.outputs.dir }} - key: ${{ runner.os }}-npmCacheDir-${{ steps.nodeModulesCacheKey.outputs.value }} - restore-keys: ${{ runner.os }}-npmCacheDir- - - name: Execute npm - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - env: - PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 - ELECTRON_SKIP_BINARY_DOWNLOAD: 1 - run: npm ci diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml deleted file mode 100644 index 344213e0ca88..000000000000 --- a/.github/workflows/ci.yml +++ /dev/null @@ -1,316 +0,0 @@ -name: CI - -on: workflow_dispatch - -# on: -# push: -# branches: -# - main -# - release/* -# pull_request: -# branches: -# - main -# - release/* - -jobs: - windows: - name: Windows - runs-on: windows-2022 - timeout-minutes: 60 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - steps: - - uses: actions/checkout@v4 - - - uses: actions/setup-node@v4 - with: - node-version-file: .nvmrc - - - uses: actions/setup-python@v5 - with: - python-version: "3.x" - - - name: Compute node modules cache key - id: nodeModulesCacheKey - run: echo "value=$(node build/azure-pipelines/common/computeNodeModulesCacheKey.js)" >> $GITHUB_OUTPUT - - name: Cache node_modules archive - id: cacheNodeModules - uses: actions/cache@v4 - with: - path: ".build/node_modules_cache" - key: "${{ runner.os }}-cacheNodeModulesArchive-${{ steps.nodeModulesCacheKey.outputs.value }}" - - name: Extract node_modules archive - if: ${{ steps.cacheNodeModules.outputs.cache-hit == 'true' }} - run: 7z.exe x .build/node_modules_cache/cache.7z -aos - - name: Get npm cache directory path - id: npmCacheDirPath - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - run: echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT - - name: Cache npm directory - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - uses: actions/cache@v4 - with: - path: ${{ steps.npmCacheDirPath.outputs.dir }} - key: ${{ runner.os }}-npmCacheDir-${{ steps.nodeModulesCacheKey.outputs.value }} - restore-keys: ${{ runner.os }}-npmCacheDir- - - name: Execute npm - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - env: - npm_config_foreground_scripts: "true" - PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 - ELECTRON_SKIP_BINARY_DOWNLOAD: 1 - run: npm ci - - name: Create node_modules archive - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - run: | - mkdir -Force .build - node build/azure-pipelines/common/listNodeModules.js .build/node_modules_list.txt - mkdir -Force .build/node_modules_cache - 7z.exe a .build/node_modules_cache/cache.7z -mx3 `@.build/node_modules_list.txt - - - name: Compile and Download - run: npm exec -- npm-run-all -lp compile "electron x64" playwright-install download-builtin-extensions - - - name: Compile Integration Tests - run: npm run compile - working-directory: test/integration/browser - - - name: Run Unit Tests (Electron) - run: .\scripts\test.bat - - - name: Run Unit Tests (node.js) - run: npm run test-node - - - name: Run Unit Tests (Browser, Chromium) - run: npm run test-browser-no-install -- --browser chromium - - - name: Run Integration Tests (Electron) - run: .\scripts\test-integration.bat - - - name: Run Integration Tests (Browser, Firefox) - timeout-minutes: 20 - run: .\scripts\test-web-integration.bat --browser firefox - - - name: Run Integration Tests (Remote) - timeout-minutes: 20 - run: .\scripts\test-remote-integration.bat - - linux: - name: Linux - runs-on: ubuntu-latest - timeout-minutes: 40 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - steps: - - uses: actions/checkout@v4 - - # TODO: rename azure-pipelines/linux/xvfb.init to github-actions - - name: Setup Build Environment - run: | - sudo apt-get update - sudo apt-get install -y libxkbfile-dev pkg-config 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 - sudo service xvfb start - - - uses: actions/setup-node@v4 - with: - node-version-file: .nvmrc - - - name: Compute node modules cache key - id: nodeModulesCacheKey - run: echo "value=$(node build/azure-pipelines/common/computeNodeModulesCacheKey.js)" >> $GITHUB_OUTPUT - - name: Cache node modules - id: cacheNodeModules - uses: actions/cache@v4 - with: - path: "**/node_modules" - key: ${{ runner.os }}-cacheNodeModulesLinux-${{ steps.nodeModulesCacheKey.outputs.value }} - - name: Get npm cache directory path - id: npmCacheDirPath - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - run: echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT - - name: Cache npm directory - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - uses: actions/cache@v4 - with: - path: ${{ steps.npmCacheDirPath.outputs.dir }} - key: ${{ runner.os }}-npmCacheDir-${{ steps.nodeModulesCacheKey.outputs.value }} - restore-keys: ${{ runner.os }}-npmCacheDir- - - name: Execute npm - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - env: - PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 - ELECTRON_SKIP_BINARY_DOWNLOAD: 1 - run: npm ci - - - name: Compile and Download - run: npm exec -- npm-run-all -lp compile "electron x64" playwright-install download-builtin-extensions - - - name: Compile Integration Tests - run: npm run compile - working-directory: test/integration/browser - - - name: Run Unit Tests (Electron) - id: electron-unit-tests - run: DISPLAY=:10 ./scripts/test.sh - - - name: Run Unit Tests (node.js) - id: nodejs-unit-tests - run: npm run test-node - - - name: Run Unit Tests (Browser, Chromium) - id: browser-unit-tests - run: DISPLAY=:10 npm run test-browser-no-install -- --browser chromium - - - name: Run Integration Tests (Electron) - id: electron-integration-tests - run: DISPLAY=:10 ./scripts/test-integration.sh - - - name: Run Integration Tests (Browser, Chromium) - id: browser-integration-tests - run: DISPLAY=:10 ./scripts/test-web-integration.sh --browser chromium - - - name: Run Integration Tests (Remote) - id: electron-remote-integration-tests - timeout-minutes: 15 - run: DISPLAY=:10 ./scripts/test-remote-integration.sh - - darwin: - name: macOS - runs-on: macos-latest - timeout-minutes: 40 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - steps: - - uses: actions/checkout@v4 - - - uses: actions/setup-node@v4 - with: - node-version-file: .nvmrc - - - name: Compute node modules cache key - id: nodeModulesCacheKey - run: echo "value=$(node build/azure-pipelines/common/computeNodeModulesCacheKey.js)" >> $GITHUB_OUTPUT - - name: Cache node modules - id: cacheNodeModules - uses: actions/cache@v4 - with: - path: "**/node_modules" - key: ${{ runner.os }}-cacheNodeModulesMacOS-${{ steps.nodeModulesCacheKey.outputs.value }} - - name: Get npm cache directory path - id: npmCacheDirPath - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - run: echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT - - name: Cache npm directory - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - uses: actions/cache@v4 - with: - path: ${{ steps.npmCacheDirPath.outputs.dir }} - key: ${{ runner.os }}-npmCacheDir-${{ steps.nodeModulesCacheKey.outputs.value }} - restore-keys: ${{ runner.os }}-npmCacheDir- - - name: Execute npm - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - env: - PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 - ELECTRON_SKIP_BINARY_DOWNLOAD: 1 - run: npm ci - - - name: Compile and Download - run: npm exec -- npm-run-all -lp compile "electron x64" playwright-install download-builtin-extensions - - - name: Compile Integration Tests - run: npm run compile - working-directory: test/integration/browser - - # This is required for SecretStorage unittests - - name: Create temporary keychain - run: | - security create-keychain -p pwd $RUNNER_TEMP/buildagent.keychain - security default-keychain -s $RUNNER_TEMP/buildagent.keychain - security unlock-keychain -p pwd $RUNNER_TEMP/buildagent.keychain - - - name: Run Unit Tests (Electron) - run: DISPLAY=:10 ./scripts/test.sh - - - name: Run Unit Tests (node.js) - run: npm run test-node - - - name: Run Unit Tests (Browser, Chromium) - run: DISPLAY=:10 npm run test-browser-no-install -- --browser chromium - - - name: Run Integration Tests (Electron) - run: DISPLAY=:10 ./scripts/test-integration.sh - - - name: Run Integration Tests (Browser, Webkit) - run: DISPLAY=:10 ./scripts/test-web-integration.sh --browser webkit - - - name: Run Integration Tests (Remote) - timeout-minutes: 15 - run: DISPLAY=:10 ./scripts/test-remote-integration.sh - - hygiene: - name: Hygiene and Layering - runs-on: ubuntu-latest - timeout-minutes: 40 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - steps: - - uses: actions/checkout@v4 - - - uses: actions/setup-node@v4 - with: - node-version-file: .nvmrc - - - name: Compute node modules cache key - id: nodeModulesCacheKey - run: echo "value=$(node build/azure-pipelines/common/computeNodeModulesCacheKey.js)" >> $GITHUB_OUTPUT - - name: Cache node modules - id: cacheNodeModules - uses: actions/cache@v4 - with: - path: "**/node_modules" - key: ${{ runner.os }}-cacheNodeModulesLinux-${{ steps.nodeModulesCacheKey.outputs.value }} - - name: Get npm cache directory path - id: npmCacheDirPath - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - run: echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT - - name: Cache npm directory - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - uses: actions/cache@v4 - with: - path: ${{ steps.npmCacheDirPath.outputs.dir }} - key: ${{ runner.os }}-npmCacheDir-${{ steps.nodeModulesCacheKey.outputs.value }} - restore-keys: ${{ runner.os }}-npmCacheDir- - - name: Execute npm - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - env: - PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 - ELECTRON_SKIP_BINARY_DOWNLOAD: 1 - run: npm ci - - - name: Download Playwright - run: npm run playwright-install - - - name: Run Hygiene Checks - run: npm run gulp hygiene - - - name: Run Valid Layers Checks - run: npm run valid-layers-check - - - name: Compile /build/ - run: npm run compile - working-directory: build - - - name: Check clean git state - run: ./.github/workflows/check-clean-git-state.sh - - - name: Run eslint - run: npm run eslint - - - name: Run vscode-dts Compile Checks - run: npm run vscode-dts-compile-check - - - name: Run Trusted Types Checks - run: npm run tsec-compile-check diff --git a/.github/workflows/dependency-review.yml b/.github/workflows/dependency-review.yml new file mode 100644 index 000000000000..690337a46782 --- /dev/null +++ b/.github/workflows/dependency-review.yml @@ -0,0 +1,27 @@ +# Dependency Review Action +# +# This Action will scan dependency manifest files that change as part of a Pull Request, +# surfacing known-vulnerable versions of the packages declared or updated in the PR. +# Once installed, if the workflow run is marked as required, +# PRs introducing known-vulnerable packages will be blocked from merging. +# +# Source repository: https://github.com/actions/dependency-review-action +name: 'Dependency Review' +on: [pull_request] + +permissions: + contents: read + +jobs: + dependency-review: + runs-on: ubuntu-latest + steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@e3f713f2d8f53843e71c69a996d56f51aa9adfb9 # v2.14.1 + with: + egress-policy: audit + + - name: 'Checkout Repository' + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: 'Dependency Review' + uses: actions/dependency-review-action@3c4e3dcb1aa7874d2c16be7d79418e9b7efd6261 # v4.8.2 diff --git a/.github/workflows/monaco-editor.yml b/.github/workflows/monaco-editor.yml deleted file mode 100644 index 1f5694faec2b..000000000000 --- a/.github/workflows/monaco-editor.yml +++ /dev/null @@ -1,97 +0,0 @@ -name: Monaco Editor checks - -on: - push: - branches: - - main - - release/* - pull_request: - branches: - - main - - release/* - -jobs: - main: - name: Monaco Editor checks - runs-on: ubuntu-latest - timeout-minutes: 40 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - steps: - - uses: actions/checkout@v4 - - - uses: actions/setup-node@v4 - with: - node-version-file: .nvmrc - - - name: Compute node modules cache key - id: nodeModulesCacheKey - run: echo "value=$(node build/azure-pipelines/common/computeNodeModulesCacheKey.js)" >> $GITHUB_OUTPUT - - name: Cache node modules - id: cacheNodeModules - uses: actions/cache@v4 - with: - path: "**/node_modules" - key: ${{ runner.os }}-cacheNodeModules20-${{ steps.nodeModulesCacheKey.outputs.value }} - restore-keys: ${{ runner.os }}-cacheNodeModules20- - - name: Get npm cache directory path - id: npmCacheDirPath - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - run: echo "dir=$(npm config get cache)" >> $GITHUB_OUTPUT - - name: Cache npm directory - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - uses: actions/cache@v4 - with: - path: ${{ steps.npmCacheDirPath.outputs.dir }} - key: ${{ runner.os }}-npmCacheDir-${{ steps.nodeModulesCacheKey.outputs.value }} - restore-keys: ${{ runner.os }}-npmCacheDir- - - name: Install system dependencies - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - run: | - sudo apt update - sudo apt install -y libxkbfile-dev pkg-config libkrb5-dev libxss1 - - name: Execute npm - if: ${{ steps.cacheNodeModules.outputs.cache-hit != 'true' }} - env: - PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 - ELECTRON_SKIP_BINARY_DOWNLOAD: 1 - run: | - npm ci - - - name: Download Playwright - run: npm run playwright-install - - - name: Run Monaco Editor Checks - run: npm run monaco-compile-check - - - name: Editor Distro & ESM - run: npm run gulp editor-esm - - - name: Editor ESM sources check - working-directory: ./test/monaco - run: npm run esm-check - - - name: Typings validation prep - run: | - mkdir typings-test - - - name: Typings validation - working-directory: ./typings-test - run: | - npm init -yp - ../node_modules/.bin/tsc --init - echo "import '../out-monaco-editor-core';" > a.ts - ../node_modules/.bin/tsc --noEmit - - - name: Package Editor with Webpack - working-directory: ./test/monaco - run: npm run bundle-webpack - - - name: Compile Editor Tests - working-directory: ./test/monaco - run: npm run compile - - - name: Run Editor Tests - timeout-minutes: 5 - working-directory: ./test/monaco - run: npm run test diff --git a/.github/workflows/no-package-lock-changes.yml b/.github/workflows/no-package-lock-changes.yml deleted file mode 100644 index 45d5d17407bf..000000000000 --- a/.github/workflows/no-package-lock-changes.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: Prevent package-lock.json changes in PRs -on: [pull_request] - -jobs: - main: - name: Prevent package-lock.json changes in PRs - runs-on: ubuntu-latest - steps: - - uses: octokit/request-action@v2.x - id: get_permissions - with: - route: GET /repos/microsoft/vscode/collaborators/{username}/permission - username: ${{ github.event.pull_request.user.login }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Set control output variable - id: control - run: | - echo "user: ${{ github.event.pull_request.user.login }}" - echo "role: ${{ fromJson(steps.get_permissions.outputs.data).permission }}" - echo "is dependabot: ${{ github.event.pull_request.user.login == 'dependabot[bot]' }}" - echo "should_run: ${{ !contains(fromJson('["admin", "maintain", "write"]'), fromJson(steps.get_permissions.outputs.data).permission) }}" - echo "should_run=${{ !contains(fromJson('["admin", "maintain", "write"]'), fromJson(steps.get_permissions.outputs.data).permission) && github.event.pull_request.user.login != 'dependabot[bot]' }}" >> $GITHUB_OUTPUT - - name: Get file changes - uses: trilom/file-changes-action@ce38c8ce2459ca3c303415eec8cb0409857b4272 - if: ${{ steps.control.outputs.should_run == 'true' }} - - name: Check for lockfile changes - if: ${{ steps.control.outputs.should_run == 'true' }} - run: | - cat $HOME/files.json | jq -e 'any(test("package-lock\\.json$|Cargo\\.lock$")) | not' \ - || (echo "Changes to package-lock.json/Cargo.lock files aren't allowed in PRs." && exit 1) diff --git a/.github/workflows/no-yarn-lock-changes.yml b/.github/workflows/no-yarn-lock-changes.yml deleted file mode 100644 index 57082a28b1cc..000000000000 --- a/.github/workflows/no-yarn-lock-changes.yml +++ /dev/null @@ -1,31 +0,0 @@ -name: Prevent yarn.lock changes in PRs -on: [pull_request] - -jobs: - main: - name: Prevent yarn.lock changes in PRs - runs-on: ubuntu-latest - steps: - - uses: octokit/request-action@v2.x - id: get_permissions - with: - route: GET /repos/microsoft/vscode/collaborators/{username}/permission - username: ${{ github.event.pull_request.user.login }} - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - - name: Set control output variable - id: control - run: | - echo "user: ${{ github.event.pull_request.user.login }}" - echo "role: ${{ fromJson(steps.get_permissions.outputs.data).permission }}" - echo "is dependabot: ${{ github.event.pull_request.user.login == 'dependabot[bot]' }}" - echo "should_run: ${{ !contains(fromJson('["admin", "maintain", "write"]'), fromJson(steps.get_permissions.outputs.data).permission) }}" - echo "should_run=${{ !contains(fromJson('["admin", "maintain", "write"]'), fromJson(steps.get_permissions.outputs.data).permission) && github.event.pull_request.user.login != 'dependabot[bot]' }}" >> $GITHUB_OUTPUT - - name: Get file changes - uses: trilom/file-changes-action@ce38c8ce2459ca3c303415eec8cb0409857b4272 - if: ${{ steps.control.outputs.should_run == 'true' }} - - name: Check for lockfile changes - if: ${{ steps.control.outputs.should_run == 'true' }} - run: | - cat $HOME/files.json | jq -e 'any(test("yarn\\.lock$|Cargo\\.lock$")) | not' \ - || (echo "Changes to yarn.lock/Cargo.lock files aren't allowed in PRs." && exit 1) diff --git a/.github/workflows/scorecards.yml b/.github/workflows/scorecards.yml new file mode 100644 index 000000000000..35133084bd91 --- /dev/null +++ b/.github/workflows/scorecards.yml @@ -0,0 +1,81 @@ +# This workflow uses actions that are not certified by GitHub. They are provided +# by a third-party and are governed by separate terms of service, privacy +# policy, and support documentation. + +name: Scorecard supply-chain security +on: + # For Branch-Protection check. Only the default branch is supported. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#branch-protection + branch_protection_rule: + # To guarantee Maintained check is occasionally updated. See + # https://github.com/ossf/scorecard/blob/main/docs/checks.md#maintained + schedule: + - cron: '20 7 * * 2' + push: + branches: ["Main"] + +# Declare default permissions as read only. +permissions: read-all + +jobs: + analysis: + name: Scorecard analysis + runs-on: ubuntu-latest + permissions: + # Needed to upload the results to code-scanning dashboard. + security-events: write + # Needed to publish results and get a badge (see publish_results below). + id-token: write + contents: read + actions: read + # To allow GraphQL ListCommits to work + issues: read + pull-requests: read + # To detect SAST tools + checks: read + + steps: + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@e3f713f2d8f53843e71c69a996d56f51aa9adfb9 # v2.14.1 + with: + egress-policy: audit + + - name: "Checkout code" + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + persist-credentials: false + + - name: "Run analysis" + uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3 + with: + results_file: results.sarif + results_format: sarif + # (Optional) "write" PAT token. Uncomment the `repo_token` line below if: + # - you want to enable the Branch-Protection check on a *public* repository, or + # - you are installing Scorecards on a *private* repository + # To create the PAT, follow the steps in https://github.com/ossf/scorecard-action#authentication-with-pat. + # repo_token: ${{ secrets.SCORECARD_TOKEN }} + + # Public repositories: + # - Publish results to OpenSSF REST API for easy access by consumers + # - Allows the repository to include the Scorecard badge. + # - See https://github.com/ossf/scorecard-action#publishing-results. + # For private repositories: + # - `publish_results` will always be set to `false`, regardless + # of the value entered here. + publish_results: true + + # Upload the results as artifacts (optional). Commenting out will disable uploads of run results in SARIF + # format to the repository Actions tab. + - name: "Upload artifact" + uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0 + with: + name: SARIF file + path: results.sarif + retention-days: 5 + + # Upload the results to GitHub's code scanning dashboard. + - name: "Upload to code-scanning" + uses: github/codeql-action/upload-sarif@19b2f06db2b6f5108140aeb04014ef02b648f789 # v4.31.11 + with: + sarif_file: results.sarif diff --git a/.github/workflows/telemetry.yml b/.github/workflows/telemetry.yml index d29ea6c58dac..d11bdb4179b8 100644 --- a/.github/workflows/telemetry.yml +++ b/.github/workflows/telemetry.yml @@ -1,19 +1,29 @@ name: 'Telemetry' on: pull_request: +permissions: + contents: read + jobs: check-metdata: name: 'Check metadata' + permissions: + contents: read runs-on: 'ubuntu-latest' steps: - - uses: 'actions/checkout@v4' + - name: Harden the runner (Audit all outbound calls) + uses: step-security/harden-runner@e3f713f2d8f53843e71c69a996d56f51aa9adfb9 # v2.14.1 + with: + egress-policy: audit + + - uses: 'actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd' # v6.0.2 - - uses: 'actions/setup-node@v4' + - uses: 'actions/setup-node@6044e13b5dc448c55e2357c09f80417699197238' # v6.2.0 with: node-version: 'lts/*' - name: 'Run vscode-telemetry-extractor' - run: 'npx --package=@vscode/telemetry-extractor --yes vscode-telemetry-extractor -s .' + run: 'npx --package=@vscode/telemetry-extractor@1.14.0 --yes vscode-telemetry-extractor -s .' env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} diff --git a/.npmrc b/.npmrc index 4a3500427e89..71d98e6c5834 100644 --- a/.npmrc +++ b/.npmrc @@ -1,6 +1,6 @@ disturl="https://electronjs.org/headers" -target="34.1.0" -ms_build_id="10990655" +target="34.3.2" +ms_build_id="11161073" runtime="electron" build_from_source="true" legacy-peer-deps="true" diff --git a/.nvmrc b/.nvmrc index d4b7699d36ca..0254b1e633c7 100644 --- a/.nvmrc +++ b/.nvmrc @@ -1 +1 @@ -20.18.1 +20.18.2 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 000000000000..43c74cd8f368 --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,39 @@ +repos: +- repo: https://github.com/digitalpulp/pre-commit-php + rev: 1.4.0 + hooks: + - id: php-lint-all +- repo: https://github.com/gherynos/pre-commit-java + rev: v0.2.4 + hooks: + - id: Checkstyle +- repo: https://github.com/gitleaks/gitleaks + rev: v8.16.3 + hooks: + - id: gitleaks +- repo: https://github.com/golangci/golangci-lint + rev: v1.52.2 + hooks: + - id: golangci-lint +- repo: https://github.com/jumanjihouse/pre-commit-hooks + rev: 3.0.0 + hooks: + - id: RuboCop + - id: shellcheck +- repo: https://github.com/pocc/pre-commit-hooks + rev: v1.3.5 + hooks: + - id: cpplint +- repo: https://github.com/pre-commit/mirrors-eslint + rev: v8.38.0 + hooks: + - id: eslint +- repo: https://github.com/pre-commit/pre-commit-hooks + rev: v4.4.0 + hooks: + - id: end-of-file-fixer + - id: trailing-whitespace +- repo: https://github.com/pylint-dev/pylint + rev: v2.17.2 + hooks: + - id: pylint diff --git a/.vscode-test.js b/.vscode-test.js index 917413ede2ac..823ef615f4f7 100644 --- a/.vscode-test.js +++ b/.vscode-test.js @@ -19,7 +19,7 @@ const { defineConfig } = require('@vscode/test-cli'); * A list of extension folders who have opted into tests, or configuration objects. * Edit me to add more! * - * @type {Array & { label: string })>} + * @type {Array & { label: string }>} */ const extensions = [ { @@ -65,6 +65,20 @@ const extensions = [ { label: 'microsoft-authentication', mocha: { timeout: 60_000 } + }, + { + label: 'vscode-api-tests-folder', + extensionDevelopmentPath: `extensions/vscode-api-tests`, + workspaceFolder: `extensions/vscode-api-tests/testWorkspace`, + mocha: { timeout: 60_000 }, + files: 'extensions/vscode-api-tests/out/singlefolder-tests/**/*.test.js', + }, + { + label: 'vscode-api-tests-workspace', + extensionDevelopmentPath: `extensions/vscode-api-tests`, + workspaceFolder: `extensions/vscode-api-tests/testworkspace.code-workspace`, + mocha: { timeout: 60_000 }, + files: 'extensions/vscode-api-tests/out/workspace-tests/**/*.test.js', } ]; @@ -75,9 +89,12 @@ const defaultLaunchArgs = process.env.API_TESTS_EXTRA_ARGS?.split(' ') || [ const config = defineConfig(extensions.map(extension => { /** @type {import('@vscode/test-cli').TestConfiguration} */ - const config = typeof extension === 'object' - ? { files: `extensions/${extension.label}/out/**/*.test.js`, ...extension } - : { files: `extensions/${extension}/out/**/*.test.js`, label: extension }; + const config = { + platform: 'desktop', + files: `extensions/${extension.label}/out/**/*.test.js`, + extensionDevelopmentPath: `extensions/${extension.label}`, + ...extension, + }; config.mocha ??= {}; if (process.env.BUILD_ARTIFACTSTAGINGDIRECTORY) { diff --git a/.vscode/extensions/vscode-selfhost-import-aid/package-lock.json b/.vscode/extensions/vscode-selfhost-import-aid/package-lock.json index 5f526aca3e9e..899595c323f7 100644 --- a/.vscode/extensions/vscode-selfhost-import-aid/package-lock.json +++ b/.vscode/extensions/vscode-selfhost-import-aid/package-lock.json @@ -9,16 +9,17 @@ "version": "0.0.1", "license": "MIT", "dependencies": { - "typescript": "5.5.4" + "typescript": "5.9.3" }, "engines": { "vscode": "^1.88.0" } }, "node_modules/typescript": { - "version": "5.5.4", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.5.4.tgz", - "integrity": "sha512-Mtq29sKDAEYP7aljRgtPOpTvOfbwRWlS6dPRzwjdE+C0R4brX/GUyhHSecbHMFLNBLcJIPt9nl9yG5TZ1weH+Q==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/.vscode/extensions/vscode-selfhost-import-aid/package.json b/.vscode/extensions/vscode-selfhost-import-aid/package.json index 30f09f9c8e5b..396eba235d1f 100644 --- a/.vscode/extensions/vscode-selfhost-import-aid/package.json +++ b/.vscode/extensions/vscode-selfhost-import-aid/package.json @@ -24,6 +24,6 @@ "watch": "gulp watch-extension:vscode-selfhost-import-aid" }, "dependencies": { - "typescript": "5.5.4" + "typescript": "5.9.3" } } diff --git a/.vscode/extensions/vscode-selfhost-test-provider/package-lock.json b/.vscode/extensions/vscode-selfhost-test-provider/package-lock.json index a71a68e4e365..08980be9b8d5 100644 --- a/.vscode/extensions/vscode-selfhost-test-provider/package-lock.json +++ b/.vscode/extensions/vscode-selfhost-test-provider/package-lock.json @@ -9,14 +9,14 @@ "version": "0.4.0", "license": "MIT", "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "ansi-styles": "^5.2.0", - "cockatiel": "^3.1.3", - "istanbul-to-vscode": "^2.0.1" + "@jridgewell/trace-mapping": "^0.3.31", + "ansi-styles": "^6.2.3", + "cockatiel": "^3.2.1", + "istanbul-to-vscode": "^2.1.1" }, "devDependencies": { - "@types/mocha": "^10.0.6", - "@types/node": "20.x" + "@types/mocha": "^10.0.10", + "@types/node": "25.x" }, "engines": { "vscode": "^1.88.0" @@ -36,9 +36,10 @@ "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" @@ -50,52 +51,58 @@ "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==" }, "node_modules/@types/mocha": { - "version": "10.0.6", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.6.tgz", - "integrity": "sha512-dJvrYWxP/UcXm36Qn36fxhUKu8A/xMRXVT2cliFF1Z7UA9liG5Psj3ezNSZw+5puH2czDXRLcXQxf8JbJt0ejg==", - "dev": true + "version": "10.0.10", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz", + "integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==", + "dev": true, + "license": "MIT" }, "node_modules/@types/node": { - "version": "20.12.11", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.11.tgz", - "integrity": "sha512-vDg9PZ/zi+Nqp6boSOT7plNuthRugEKixDv5sFTIpkE89MmNtEArAShI4mxuX2+UrLEe9pxC1vm2cjm9YlWbJw==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/ansi-styles": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", - "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=12" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/cockatiel": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/cockatiel/-/cockatiel-3.1.3.tgz", - "integrity": "sha512-xC759TpZ69d7HhfDp8m2WkRwEUiCkxY8Ee2OQH/3H6zmy2D/5Sm+zSTbPRa+V2QyjDtpMvjOIAOVjA2gp6N1kQ==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/cockatiel/-/cockatiel-3.2.1.tgz", + "integrity": "sha512-gfrHV6ZPkquExvMh9IOkKsBzNDk6sDuZ6DdBGUBkvFnTCqCxzpuq48RySgP0AnaqQkw2zynOFj9yly6T1Q2G5Q==", + "license": "MIT", "engines": { "node": ">=16" } }, "node_modules/istanbul-to-vscode": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/istanbul-to-vscode/-/istanbul-to-vscode-2.0.1.tgz", - "integrity": "sha512-V9Hhr7kX3UvkvkaT1lK3AmCRPkaIAIogQBrduTpNiLTkp1eVsybnJhWiDSVeCQap/3aGeZ2019oIivhX9MNsCQ==", + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/istanbul-to-vscode/-/istanbul-to-vscode-2.1.1.tgz", + "integrity": "sha512-F4IvCWYzWJkFfbYFjLUgxndE6g4EVWQH+rObEQ8OexBGTLhQ7e6VQ8p9f4BVrQmbNmo0nRGIGF34XqARXXgMjA==", + "license": "MIT", "dependencies": { "@types/istanbul-lib-coverage": "^2.0.6" } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" } } } diff --git a/.vscode/extensions/vscode-selfhost-test-provider/package.json b/.vscode/extensions/vscode-selfhost-test-provider/package.json index 71b5ee73c60d..2a579793a118 100644 --- a/.vscode/extensions/vscode-selfhost-test-provider/package.json +++ b/.vscode/extensions/vscode-selfhost-test-provider/package.json @@ -77,13 +77,13 @@ "test": "npx mocha --ui tdd 'out/*.test.js'" }, "devDependencies": { - "@types/mocha": "^10.0.6", - "@types/node": "20.x" + "@types/mocha": "^10.0.10", + "@types/node": "25.x" }, "dependencies": { - "@jridgewell/trace-mapping": "^0.3.25", - "ansi-styles": "^5.2.0", - "cockatiel": "^3.1.3", - "istanbul-to-vscode": "^2.0.1" + "@jridgewell/trace-mapping": "^0.3.31", + "ansi-styles": "^6.2.3", + "cockatiel": "^3.2.1", + "istanbul-to-vscode": "^2.1.1" } } diff --git a/.vscode/launch.json b/.vscode/launch.json index afd57934304f..f922b5d9c803 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -251,7 +251,51 @@ "timeout": 0, "env": { "VSCODE_EXTHOST_WILL_SEND_SOCKET": null, - "VSCODE_SKIP_PRELAUNCH": "1" + "VSCODE_SKIP_PRELAUNCH": "1", + }, + "cleanUp": "wholeBrowser", + "runtimeArgs": [ + "--inspect-brk=5875", + "--no-cached-data", + "--crash-reporter-directory=${workspaceFolder}/.profile-oss/crashes", + // for general runtime freezes: https://github.com/microsoft/vscode/issues/127861#issuecomment-904144910 + "--disable-features=CalculateNativeWinOcclusion", + "--disable-extension=vscode.vscode-api-tests" + ], + "userDataDir": "${userHome}/.vscode-oss-dev", + "webRoot": "${workspaceFolder}", + "cascadeTerminateToConfigurations": [ + "Attach to Extension Host" + ], + "pauseForSourceMap": false, + "outFiles": [ + "${workspaceFolder}/out/**/*.js" + ], + "browserLaunchLocation": "workspace", + "presentation": { + "hidden": true, + }, + }, + { + // To debug observables you also need the extension "ms-vscode.debug-value-editor" + "type": "chrome", + "request": "launch", + "name": "Launch VS Code Internal (Dev Debug)", + "windows": { + "runtimeExecutable": "${workspaceFolder}/scripts/code.bat" + }, + "osx": { + "runtimeExecutable": "${workspaceFolder}/scripts/code.sh" + }, + "linux": { + "runtimeExecutable": "${workspaceFolder}/scripts/code.sh" + }, + "port": 9222, + "timeout": 0, + "env": { + "VSCODE_EXTHOST_WILL_SEND_SOCKET": null, + "VSCODE_SKIP_PRELAUNCH": "1", + "VSCODE_DEV_DEBUG": "1", }, "cleanUp": "wholeBrowser", "runtimeArgs": [ @@ -568,6 +612,21 @@ "order": 1 } }, + { + "name": "VS Code (Debug Observables)", + "stopAll": true, + "configurations": [ + "Launch VS Code Internal (Dev Debug)", + "Attach to Main Process", + "Attach to Extension Host", + "Attach to Shared Process", + ], + "preLaunchTask": "Ensure Prelaunch Dependencies", + "presentation": { + "group": "0_vscode", + "order": 1 + } + }, { "name": "Search, Renderer, and Main processes", "configurations": [ diff --git a/.vscode/notebooks/api.github-issues b/.vscode/notebooks/api.github-issues index 09d6480e1024..3a0eaec922fa 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:\"February 2025\"" + "value": "$REPO=repo:microsoft/vscode\n$MILESTONE=milestone:\"March 2025\"" }, { "kind": 1, diff --git a/.vscode/notebooks/endgame.github-issues b/.vscode/notebooks/endgame.github-issues index d1c128a6d0a7..ca93d5033384 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/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\n\n$MILESTONE=milestone:\"January 2025\"" + "value": "$REPOS=repo:microsoft/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\r\n\r\n$MILESTONE=milestone:\"February 2025\"" }, { "kind": 1, @@ -97,7 +97,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE is:issue is:closed reason:completed label:verification-needed -label:verified" + "value": "$REPOS $MILESTONE is:issue is:closed reason:completed label:verification-needed -label:verified -label:on-testplan" }, { "kind": 1, diff --git a/.vscode/notebooks/my-endgame.github-issues b/.vscode/notebooks/my-endgame.github-issues index b16f0025f564..0fd05ece4853 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/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\n\n$MILESTONE=milestone:\"January 2025\"\n\n$MINE=assignee:@me" + "value": "$REPOS=repo:microsoft/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\n\n$MILESTONE=milestone:\"February 2025\"\n\n$MINE=assignee:@me" }, { "kind": 1, @@ -62,7 +62,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE $MINE is:issue is:closed reason:completed label:feature-request label:verification-needed -label:verified" + "value": "$REPOS $MILESTONE $MINE is:issue is:closed reason:completed label:feature-request label:verification-needed -label:verified -label:on-testplan" }, { "kind": 1, @@ -87,7 +87,7 @@ { "kind": 2, "language": "github-issues", - "value": "$REPOS $MILESTONE -$MINE is:issue is:closed reason:completed -assignee:@me -label:verified -label:z-author-verified label:feature-request label:verification-needed -label:verification-steps-needed -label:unreleased" + "value": "$REPOS $MILESTONE -$MINE is:issue is:closed reason:completed -assignee:@me -label:verified -label:z-author-verified label:feature-request label:verification-needed -label:verification-steps-needed -label:unreleased -label:on-testplan" }, { "kind": 1, diff --git a/.vscode/notebooks/my-work.github-issues b/.vscode/notebooks/my-work.github-issues index e8b184f8e573..c7674cef414a 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/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\n\n// current milestone name\n$MILESTONE=milestone:\"January 2025\"\n" + "value": "// list of repos we work in\n$REPOS=repo:microsoft/lsprotocol repo:microsoft/monaco-editor repo:microsoft/vscode repo:microsoft/vscode-anycode repo:microsoft/vscode-autopep8 repo:microsoft/vscode-black-formatter repo:microsoft/vscode-copilot repo:microsoft/vscode-copilot-release repo:microsoft/vscode-dev repo:microsoft/vscode-dev-chrome-launcher repo:microsoft/vscode-emmet-helper repo:microsoft/vscode-extension-telemetry repo:microsoft/vscode-flake8 repo:microsoft/vscode-github-issue-notebooks repo:microsoft/vscode-hexeditor repo:microsoft/vscode-internalbacklog repo:microsoft/vscode-isort repo:microsoft/vscode-js-debug repo:microsoft/vscode-jupyter repo:microsoft/vscode-jupyter-internal repo:microsoft/vscode-l10n repo:microsoft/vscode-livepreview repo:microsoft/vscode-markdown-languageservice repo:microsoft/vscode-markdown-tm-grammar repo:microsoft/vscode-mypy repo:microsoft/vscode-pull-request-github repo:microsoft/vscode-pylint repo:microsoft/vscode-python repo:microsoft/vscode-python-debugger repo:microsoft/vscode-python-tools-extension-template repo:microsoft/vscode-references-view repo:microsoft/vscode-remote-release repo:microsoft/vscode-remote-repositories-github repo:microsoft/vscode-remote-tunnels repo:microsoft/vscode-remotehub repo:microsoft/vscode-settings-sync-server repo:microsoft/vscode-unpkg repo:microsoft/vscode-vsce\n\n// current milestone name\n$MILESTONE=milestone:\"February 2025\"\n" }, { "kind": 1, diff --git a/.vscode/settings.json b/.vscode/settings.json index 59a78245c83b..87e51525722b 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -49,6 +49,7 @@ "out-vscode-reh/**": true, "extensions/**/dist/**": true, "extensions/**/out/**": true, + "extensions/terminal-suggest/src/completions/upstream/**": true, "test/smoke/out/**": true, "test/automation/out/**": true, "test/integration/browser/out/**": true @@ -76,6 +77,7 @@ "npm.exclude": "**/extensions/**", "emmet.excludeLanguages": [], "typescript.preferences.importModuleSpecifier": "relative", + "typescript.preferences.importModuleSpecifierEnding": "js", "typescript.preferences.quoteStyle": "single", "json.schemas": [ { @@ -156,6 +158,7 @@ "application.experimental.rendererProfiling": true, "editor.experimental.asyncTokenization": true, "editor.experimental.asyncTokenizationVerification": true, + "terminal.integrated.suggest.enabled": true, "typescript.preferences.autoImportFileExcludePatterns": [ "@xterm/xterm", "@xterm/headless", diff --git a/ThirdPartyNotices.txt b/ThirdPartyNotices.txt index a0026469915f..0f17c5ed11bd 100644 --- a/ThirdPartyNotices.txt +++ b/ThirdPartyNotices.txt @@ -4,6 +4,34 @@ This repository incorporates material as listed below or described in the code. +--------------------------------------------------------- + +@fig/autocomplete-shared 1.1.2 +https://github.com/withfig/autocomplete-tools + +MIT License + +Copyright (c) 2021 Hercules Labs Inc. (Fig) + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--------------------------------------------------------- + --------------------------------------------------------- @iktakahiro/markdown-it-katex 4.0.2 - MIT @@ -58,6 +86,34 @@ SOFTWARE. --------------------------------------------------------- +amazon-q-developer-cli f66e0b0e917ab185eef528dc36eca56b78ca8b5d +https://github.com/aws/amazon-q-developer-cli + +MIT License + +Copyright (c) 2024 Amazon.com, Inc. or its affiliates. + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +--------------------------------------------------------- + +--------------------------------------------------------- + atom/language-clojure 0.22.8 - MIT https://github.com/atom/language-clojure @@ -468,6 +524,580 @@ Title to copyright in this work will at all times remain with copyright holders. --------------------------------------------------------- +dompurify 3.1.7 - Apache 2.0 +https://github.com/cure53/DOMPurify + +DOMPurify +Copyright 2025 Dr.-Ing. Mario Heiderich, Cure53 + +DOMPurify is free software; you can redistribute it and/or modify it under the +terms of either: + +a) the Apache License Version 2.0, or +b) the Mozilla Public License Version 2.0 + +----------------------------------------------------------------------------- + + 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. + +----------------------------------------------------------------------------- +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 + http://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. +--------------------------------------------------------- + +--------------------------------------------------------- + dotnet/csharp-tmLanguage 0.1.0 - MIT https://github.com/dotnet/csharp-tmLanguage @@ -889,7 +1519,7 @@ SOFTWARE. --------------------------------------------------------- -jlelong/vscode-latex-basics 1.9.0 - MIT +jlelong/vscode-latex-basics 1.10.0 - MIT https://github.com/jlelong/vscode-latex-basics Copyright (c) vscode-latex-basics authors @@ -2539,7 +3169,7 @@ Creative Commons may be contacted at creativecommons.org. --------------------------------------------------------- -vscode-logfile-highlighter 2.17.0 - MIT +vscode-logfile-highlighter 3.3.4 - MIT https://github.com/emilast/vscode-logfile-highlighter The MIT License (MIT) diff --git a/build/.npmrc b/build/.npmrc index 1b073e71a832..551822f79cd6 100644 --- a/build/.npmrc +++ b/build/.npmrc @@ -2,4 +2,5 @@ disturl="https://nodejs.org/dist" runtime="node" build_from_source="true" legacy-peer-deps="true" +force_process_config="true" timeout=180000 diff --git a/build/azure-pipelines/cli/cli-compile.yml b/build/azure-pipelines/cli/cli-compile.yml index 71cd3f71e78b..a5d8bdc1a2c3 100644 --- a/build/azure-pipelines/cli/cli-compile.yml +++ b/build/azure-pipelines/cli/cli-compile.yml @@ -42,7 +42,6 @@ steps: - script: | set -e if [ -n "$SYSROOT_ARCH" ]; then - export VSCODE_SYSROOT_PREFIX='-glibc-2.17' export VSCODE_SYSROOT_DIR=$(Build.SourcesDirectory)/.build/sysroots node -e '(async () => { const { getVSCodeSysroot } = require("../build/linux/debian/install-sysroot.js"); await getVSCodeSysroot(process.env["SYSROOT_ARCH"]); })()' if [ "$SYSROOT_ARCH" == "arm64" ]; then @@ -73,7 +72,7 @@ steps: # verify glibc requirement if [ -n "$SYSROOT_ARCH" ]; then - glibc_version="2.17" + glibc_version="2.28" while IFS= read -r line; do if [[ $line == *"GLIBC_"* ]]; then version=$(echo "$line" | awk '{print $5}' | tr -d '()') @@ -83,8 +82,8 @@ steps: fi fi done < <("$OBJDUMP" -T "$PWD/target/${{ parameters.VSCODE_CLI_TARGET }}/release/code") - if [[ "$glibc_version" != "2.17" ]]; then - echo "Error: binary has dependency on GLIBC > 2.17, found $glibc_version" + if [[ "$glibc_version" != "2.28" ]]; then + echo "Error: binary has dependency on GLIBC > 2.28, found $glibc_version" exit 1 fi fi @@ -120,22 +119,6 @@ steps: ArtifactServices.Symbol.UseAAD: false displayName: Publish Symbols - - task: CopyFiles@2 - inputs: - SourceFolder: $(Build.SourcesDirectory)/cli/target/${{ parameters.VSCODE_CLI_TARGET }}/release - Contents: 'code.*' - TargetFolder: $(Agent.TempDirectory)/binskim-cli - displayName: Copy files for BinSkim - - - task: BinSkim@4 - inputs: - InputType: Basic - Function: analyze - TargetPattern: guardianGlob - AnalyzeTargetGlob: $(Agent.TempDirectory)/binskim-cli/*.* - AnalyzeSymPath: $(Agent.TempDirectory)/binskim-cli - displayName: Run BinSkim - - powershell: | . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" diff --git a/build/azure-pipelines/cli/install-rust-posix.yml b/build/azure-pipelines/cli/install-rust-posix.yml index fee56e028f72..0607cde33e56 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.81 + default: 1.85 - 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 45a1cfd188e1..bff114fccd07 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.81 + default: 1.85 - name: targets default: [] type: object diff --git a/build/azure-pipelines/common/publish.js b/build/azure-pipelines/common/publish.js index 444e005d3c63..8e052881b2ca 100644 --- a/build/azure-pipelines/common/publish.js +++ b/build/azure-pipelines/common/publish.js @@ -382,7 +382,7 @@ async function unzip(packagePath, outputPath) { }); } // Contains all of the logic for mapping details to our actual product names in CosmosDB -function getPlatform(product, os, arch, type, isLegacy) { +function getPlatform(product, os, arch, type) { switch (os) { case 'win32': switch (product) { @@ -427,12 +427,12 @@ function getPlatform(product, os, arch, type, isLegacy) { case 'client': return `linux-${arch}`; case 'server': - return isLegacy ? `server-linux-legacy-${arch}` : `server-linux-${arch}`; + return `server-linux-${arch}`; case 'web': if (arch === 'standalone') { return 'web-standalone'; } - return isLegacy ? `server-linux-legacy-${arch}-web` : `server-linux-${arch}-web`; + return `server-linux-${arch}-web`; default: throw new Error(`Unrecognized: ${product} ${os} ${arch} ${type}`); } @@ -556,8 +556,7 @@ async function processArtifact(artifact, filePath) { await releaseService.createRelease(version, filePath, friendlyFileName); } const { product, os, arch, unprocessedType } = match.groups; - const isLegacy = artifact.name.includes('_legacy'); - const platform = getPlatform(product, os, arch, unprocessedType, isLegacy); + const platform = getPlatform(product, os, arch, unprocessedType); const type = getRealType(unprocessedType); const size = fs_1.default.statSync(filePath).size; const stream = fs_1.default.createReadStream(filePath); @@ -610,9 +609,6 @@ async function main() { if (e('VSCODE_BUILD_STAGE_LINUX') === 'True') { stages.add('Linux'); } - if (e('VSCODE_BUILD_STAGE_LINUX_LEGACY_SERVER') === 'True') { - stages.add('LinuxLegacyServer'); - } if (e('VSCODE_BUILD_STAGE_ALPINE') === 'True') { stages.add('Alpine'); } diff --git a/build/azure-pipelines/common/publish.ts b/build/azure-pipelines/common/publish.ts index f061d043d137..13557c60181c 100644 --- a/build/azure-pipelines/common/publish.ts +++ b/build/azure-pipelines/common/publish.ts @@ -12,13 +12,10 @@ import yauzl from 'yauzl'; import crypto from 'crypto'; import { retry } from './retry'; import { CosmosClient } from '@azure/cosmos'; -import cp from 'child_process'; -import os from 'os'; -import { Worker, isMainThread, workerData } from 'node:worker_threads'; -import { ConfidentialClientApplication } from '@azure/msal-node'; -import { BlobClient, BlobServiceClient, BlockBlobClient, ContainerClient, ContainerSASPermissions, generateBlobSASQueryParameters } from '@azure/storage-blob'; -import jws from 'jws'; -import { clearInterval, setInterval } from 'node:timers'; +import { ClientSecretCredential, ClientAssertionCredential } from '@azure/identity'; +import * as cp from 'child_process'; +import * as os from 'os'; +import { Worker, isMainThread, parentPort, workerData } from 'node:worker_threads'; function e(name: string): string { const result = process.env[name]; @@ -30,9 +27,11 @@ function e(name: string): string { return result; } -function hashStream(hashName: string, stream: Readable): Promise { - return new Promise((c, e) => { - const shasum = crypto.createHash(hashName); +const quality = e('VSCODE_QUALITY'); +const commit = e('BUILD_SOURCEVERSION'); + +class Temp { + private _files: string[] = []; stream .on('data', shasum.update.bind(shasum)) @@ -694,7 +693,7 @@ interface Asset { } // Contains all of the logic for mapping details to our actual product names in CosmosDB -function getPlatform(product: string, os: string, arch: string, type: string, isLegacy: boolean): string { +function getPlatform(product: string, os: string, arch: string, type: string): string { switch (os) { case 'win32': switch (product) { @@ -739,12 +738,12 @@ function getPlatform(product: string, os: string, arch: string, type: string, is case 'client': return `linux-${arch}`; case 'server': - return isLegacy ? `server-linux-legacy-${arch}` : `server-linux-${arch}`; + return `server-linux-${arch}`; case 'web': if (arch === 'standalone') { return 'web-standalone'; } - return isLegacy ? `server-linux-legacy-${arch}-web` : `server-linux-${arch}-web`; + return `server-linux-${arch}-web`; default: throw new Error(`Unrecognized: ${product} ${os} ${arch} ${type}`); } @@ -797,52 +796,7 @@ function getRealType(type: string) { } } -async function withLease(client: BlockBlobClient, fn: () => Promise) { - const lease = client.getBlobLeaseClient(); - - for (let i = 0; i < 360; i++) { // Try to get lease for 30 minutes - try { - await client.uploadData(new ArrayBuffer()); // blob needs to exist for lease to be acquired - await lease.acquireLease(60); - - try { - const abortController = new AbortController(); - const refresher = new Promise((c, e) => { - abortController.signal.onabort = () => { - clearInterval(interval); - c(); - }; - - const interval = setInterval(() => { - lease.renewLease().catch(err => { - clearInterval(interval); - e(new Error('Failed to renew lease ' + err)); - }); - }, 30_000); - }); - - const result = await Promise.race([fn(), refresher]); - abortController.abort(); - return result; - } finally { - await lease.releaseLease(); - } - } catch (err) { - if (err.statusCode !== 409 && err.statusCode !== 412) { - throw err; - } - - await new Promise(c => setTimeout(c, 5000)); - } - } - - throw new Error('Failed to acquire lease on blob after 30 minutes'); -} - -async function processArtifact( - artifact: Artifact, - filePath: string -) { +async function processArtifact(artifact: Artifact, artifactFilePath: string): Promise { const log = (...args: any[]) => console.log(`[${artifact.name}]`, ...args); const match = /^vscode_(?[^_]+)_(?[^_]+)(?:_legacy)?_(?[^_]+)_(?[^_]+)$/.exec(artifact.name); @@ -850,77 +804,30 @@ async function processArtifact( throw new Error(`Invalid artifact name: ${artifact.name}`); } - const { cosmosDBAccessToken, blobServiceAccessToken } = JSON.parse(e('PUBLISH_AUTH_TOKENS')); - const quality = e('VSCODE_QUALITY'); - const version = e('BUILD_SOURCEVERSION'); - const friendlyFileName = `${quality}/${version}/${path.basename(filePath)}`; - - const blobServiceClient = new BlobServiceClient(`https://${e('VSCODE_STAGING_BLOB_STORAGE_ACCOUNT_NAME')}.blob.core.windows.net/`, { getToken: async () => blobServiceAccessToken }); - const leasesContainerClient = blobServiceClient.getContainerClient('leases'); - await leasesContainerClient.createIfNotExists(); - const leaseBlobClient = leasesContainerClient.getBlockBlobClient(friendlyFileName); - - log(`Acquiring lease for: ${friendlyFileName}`); - - await withLease(leaseBlobClient, async () => { - log(`Successfully acquired lease for: ${friendlyFileName}`); - - const url = `${e('PRSS_CDN_URL')}/${friendlyFileName}`; - const res = await retry(() => fetch(url)); - - if (res.status === 200) { - log(`Already released and provisioned: ${url}`); - } else { - const stagingContainerClient = blobServiceClient.getContainerClient('staging'); - await stagingContainerClient.createIfNotExists(); - - const now = new Date().valueOf(); - const oneHour = 60 * 60 * 1000; - const oneHourAgo = new Date(now - oneHour); - const oneHourFromNow = new Date(now + oneHour); - const userDelegationKey = await blobServiceClient.getUserDelegationKey(oneHourAgo, oneHourFromNow); - const sasOptions = { containerName: 'staging', permissions: ContainerSASPermissions.from({ read: true }), startsOn: oneHourAgo, expiresOn: oneHourFromNow }; - const stagingSasToken = generateBlobSASQueryParameters(sasOptions, userDelegationKey, e('VSCODE_STAGING_BLOB_STORAGE_ACCOUNT_NAME')).toString(); - - const releaseService = await ESRPReleaseService.create( - log, - e('RELEASE_TENANT_ID'), - e('RELEASE_CLIENT_ID'), - e('RELEASE_AUTH_CERT'), - e('RELEASE_REQUEST_SIGNING_CERT'), - stagingContainerClient, - stagingSasToken - ); - - await releaseService.createRelease(version, filePath, friendlyFileName); - } - - const { product, os, arch, unprocessedType } = match.groups!; - const isLegacy = artifact.name.includes('_legacy'); - const platform = getPlatform(product, os, arch, unprocessedType, isLegacy); - const type = getRealType(unprocessedType); - const size = fs.statSync(filePath).size; - const stream = fs.createReadStream(filePath); - const [hash, sha256hash] = await Promise.all([hashStream('sha1', stream), hashStream('sha256', stream)]); // CodeQL [SM04514] Using SHA1 only for legacy reasons, we are actually only respecting SHA256 - const asset: Asset = { platform, type, url, hash: hash.toString('hex'), sha256hash: sha256hash.toString('hex'), size, supportsFastUpdate: true }; - log('Creating asset...'); - - const result = await retry(async (attempt) => { - log(`Creating asset in Cosmos DB (attempt ${attempt})...`); - const client = new CosmosClient({ endpoint: e('AZURE_DOCUMENTDB_ENDPOINT')!, tokenProvider: () => Promise.resolve(`type=aad&ver=1.0&sig=${cosmosDBAccessToken.token}`) }); - const scripts = client.database('builds').container(quality).scripts; - const { resource: result } = await scripts.storedProcedure('createAsset').execute<'ok' | 'already exists'>('', [version, asset, true]); - return result; - }); - - if (result === 'already exists') { - log('Asset already exists!'); - } else { - log('Asset successfully created: ', JSON.stringify(asset, undefined, 2)); - } - }); - - log(`Successfully released lease for: ${friendlyFileName}`); + // getPlatform needs the unprocessedType + const { product, os, arch, unprocessedType } = match.groups!; + const isLegacy = artifact.name.includes('_legacy'); + const platform = getPlatform(product, os, arch, unprocessedType, isLegacy); + const type = getRealType(unprocessedType); + const size = fs.statSync(artifactFilePath).size; + const stream = fs.createReadStream(artifactFilePath); + const [hash, sha256hash] = await Promise.all([hashStream('sha1', stream), hashStream('sha256', stream)]); // CodeQL [SM04514] Using SHA1 only for legacy reasons, we are actually only respecting SHA256 + + const url = await releaseAndProvision( + log, + e('RELEASE_TENANT_ID'), + e('RELEASE_CLIENT_ID'), + e('RELEASE_AUTH_CERT_SUBJECT_NAME'), + e('RELEASE_REQUEST_SIGNING_CERT_SUBJECT_NAME'), + e('PROVISION_TENANT_ID'), + e('PROVISION_AAD_USERNAME'), + e('PROVISION_AAD_PASSWORD'), + commit, + quality, + artifactFilePath + ); + + return { platform, type, url, hash, sha256hash, size, supportsFastUpdate: true }; } // It is VERY important that we don't download artifacts too much too fast from AZDO. @@ -932,7 +839,8 @@ async function processArtifact( async function main() { if (!isMainThread) { const { artifact, artifactFilePath } = workerData; - await processArtifact(artifact, artifactFilePath); + const asset = await processArtifact(artifact, artifactFilePath); + parentPort!.postMessage(asset); return; } @@ -956,13 +864,14 @@ async function main() { if (e('VSCODE_BUILD_STAGE_WINDOWS') === 'True') { stages.add('Windows'); } if (e('VSCODE_BUILD_STAGE_LINUX') === 'True') { stages.add('Linux'); } - if (e('VSCODE_BUILD_STAGE_LINUX_LEGACY_SERVER') === 'True') { stages.add('LinuxLegacyServer'); } if (e('VSCODE_BUILD_STAGE_ALPINE') === 'True') { stages.add('Alpine'); } if (e('VSCODE_BUILD_STAGE_MACOS') === 'True') { stages.add('macOS'); } if (e('VSCODE_BUILD_STAGE_WEB') === 'True') { stages.add('Web'); } let resultPromise = Promise.resolve[]>([]); const operations: { name: string; operation: Promise }[] = []; + const aadCredentials = new ClientAssertionCredential(process.env['AZURE_TENANT_ID']!, process.env['AZURE_CLIENT_ID']!, () => Promise.resolve(process.env['AZURE_ID_TOKEN']!)); + const client = new CosmosClient({ endpoint: e('AZURE_DOCUMENTDB_ENDPOINT'), aadCredentials }); while (true) { const [timeline, artifacts] = await Promise.all([retry(() => getPipelineTimeline()), retry(() => getPipelineArtifacts())]); @@ -1004,12 +913,27 @@ async function main() { processing.add(artifact.name); const promise = new Promise((resolve, reject) => { + const log = (...args: any[]) => console.log(`[${artifact.name}]`, ...args); const worker = new Worker(__filename, { workerData: { artifact, artifactFilePath } }); worker.on('error', reject); - worker.on('exit', code => { - if (code === 0) { + worker.once('message', async (asset: Asset) => { + try { + log('Creating asset...', JSON.stringify(asset, undefined, 2)); + + await retry(async (attempt) => { + log(`Creating asset in Cosmos DB (attempt ${attempt})...`); + const scripts = client.database('builds').container(quality).scripts; + await scripts.storedProcedure('createAsset').execute('', [commit, asset, true]); + }); + + log('Asset successfully created'); resolve(); - } else { + } catch (err) { + reject(err); + } + }); + worker.on('exit', code => { + if (code !== 0) { reject(new Error(`[${artifact.name}] Worker stopped with exit code ${code}`)); } }); diff --git a/build/azure-pipelines/darwin/product-build-darwin.yml b/build/azure-pipelines/darwin/product-build-darwin.yml index e77000d431b6..bd37d675aa2f 100644 --- a/build/azure-pipelines/darwin/product-build-darwin.yml +++ b/build/azure-pipelines/darwin/product-build-darwin.yml @@ -121,6 +121,11 @@ steps: - template: ../common/install-builtin-extensions.yml@self + - ${{ if and(ne(parameters.VSCODE_CIBUILD, true), ne(parameters.VSCODE_QUALITY, 'oss')) }}: + - script: node build/lib/policies darwin + displayName: Generate policy definitions + retryCountOnTaskFailure: 3 + - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - script: | set -e diff --git a/build/azure-pipelines/linux/product-build-linux-legacy-server.yml b/build/azure-pipelines/linux/product-build-linux-legacy-server.yml deleted file mode 100644 index 4c26baf2f999..000000000000 --- a/build/azure-pipelines/linux/product-build-linux-legacy-server.yml +++ /dev/null @@ -1,233 +0,0 @@ -parameters: - - name: VSCODE_QUALITY - type: string - - name: VSCODE_RUN_INTEGRATION_TESTS - type: boolean - - name: VSCODE_ARCH - type: string - -steps: - - task: NodeTool@0 - inputs: - versionSource: fromFile - versionFilePath: .nvmrc - nodejsMirror: https://github.com/joaomoreno/node-mirror/releases/download - - - template: ../distro/download-distro.yml - - - task: AzureKeyVault@2 - displayName: "Azure Key Vault: Get Secrets" - inputs: - azureSubscription: vscode - KeyVaultName: vscode-build-secrets - SecretsFilter: "github-distro-mixin-password" - - - task: DownloadPipelineArtifact@2 - inputs: - artifact: Compilation - path: $(Build.ArtifactStagingDirectory) - displayName: Download compilation output - - - script: tar -xzf $(Build.ArtifactStagingDirectory)/compilation.tar.gz - displayName: Extract compilation output - - - script: | - set -e - # Start X server - ./build/azure-pipelines/linux/apt-retry.sh sudo apt-get update - ./build/azure-pipelines/linux/apt-retry.sh sudo apt-get install -y pkg-config \ - dbus \ - xvfb \ - libgtk-3-0 \ - libxkbfile-dev \ - libkrb5-dev \ - libgbm1 \ - rpm \ - gcc-10 \ - g++-10 - 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')) - displayName: Setup NPM Registry - - - script: | - set -e - # Set the private NPM registry to the global npmrc file - # so that authentication works for subfolders like build/, remote/, extensions/ etc - # which does not have their own .npmrc file - npm config set registry "$NPM_REGISTRY" - echo "##vso[task.setvariable variable=NPMRC_PATH]$(npm config get userconfig)" - condition: and(succeeded(), ne(variables['NPM_REGISTRY'], 'none')) - displayName: Setup NPM - - - task: npmAuthenticate@0 - inputs: - workingFile: $(NPMRC_PATH) - condition: and(succeeded(), ne(variables['NPM_REGISTRY'], 'none')) - displayName: Setup NPM Authentication - - - ${{ if or(eq(parameters.VSCODE_ARCH, 'x64'), eq(parameters.VSCODE_ARCH, 'arm64')) }}: - - task: Docker@1 - displayName: "Pull Docker image" - inputs: - azureSubscriptionEndpoint: vscode - azureContainerRegistry: vscodehub.azurecr.io - command: "Run an image" - imageName: vscode-linux-build-agent:centos7-devtoolset8-$(VSCODE_ARCH) - containerCommand: uname - - - script: | - set -e - - for i in {1..5}; do # try 5 times - npm ci && break - if [ $i -eq 5 ]; then - echo "Npm install failed too many times" >&2 - exit 1 - fi - echo "Npm install failed $i, trying again..." - done - workingDirectory: build - env: - GITHUB_TOKEN: "$(github-distro-mixin-password)" - displayName: Install build dependencies - - - script: | - set -e - - export VSCODE_SYSROOT_PREFIX='-glibc-2.17' - export CC=$(which gcc-10) - export CXX=$(which g++-10) - source ./build/azure-pipelines/linux/setup-env.sh --skip-sysroot - - for i in {1..5}; do # try 5 times - npm ci && break - if [ $i -eq 5 ]; then - echo "Npm install failed too many times" >&2 - exit 1 - fi - echo "Npm install failed $i, trying again..." - done - env: - npm_config_arch: $(NPM_ARCH) - VSCODE_ARCH: $(VSCODE_ARCH) - NPM_REGISTRY: "$(NPM_REGISTRY)" - ELECTRON_SKIP_BINARY_DOWNLOAD: 1 - PLAYWRIGHT_SKIP_BROWSER_DOWNLOAD: 1 - GITHUB_TOKEN: "$(github-distro-mixin-password)" - VSCODE_HOST_MOUNT: "/mnt/vss/_work/1/s" - ${{ if or(eq(parameters.VSCODE_ARCH, 'x64'), eq(parameters.VSCODE_ARCH, 'arm64')) }}: - VSCODE_REMOTE_DEPENDENCIES_CONTAINER_NAME: vscodehub.azurecr.io/vscode-linux-build-agent:centos7-devtoolset8-$(VSCODE_ARCH) - displayName: Install dependencies - - - script: node build/azure-pipelines/distro/mixin-npm - displayName: Mixin distro node modules - - - script: node build/azure-pipelines/distro/mixin-quality - displayName: Mixin distro quality - - - template: ../common/install-builtin-extensions.yml - - - script: | - set -e - npm run gulp vscode-linux-$(VSCODE_ARCH)-min-ci - ARCHIVE_PATH=".build/linux/client/code-${{ parameters.VSCODE_QUALITY }}-$(VSCODE_ARCH)-$(date +%s).tar.gz" - mkdir -p $(dirname $ARCHIVE_PATH) - echo "##vso[task.setvariable variable=CLIENT_PATH]$ARCHIVE_PATH" - env: - GITHUB_TOKEN: "$(github-distro-mixin-password)" - displayName: Build client - - - script: | - set -e - tar -czf $CLIENT_PATH -C .. VSCode-linux-$(VSCODE_ARCH) - env: - GITHUB_TOKEN: "$(github-distro-mixin-password)" - displayName: Archive client - - - script: | - set -e - export VSCODE_NODE_GLIBC="-glibc-2.17" - npm run gulp vscode-reh-linux-$(VSCODE_ARCH)-min-ci - mv ../vscode-reh-linux-$(VSCODE_ARCH) ../vscode-server-linux-$(VSCODE_ARCH) # TODO@joaomoreno - ARCHIVE_PATH=".build/linux/server/vscode-server-linux-legacy-$(VSCODE_ARCH).tar.gz" - UNARCHIVE_PATH="`pwd`/../vscode-server-linux-$(VSCODE_ARCH)" - mkdir -p $(dirname $ARCHIVE_PATH) - tar --owner=0 --group=0 -czf $ARCHIVE_PATH -C .. vscode-server-linux-$(VSCODE_ARCH) - echo "##vso[task.setvariable variable=SERVER_PATH]$ARCHIVE_PATH" - echo "##vso[task.setvariable variable=SERVER_UNARCHIVE_PATH]$UNARCHIVE_PATH" - env: - GITHUB_TOKEN: "$(github-distro-mixin-password)" - displayName: Build server - - - script: | - set -e - export VSCODE_NODE_GLIBC="-glibc-2.17" - npm run gulp vscode-reh-web-linux-$(VSCODE_ARCH)-min-ci - mv ../vscode-reh-web-linux-$(VSCODE_ARCH) ../vscode-server-linux-$(VSCODE_ARCH)-web # TODO@joaomoreno - ARCHIVE_PATH=".build/linux/web/vscode-server-linux-legacy-$(VSCODE_ARCH)-web.tar.gz" - mkdir -p $(dirname $ARCHIVE_PATH) - tar --owner=0 --group=0 -czf $ARCHIVE_PATH -C .. vscode-server-linux-$(VSCODE_ARCH)-web - echo "##vso[task.setvariable variable=WEB_PATH]$ARCHIVE_PATH" - env: - GITHUB_TOKEN: "$(github-distro-mixin-password)" - displayName: Build server (web) - - - ${{ if or(eq(parameters.VSCODE_ARCH, 'x64'), eq(parameters.VSCODE_ARCH, 'arm64')) }}: - - script: | - set -e - EXPECTED_GLIBC_VERSION="2.17" \ - EXPECTED_GLIBCXX_VERSION="3.4.19" \ - ./build/azure-pipelines/linux/verify-glibc-requirements.sh - env: - SEARCH_PATH: $(SERVER_UNARCHIVE_PATH) - displayName: Check GLIBC and GLIBCXX dependencies in server archive - - - ${{ else }}: - - script: | - set -e - EXPECTED_GLIBC_VERSION="2.17" \ - EXPECTED_GLIBCXX_VERSION="3.4.22" \ - ./build/azure-pipelines/linux/verify-glibc-requirements.sh - env: - SEARCH_PATH: $(SERVER_UNARCHIVE_PATH) - displayName: Check GLIBC and GLIBCXX dependencies in server archive - - - ${{ if eq(parameters.VSCODE_RUN_INTEGRATION_TESTS, true) }}: - - template: product-build-linux-test.yml - parameters: - VSCODE_QUALITY: ${{ parameters.VSCODE_QUALITY }} - VSCODE_RUN_UNIT_TESTS: false - VSCODE_RUN_INTEGRATION_TESTS: ${{ parameters.VSCODE_RUN_INTEGRATION_TESTS }} - VSCODE_RUN_SMOKE_TESTS: false - ${{ if ne(parameters.VSCODE_QUALITY, 'oss') }}: - PUBLISH_TASK_NAME: 1ES.PublishPipelineArtifact@1 - - - task: 1ES.PublishPipelineArtifact@1 - inputs: - targetPath: $(SERVER_PATH) - artifactName: $(ARTIFACT_PREFIX)vscode_server_linux_legacy_$(VSCODE_ARCH)_archive-unsigned - sbomBuildDropPath: $(Agent.BuildDirectory)/vscode-server-linux-$(VSCODE_ARCH) - sbomPackageName: "VS Code Linux $(VSCODE_ARCH) Legacy Server" - sbomPackageVersion: $(Build.SourceVersion) - condition: and(succeededOrFailed(), ne(variables['SERVER_PATH'], '')) - displayName: Publish server archive - - - task: 1ES.PublishPipelineArtifact@1 - inputs: - targetPath: $(WEB_PATH) - artifactName: $(ARTIFACT_PREFIX)vscode_web_linux_legacy_$(VSCODE_ARCH)_archive-unsigned - sbomBuildDropPath: $(Agent.BuildDirectory)/vscode-server-linux-$(VSCODE_ARCH)-web - sbomPackageName: "VS Code Linux $(VSCODE_ARCH) Legacy Web" - sbomPackageVersion: $(Build.SourceVersion) - condition: and(succeededOrFailed(), ne(variables['WEB_PATH'], '')) - displayName: Publish web server archive diff --git a/build/azure-pipelines/linux/setup-env.sh b/build/azure-pipelines/linux/setup-env.sh index dc444cc83f82..94105642b190 100755 --- a/build/azure-pipelines/linux/setup-env.sh +++ b/build/azure-pipelines/linux/setup-env.sh @@ -16,56 +16,50 @@ else fi if [ "$npm_config_arch" == "x64" ]; then - # Download clang based on chromium revision used by vscode - curl -s https://raw.githubusercontent.com/chromium/chromium/132.0.6834.194/tools/clang/scripts/update.py | python - --output-dir=$PWD/.build/CR_Clang --host-os=linux + # Download clang based on chromium revision used by vscode + curl -s https://raw.githubusercontent.com/chromium/chromium/132.0.6834.210/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 + # 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/132.0.6834.194:build/config/arm.gni - # https://source.chromium.org/chromium/chromium/src/+/refs/tags/132.0.6834.194:build/config/compiler/BUILD.gn - # https://source.chromium.org/chromium/chromium/src/+/refs/tags/132.0.6834.194:build/config/c++/BUILD.gn - export CC="$PWD/.build/CR_Clang/bin/clang --gcc-toolchain=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu" - export CXX="$PWD/.build/CR_Clang/bin/clang++ --gcc-toolchain=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu" - export CXXFLAGS="-nostdinc++ -D__NO_INLINE__ -DSPDLOG_USE_STD_FORMAT -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 -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_EXTENSIVE --sysroot=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot" - export LDFLAGS="-stdlib=libc++ --sysroot=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot -fuse-ld=lld -flto=thin -L$PWD/.build/libcxx-objects -lc++abi -L$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot/usr/lib/x86_64-linux-gnu -L$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot/lib/x86_64-linux-gnu -Wl,--lto-O0" + # Set compiler toolchain + # Flags for the client build are based on + # https://source.chromium.org/chromium/chromium/src/+/refs/tags/132.0.6834.210:build/config/arm.gni + # https://source.chromium.org/chromium/chromium/src/+/refs/tags/132.0.6834.210:build/config/compiler/BUILD.gn + # https://source.chromium.org/chromium/chromium/src/+/refs/tags/132.0.6834.210:build/config/c++/BUILD.gn + export CC="$PWD/.build/CR_Clang/bin/clang --gcc-toolchain=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu" + export CXX="$PWD/.build/CR_Clang/bin/clang++ --gcc-toolchain=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu" + export CXXFLAGS="-nostdinc++ -D__NO_INLINE__ -DSPDLOG_USE_STD_FORMAT -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 -D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_EXTENSIVE --sysroot=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot" + export LDFLAGS="-stdlib=libc++ --sysroot=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot -fuse-ld=lld -flto=thin -L$PWD/.build/libcxx-objects -lc++abi -L$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot/usr/lib/x86_64-linux-gnu -L$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot/lib/x86_64-linux-gnu -Wl,--lto-O0" - if [ "$(echo "$@" | grep -c -- "--skip-sysroot")" -eq 0 ]; then - # Set compiler toolchain for remote server - export VSCODE_REMOTE_CC=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/bin/x86_64-linux-gnu-gcc - export VSCODE_REMOTE_CXX=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/bin/x86_64-linux-gnu-g++ - export VSCODE_REMOTE_CXXFLAGS="--sysroot=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot" - export VSCODE_REMOTE_LDFLAGS="--sysroot=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot -L$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot/usr/lib/x86_64-linux-gnu -L$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot/lib/x86_64-linux-gnu" - fi + # Set compiler toolchain for remote server + export VSCODE_REMOTE_CC=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/bin/x86_64-linux-gnu-gcc + export VSCODE_REMOTE_CXX=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/bin/x86_64-linux-gnu-g++ + export VSCODE_REMOTE_CXXFLAGS="--sysroot=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot" + export VSCODE_REMOTE_LDFLAGS="--sysroot=$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot -L$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot/usr/lib/x86_64-linux-gnu -L$VSCODE_SYSROOT_DIR/x86_64-linux-gnu/x86_64-linux-gnu/sysroot/lib/x86_64-linux-gnu" elif [ "$npm_config_arch" == "arm64" ]; then - if [ "$(echo "$@" | grep -c -- "--skip-sysroot")" -eq 0 ]; then - # Set compiler toolchain for client native modules - export CC=$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc - export CXX=$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/bin/aarch64-linux-gnu-g++ - export CXXFLAGS="--sysroot=$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/aarch64-linux-gnu/sysroot" - export LDFLAGS="--sysroot=$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/aarch64-linux-gnu/sysroot -L$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/aarch64-linux-gnu/sysroot/usr/lib/aarch64-linux-gnu -L$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/aarch64-linux-gnu/sysroot/lib/aarch64-linux-gnu" + # Set compiler toolchain for client native modules + export CC=$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc + export CXX=$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/bin/aarch64-linux-gnu-g++ + export CXXFLAGS="--sysroot=$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/aarch64-linux-gnu/sysroot" + export LDFLAGS="--sysroot=$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/aarch64-linux-gnu/sysroot -L$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/aarch64-linux-gnu/sysroot/usr/lib/aarch64-linux-gnu -L$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/aarch64-linux-gnu/sysroot/lib/aarch64-linux-gnu" - # Set compiler toolchain for remote server - export VSCODE_REMOTE_CC=$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc - export VSCODE_REMOTE_CXX=$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/bin/aarch64-linux-gnu-g++ - export VSCODE_REMOTE_CXXFLAGS="--sysroot=$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/aarch64-linux-gnu/sysroot" - export VSCODE_REMOTE_LDFLAGS="--sysroot=$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/aarch64-linux-gnu/sysroot -L$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/aarch64-linux-gnu/sysroot/usr/lib/aarch64-linux-gnu -L$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/aarch64-linux-gnu/sysroot/lib/aarch64-linux-gnu" - fi + # Set compiler toolchain for remote server + export VSCODE_REMOTE_CC=$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/bin/aarch64-linux-gnu-gcc + export VSCODE_REMOTE_CXX=$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/bin/aarch64-linux-gnu-g++ + export VSCODE_REMOTE_CXXFLAGS="--sysroot=$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/aarch64-linux-gnu/sysroot" + export VSCODE_REMOTE_LDFLAGS="--sysroot=$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/aarch64-linux-gnu/sysroot -L$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/aarch64-linux-gnu/sysroot/usr/lib/aarch64-linux-gnu -L$VSCODE_SYSROOT_DIR/aarch64-linux-gnu/aarch64-linux-gnu/sysroot/lib/aarch64-linux-gnu" elif [ "$npm_config_arch" == "arm" ]; then - if [ "$(echo "$@" | grep -c -- "--skip-sysroot")" -eq 0 ]; then - # Set compiler toolchain for client native modules - export CC=$VSCODE_SYSROOT_DIR/arm-rpi-linux-gnueabihf/bin/arm-rpi-linux-gnueabihf-gcc - export CXX=$VSCODE_SYSROOT_DIR/arm-rpi-linux-gnueabihf/bin/arm-rpi-linux-gnueabihf-g++ - export CXXFLAGS="--sysroot=$VSCODE_SYSROOT_DIR/arm-rpi-linux-gnueabihf/arm-rpi-linux-gnueabihf/sysroot" - export LDFLAGS="--sysroot=$VSCODE_SYSROOT_DIR/arm-rpi-linux-gnueabihf/arm-rpi-linux-gnueabihf/sysroot -L$VSCODE_SYSROOT_DIR/arm-rpi-linux-gnueabihf/arm-rpi-linux-gnueabihf/sysroot/usr/lib/arm-linux-gnueabihf -L$VSCODE_SYSROOT_DIR/arm-rpi-linux-gnueabihf/arm-rpi-linux-gnueabihf/sysroot/lib/arm-linux-gnueabihf" - fi + # Set compiler toolchain for client native modules + export CC=$VSCODE_SYSROOT_DIR/arm-rpi-linux-gnueabihf/bin/arm-rpi-linux-gnueabihf-gcc + export CXX=$VSCODE_SYSROOT_DIR/arm-rpi-linux-gnueabihf/bin/arm-rpi-linux-gnueabihf-g++ + export CXXFLAGS="--sysroot=$VSCODE_SYSROOT_DIR/arm-rpi-linux-gnueabihf/arm-rpi-linux-gnueabihf/sysroot" + export LDFLAGS="--sysroot=$VSCODE_SYSROOT_DIR/arm-rpi-linux-gnueabihf/arm-rpi-linux-gnueabihf/sysroot -L$VSCODE_SYSROOT_DIR/arm-rpi-linux-gnueabihf/arm-rpi-linux-gnueabihf/sysroot/usr/lib/arm-linux-gnueabihf -L$VSCODE_SYSROOT_DIR/arm-rpi-linux-gnueabihf/arm-rpi-linux-gnueabihf/sysroot/lib/arm-linux-gnueabihf" # Set compiler toolchain for remote server export VSCODE_REMOTE_CC=$VSCODE_SYSROOT_DIR/arm-rpi-linux-gnueabihf/bin/arm-rpi-linux-gnueabihf-gcc diff --git a/build/azure-pipelines/product-build.yml b/build/azure-pipelines/product-build.yml index 7e680884b45d..8ec24be3892b 100644 --- a/build/azure-pipelines/product-build.yml +++ b/build/azure-pipelines/product-build.yml @@ -41,26 +41,14 @@ parameters: displayName: "🎯 Linux x64" type: boolean default: true - - name: VSCODE_BUILD_LINUX_X64_LEGACY_SERVER - displayName: "🎯 Linux x64 Legacy Server" - type: boolean - default: true - name: VSCODE_BUILD_LINUX_ARM64 displayName: "🎯 Linux arm64" type: boolean default: true - - name: VSCODE_BUILD_LINUX_ARM64_LEGACY_SERVER - displayName: "🎯 Linux arm64 Legacy Server" - type: boolean - default: true - name: VSCODE_BUILD_LINUX_ARMHF displayName: "🎯 Linux armhf" type: boolean default: true - - name: VSCODE_BUILD_LINUX_ARMHF_LEGACY_SERVER - displayName: "🎯 Linux armhf Legacy Server" - type: boolean - default: true - name: VSCODE_BUILD_ALPINE displayName: "🎯 Alpine x64" type: boolean @@ -106,7 +94,10 @@ variables: - name: VSCODE_PRIVATE_BUILD value: ${{ ne(variables['Build.Repository.Uri'], 'https://github.com/microsoft/vscode.git') }} - name: NPM_REGISTRY - value: ${{ parameters.NPM_REGISTRY }} + ${{ if in(variables['Build.Reason'], 'IndividualCI', 'BatchedCI') }}: # disable terrapin when in VSCODE_CIBUILD + value: none + ${{ else }}: + value: ${{ parameters.NPM_REGISTRY }} - name: CARGO_REGISTRY value: ${{ parameters.CARGO_REGISTRY }} - name: VSCODE_QUALITY @@ -115,8 +106,6 @@ variables: value: ${{ or(eq(parameters.VSCODE_BUILD_WIN32, true), eq(parameters.VSCODE_BUILD_WIN32_ARM64, true)) }} - name: VSCODE_BUILD_STAGE_LINUX value: ${{ or(eq(parameters.VSCODE_BUILD_LINUX, true), eq(parameters.VSCODE_BUILD_LINUX_ARMHF, true), eq(parameters.VSCODE_BUILD_LINUX_ARM64, true)) }} - - name: VSCODE_BUILD_STAGE_LINUX_LEGACY_SERVER - value: ${{ or(eq(parameters.VSCODE_BUILD_LINUX_X64_LEGACY_SERVER, true), eq(parameters.VSCODE_BUILD_LINUX_ARMHF_LEGACY_SERVER, true), eq(parameters.VSCODE_BUILD_LINUX_ARM64_LEGACY_SERVER, true)) }} - name: VSCODE_BUILD_STAGE_ALPINE value: ${{ or(eq(parameters.VSCODE_BUILD_ALPINE, true), eq(parameters.VSCODE_BUILD_ALPINE_ARM64, true)) }} - name: VSCODE_BUILD_STAGE_MACOS @@ -129,8 +118,6 @@ variables: value: ${{ and(eq(parameters.VSCODE_PUBLISH, true), eq(variables.VSCODE_CIBUILD, false), eq(parameters.VSCODE_COMPILE_ONLY, false)) }} - name: VSCODE_SCHEDULEDBUILD value: ${{ eq(variables['Build.Reason'], 'Schedule') }} - - name: VSCODE_7PM_BUILD - value: ${{ in(variables['Build.Reason'], 'BuildCompletion', 'ResourceTrigger') }} - name: VSCODE_STEP_ON_IT value: ${{ eq(parameters.VSCODE_STEP_ON_IT, true) }} - name: VSCODE_BUILD_MACOS_UNIVERSAL @@ -182,6 +169,8 @@ extends: tsa: enabled: true configFile: $(Build.SourcesDirectory)/build/azure-pipelines/config/tsaoptions.json + binskim: + analyzeTargetGlob: '+:file|$(Agent.BuildDirectory)/VSCode-*/**/*.exe;+:file|$(Agent.BuildDirectory)/VSCode-*/**/*.node;+:file|$(Agent.BuildDirectory)/VSCode-*/**/*.dll;-:file|$(Build.SourcesDirectory)/.build/**/system-setup/VSCodeSetup*.exe;-:file|$(Build.SourcesDirectory)/.build/**/user-setup/VSCodeUserSetup*.exe' codeql: runSourceLanguagesInSourceAnalysis: true compiled: @@ -200,6 +189,8 @@ extends: image: vscodehub.azurecr.io/vscode-linux-build-agent:snapcraft-x64 ubuntu-2004-arm64: image: onebranch.azurecr.io/linux/ubuntu-2004-arm64:latest + featureFlags: + EnableDefenderForLinux: true stages: - stage: Compile jobs: @@ -208,7 +199,10 @@ extends: pool: name: AcesShared os: macOS + # name: 1es-ubuntu-22.04-x64 + # os: linux variables: + # VSCODE_ARCH: x64 VSCODE_ARCH: arm64 steps: - template: build/azure-pipelines/product-compile.yml@self @@ -254,30 +248,30 @@ extends: VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} VSCODE_BUILD_ALPINE: ${{ parameters.VSCODE_BUILD_ALPINE }} - - ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_ALPINE_ARM64, true)) }}: - - job: CLIAlpineARM64 - pool: - name: 1es-mariner-2.0-arm64 - os: linux - hostArchitecture: arm64 - container: ubuntu-2004-arm64 - templateContext: - authenticatedContainerRegistries: - - registry: onebranch.azurecr.io - tenant: AME - identity: 1ESPipelineIdentity - steps: - - template: build/azure-pipelines/alpine/cli-build-alpine.yml@self - parameters: - VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - VSCODE_BUILD_ALPINE_ARM64: ${{ parameters.VSCODE_BUILD_ALPINE_ARM64 }} + # - ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_ALPINE_ARM64, true)) }}: + # - job: CLIAlpineARM64 + # pool: + # name: 1es-mariner-2.0-arm64 + # os: linux + # hostArchitecture: arm64 + # container: ubuntu-2004-arm64 + # steps: + # - template: build/azure-pipelines/alpine/cli-build-alpine.yml@self + # parameters: + # VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} + # VSCODE_BUILD_ALPINE_ARM64: ${{ parameters.VSCODE_BUILD_ALPINE_ARM64 }} - ${{ if eq(parameters.VSCODE_BUILD_MACOS, true) }}: - job: CLIMacOSX64 pool: - name: Azure Pipelines - image: macOS-13 + name: AcesShared + # name: Azure Pipelines + # image: macOS-13 os: macOS + variables: + # todo@connor4312 to diagnose build flakes + - name: MSRUSTUP_LOG + value: debug steps: - template: build/azure-pipelines/darwin/cli-build-darwin.yml@self parameters: @@ -288,9 +282,14 @@ extends: - ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_MACOS_ARM64, true)) }}: - job: CLIMacOSARM64 pool: - name: Azure Pipelines - image: macOS-13 + name: AcesShared + # name: Azure Pipelines + # image: macOS-13 os: macOS + variables: + # todo@connor4312 to diagnose build flakes + - name: MSRUSTUP_LOG + value: debug steps: - template: build/azure-pipelines/darwin/cli-build-darwin.yml@self parameters: @@ -321,13 +320,13 @@ extends: VSCODE_BUILD_WIN32_ARM64: ${{ parameters.VSCODE_BUILD_WIN32_ARM64 }} - ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_COMPILE_ONLY, false)) }}: - - stage: CustomSDL + - stage: APIScan dependsOn: [] pool: name: 1es-windows-2019-x64 os: windows jobs: - - job: WindowsSDL + - job: WindowsAPIScan steps: - template: build/azure-pipelines/win32/sdl-scan-win32.yml@self parameters: @@ -501,20 +500,15 @@ extends: VSCODE_RUN_INTEGRATION_TESTS: ${{ eq(parameters.VSCODE_STEP_ON_IT, false) }} VSCODE_RUN_SMOKE_TESTS: ${{ eq(parameters.VSCODE_STEP_ON_IT, false) }} - - ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_LINUX, true)) }}: - - job: LinuxSnap - dependsOn: - - Linuxx64 - container: snapcraft - variables: - VSCODE_ARCH: x64 - templateContext: - authenticatedContainerRegistries: - - registry: onebranch.azurecr.io - tenant: AME - identity: 1ESPipelineIdentity - steps: - - template: build/azure-pipelines/linux/snap-build-linux.yml@self + # - ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_LINUX, true)) }}: + # - job: LinuxSnap + # dependsOn: + # - Linuxx64 + # container: snapcraft + # variables: + # VSCODE_ARCH: x64 + # steps: + # - template: build/azure-pipelines/linux/snap-build-linux.yml@self - ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_BUILD_LINUX_ARMHF, true)) }}: - job: LinuxArmhf @@ -546,51 +540,6 @@ extends: VSCODE_RUN_INTEGRATION_TESTS: false VSCODE_RUN_SMOKE_TESTS: false - - ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_COMPILE_ONLY, false), eq(variables['VSCODE_BUILD_STAGE_LINUX_LEGACY_SERVER'], true)) }}: - - stage: LinuxLegacyServer - dependsOn: - - Compile - pool: - name: 1es-ubuntu-20.04-x64 - os: linux - jobs: - - ${{ if eq(parameters.VSCODE_BUILD_LINUX_X64_LEGACY_SERVER, true) }}: - - job: Linuxx64LegacyServer - variables: - VSCODE_ARCH: x64 - NPM_ARCH: x64 - DISPLAY: ":10" - steps: - - template: build/azure-pipelines/linux/product-build-linux-legacy-server.yml@self - parameters: - VSCODE_ARCH: x64 - VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - VSCODE_RUN_INTEGRATION_TESTS: ${{ eq(parameters.VSCODE_STEP_ON_IT, false) }} - - - ${{ if eq(parameters.VSCODE_BUILD_LINUX_ARMHF_LEGACY_SERVER, true) }}: - - job: LinuxArmhfLegacyServer - variables: - VSCODE_ARCH: armhf - NPM_ARCH: arm - steps: - - template: build/azure-pipelines/linux/product-build-linux-legacy-server.yml@self - parameters: - VSCODE_ARCH: armhf - VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - VSCODE_RUN_INTEGRATION_TESTS: false - - - ${{ if eq(parameters.VSCODE_BUILD_LINUX_ARM64_LEGACY_SERVER, true) }}: - - job: LinuxArm64LegacyServer - variables: - VSCODE_ARCH: arm64 - NPM_ARCH: arm64 - steps: - - template: build/azure-pipelines/linux/product-build-linux-legacy-server.yml@self - parameters: - VSCODE_ARCH: arm64 - VSCODE_QUALITY: ${{ variables.VSCODE_QUALITY }} - VSCODE_RUN_INTEGRATION_TESTS: false - - ${{ if and(eq(variables['VSCODE_CIBUILD'], false), eq(parameters.VSCODE_COMPILE_ONLY, false), eq(variables['VSCODE_BUILD_STAGE_ALPINE'], true)) }}: - stage: Alpine dependsOn: @@ -625,8 +574,7 @@ extends: - ${{ if or(eq(parameters.VSCODE_BUILD_LINUX, true),eq(parameters.VSCODE_BUILD_LINUX_ARMHF, true),eq(parameters.VSCODE_BUILD_LINUX_ARM64, true),eq(parameters.VSCODE_BUILD_ALPINE, true),eq(parameters.VSCODE_BUILD_ALPINE_ARM64, true),eq(parameters.VSCODE_BUILD_MACOS, true),eq(parameters.VSCODE_BUILD_MACOS_ARM64, true),eq(parameters.VSCODE_BUILD_WIN32, true),eq(parameters.VSCODE_BUILD_WIN32_ARM64, true)) }}: - CompileCLI pool: - name: Azure Pipelines - image: macOS-13 + name: AcesShared os: macOS variables: BUILDSECMON_OPT_IN: true @@ -690,7 +638,7 @@ extends: - job: macOSTest timeoutInMinutes: 90 variables: - VSCODE_ARCH: x64 + VSCODE_ARCH: arm64 steps: - template: build/azure-pipelines/darwin/product-build-darwin.yml@self parameters: diff --git a/build/azure-pipelines/product-compile.yml b/build/azure-pipelines/product-compile.yml index dcc1a62225d6..e3e0e4227412 100644 --- a/build/azure-pipelines/product-compile.yml +++ b/build/azure-pipelines/product-compile.yml @@ -53,10 +53,9 @@ steps: condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true'), ne(variables['NPM_REGISTRY'], 'none')) displayName: Setup NPM Authentication - - ${{ if eq(parameters.VSCODE_QUALITY, 'oss') }}: - - script: sudo apt update -y && sudo apt install -y build-essential pkg-config libx11-dev libx11-xcb-dev libxkbfile-dev libnotify-bin libkrb5-dev - displayName: Install build tools - condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) + # - script: sudo apt update -y && sudo apt install -y build-essential pkg-config libx11-dev libx11-xcb-dev libxkbfile-dev libnotify-bin libkrb5-dev + # displayName: Install build tools + # condition: and(succeeded(), ne(variables.NODE_MODULES_RESTORED, 'true')) - script: | set -e @@ -104,12 +103,12 @@ steps: - template: common/install-builtin-extensions.yml@self - ${{ if eq(parameters.VSCODE_QUALITY, 'oss') }}: - - script: npm exec -- npm-run-all -lp core-ci-pr extensions-ci-pr hygiene eslint valid-layers-check vscode-dts-compile-check tsec-compile-check + - script: npm exec -- npm-run-all -lp core-ci-pr extensions-ci-pr hygiene eslint valid-layers-check property-init-order-check vscode-dts-compile-check tsec-compile-check env: GITHUB_TOKEN: "$(github-distro-mixin-password)" displayName: Compile & Hygiene (OSS) - ${{ else }}: - - script: npm exec -- npm-run-all -lp core-ci extensions-ci hygiene eslint valid-layers-check vscode-dts-compile-check tsec-compile-check + - script: npm exec -- npm-run-all -lp core-ci extensions-ci hygiene eslint valid-layers-check property-init-order-check vscode-dts-compile-check tsec-compile-check env: GITHUB_TOKEN: "$(github-distro-mixin-password)" displayName: Compile & Hygiene (non-OSS) diff --git a/build/azure-pipelines/product-publish.yml b/build/azure-pipelines/product-publish.yml index 8ecf5e6238ef..1127e5212e9b 100644 --- a/build/azure-pipelines/product-publish.yml +++ b/build/azure-pipelines/product-publish.yml @@ -102,7 +102,6 @@ steps: $stages = @( if ($env:VSCODE_BUILD_STAGE_WINDOWS -eq 'True') { 'Windows' } if ($env:VSCODE_BUILD_STAGE_LINUX -eq 'True') { 'Linux' } - if ($env:VSCODE_BUILD_STAGE_LINUX_LEGACY_SERVER -eq 'True') { 'LinuxLegacyServer' } if ($env:VSCODE_BUILD_STAGE_ALPINE -eq 'True') { 'Alpine' } if ($env:VSCODE_BUILD_STAGE_MACOS -eq 'True') { 'macOS' } if ($env:VSCODE_BUILD_STAGE_WEB -eq 'True') { 'Web' } diff --git a/build/azure-pipelines/publish-types/update-types.ts b/build/azure-pipelines/publish-types/update-types.ts index 0f99b07cf9a3..3bb02b7adabd 100644 --- a/build/azure-pipelines/publish-types/update-types.ts +++ b/build/azure-pipelines/publish-types/update-types.ts @@ -16,7 +16,7 @@ try { const dtsUri = `https://raw.githubusercontent.com/microsoft/vscode/${tag}/src/vscode-dts/vscode.d.ts`; const outPath = path.resolve(process.cwd(), 'DefinitelyTyped/types/vscode/index.d.ts'); - cp.execSync(`curl ${dtsUri} --output ${outPath}`); + cp.execFileSync('curl', [dtsUri, '--output', outPath]); updateDTSFile(outPath, tag); diff --git a/build/azure-pipelines/win32/product-build-win32.yml b/build/azure-pipelines/win32/product-build-win32.yml index ed7f10048cad..ab0b6cbb4dff 100644 --- a/build/azure-pipelines/win32/product-build-win32.yml +++ b/build/azure-pipelines/win32/product-build-win32.yml @@ -124,7 +124,7 @@ steps: - template: ../common/install-builtin-extensions.yml@self - ${{ if and(ne(parameters.VSCODE_CIBUILD, true), ne(parameters.VSCODE_QUALITY, 'oss')) }}: - - powershell: node build\lib\policies + - powershell: node build\lib\policies win32 displayName: Generate Group Policy definitions retryCountOnTaskFailure: 3 @@ -145,7 +145,7 @@ steps: exec { npm run gulp "vscode-win32-$(VSCODE_ARCH)-min-ci" } exec { npm run gulp "vscode-win32-$(VSCODE_ARCH)-inno-updater" } echo "##vso[task.setvariable variable=BUILT_CLIENT]true" - echo "##vso[task.setvariable variable=CodeSigningFolderPath]$(agent.builddirectory)/VSCode-win32-$(VSCODE_ARCH)" + echo "##vso[task.setvariable variable=CodeSigningFolderPath]$(Agent.BuildDirectory)/VSCode-win32-$(VSCODE_ARCH)" env: GITHUB_TOKEN: "$(github-distro-mixin-password)" displayName: Build client @@ -156,7 +156,7 @@ steps: exec { npm run gulp "vscode-reh-win32-$(VSCODE_ARCH)-min-ci" } mv ..\vscode-reh-win32-$(VSCODE_ARCH) ..\vscode-server-win32-$(VSCODE_ARCH) # TODO@joaomoreno echo "##vso[task.setvariable variable=BUILT_SERVER]true" - echo "##vso[task.setvariable variable=CodeSigningFolderPath]$(CodeSigningFolderPath),$(agent.builddirectory)/vscode-server-win32-$(VSCODE_ARCH)" + echo "##vso[task.setvariable variable=CodeSigningFolderPath]$(CodeSigningFolderPath),$(Agent.BuildDirectory)/vscode-server-win32-$(VSCODE_ARCH)" env: GITHUB_TOKEN: "$(github-distro-mixin-password)" displayName: Build server @@ -196,10 +196,10 @@ steps: $ErrorActionPreference = "Stop" $ArtifactName = (gci -Path "$(Build.ArtifactStagingDirectory)/cli" | Select-Object -last 1).FullName Expand-Archive -Path $ArtifactName -DestinationPath "$(Build.ArtifactStagingDirectory)/cli" - $AppProductJson = Get-Content -Raw -Path "$(agent.builddirectory)\VSCode-win32-$(VSCODE_ARCH)\resources\app\product.json" | ConvertFrom-Json + $AppProductJson = Get-Content -Raw -Path "$(Agent.BuildDirectory)\VSCode-win32-$(VSCODE_ARCH)\resources\app\product.json" | ConvertFrom-Json $CliAppName = $AppProductJson.tunnelApplicationName $AppName = $AppProductJson.applicationName - Move-Item -Path "$(Build.ArtifactStagingDirectory)/cli/$AppName.exe" -Destination "$(agent.builddirectory)/VSCode-win32-$(VSCODE_ARCH)/bin/$CliAppName.exe" + Move-Item -Path "$(Build.ArtifactStagingDirectory)/cli/$AppName.exe" -Destination "$(Agent.BuildDirectory)/VSCode-win32-$(VSCODE_ARCH)/bin/$CliAppName.exe" displayName: Move VS Code CLI - task: UseDotNet@2 @@ -247,13 +247,16 @@ steps: condition: succeededOrFailed() displayName: Get product version - - powershell: | + - powershell: | . build/azure-pipelines/win32/exec.ps1 $ErrorActionPreference = "Stop" $ArchivePath = ".build\win32-$(VSCODE_ARCH)\VSCode-win32-$(VSCODE_ARCH)-$(VSCODE_VERSION).zip" New-Item -ItemType Directory -Path .build\win32-$(VSCODE_ARCH) -Force - exec { 7z.exe a -tzip $ArchivePath -x!CodeSignSummary*.md ..\VSCode-win32-$(VSCODE_ARCH)\* -r } + exec { 7z.exe a -tzip $ArchivePath ..\VSCode-win32-$(VSCODE_ARCH)\* "-xr!CodeSignSummary*.md" } echo "##vso[task.setvariable variable=CLIENT_PATH]$ArchivePath" + + echo "Listing archive contents" + 7z.exe l $ArchivePath condition: and(succeededOrFailed(), eq(variables['BUILT_CLIENT'], 'true')) displayName: Package client @@ -262,8 +265,11 @@ 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) } echo "##vso[task.setvariable variable=SERVER_PATH]$ArchivePath" + + echo "Listing archive contents" + 7z.exe l $ArchivePath condition: and(succeededOrFailed(), eq(variables['BUILT_SERVER'], 'true')) displayName: Package server @@ -272,8 +278,11 @@ 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 } echo "##vso[task.setvariable variable=WEB_PATH]$ArchivePath" + + echo "Listing archive contents" + 7z.exe l $ArchivePath condition: and(succeededOrFailed(), eq(variables['BUILT_WEB'], 'true')) displayName: Package server (web) diff --git a/build/azure-pipelines/win32/sdl-scan-win32.yml b/build/azure-pipelines/win32/sdl-scan-win32.yml index def3cb53dfcc..bf6819a4b479 100644 --- a/build/azure-pipelines/win32/sdl-scan-win32.yml +++ b/build/azure-pipelines/win32/sdl-scan-win32.yml @@ -102,13 +102,6 @@ steps: - powershell: npm run compile displayName: Compile - - powershell: | - Get-ChildItem '$(Build.SourcesDirectory)' -Recurse -Filter "*.exe" - Get-ChildItem '$(Build.SourcesDirectory)' -Recurse -Filter "*.dll" - Get-ChildItem '$(Build.SourcesDirectory)' -Recurse -Filter "*.node" - Get-ChildItem '$(Build.SourcesDirectory)' -Recurse -Filter "*.pdb" - displayName: List files - - powershell: npm run gulp "vscode-symbols-win32-${{ parameters.VSCODE_ARCH }}" env: GITHUB_TOKEN: "$(github-distro-mixin-password)" @@ -119,16 +112,7 @@ steps: Get-ChildItem '$(Agent.BuildDirectory)\scanbin' -Recurse -Filter "*.dll" Get-ChildItem '$(Agent.BuildDirectory)\scanbin' -Recurse -Filter "*.node" Get-ChildItem '$(Agent.BuildDirectory)\scanbin' -Recurse -Filter "*.pdb" - displayName: List files again - - - task: BinSkim@4 - inputs: - InputType: "Basic" - Function: "analyze" - TargetPattern: "guardianGlob" - AnalyzeIgnorePdbLoadError: true - AnalyzeTargetGlob: '$(Agent.BuildDirectory)\scanbin\**.dll;$(Agent.BuildDirectory)\scanbin\**.exe;$(Agent.BuildDirectory)\scanbin\**.node' - AnalyzeLocalSymbolDirectories: '$(Agent.BuildDirectory)\scanbin\VSCode-win32-${{ parameters.VSCODE_ARCH }}\pdb' + displayName: List files - task: CopyFiles@2 displayName: 'Collect Symbols for API Scan' @@ -139,19 +123,6 @@ steps: flattenFolders: true condition: succeeded() - - task: PublishSymbols@2 - inputs: - IndexSources: false - SymbolsFolder: '$(Agent.BuildDirectory)\symbols' - SearchPattern: '**\*.pdb' - SymbolServerType: TeamServices - SymbolsProduct: 'code' - ArtifactServices.Symbol.AccountName: microsoft - ArtifactServices.Symbol.PAT: $(System.AccessToken) - ArtifactServices.Symbol.UseAAD: false - displayName: Publish Symbols - condition: succeeded() - - task: APIScan@2 inputs: softwareFolder: $(Agent.BuildDirectory)\scanbin diff --git a/build/checksums/electron.txt b/build/checksums/electron.txt index 7ea1ada127ca..d390d7a233ca 100644 --- a/build/checksums/electron.txt +++ b/build/checksums/electron.txt @@ -1,75 +1,75 @@ -2e7aec01196298a2d8ce2f348a7b7f73cb8aa5b368fc6793411b9b173716383a *chromedriver-v34.1.0-darwin-arm64.zip -beeaeef6fc09cebb0daeae09c77884a3e4ba8e136aa7ff5e7e2b73f9139d0c1c *chromedriver-v34.1.0-darwin-x64.zip -5914d3df6b44207d086eecab2297980f01cd0c2641de9338222547868ca752de *chromedriver-v34.1.0-linux-arm64.zip -4116d2237e5c1aeb829dedc4284e224453577b500b42dcf0854e3dc43eecd584 *chromedriver-v34.1.0-linux-armv7l.zip -a3198d2a4bfcb984e00fe4b4bc9b2bb6c886a1822ff97e99fad570cf5d346de7 *chromedriver-v34.1.0-linux-x64.zip -1be1aff31889fae70833e41f1d2c5c786e982db631f52930976367406bec2e99 *chromedriver-v34.1.0-mas-arm64.zip -8acd9a0d3be0e59fc9ef5eb041afbd8064a0b36e72b255e0266f9cc0d2a32a8e *chromedriver-v34.1.0-mas-x64.zip -ed9ef15dd34a37cc382fb1873b2c7f56a8012551d30fc1b2373d8fb5a7c370e9 *chromedriver-v34.1.0-win32-arm64.zip -97853f50cc657c53f970de66b9709ea2853ac0a7b07f61f5f491538bc9372af9 *chromedriver-v34.1.0-win32-ia32.zip -8342eec7aa41c1c383f13d9d6515ebfb50892e5ac4fe1f42c5111f4c5e2b7e6c *chromedriver-v34.1.0-win32-x64.zip -ed5fdef3441a90c9d7b2425d77575d92ce8c718ecf41caee9ae105b9f0519a2e *electron-api.json -82ee848a4c83c1390453cf1475a4a74a49fc3753ae899266d4db4227fe233a4b *electron-v34.1.0-darwin-arm64-dsym-snapshot.zip -3a45222b3978325b62c31cf85e7c68ce3f58222e53e2ef72acc2be6ed60f264b *electron-v34.1.0-darwin-arm64-dsym.zip -aee0826753792516d01c89edb77dbe6f43d3bd8a30b03288fd0d6d704a7d3f91 *electron-v34.1.0-darwin-arm64-symbols.zip -fce8fd9bd56d0bdfbed5dcaab44538a6b109db688719062d28419970883166de *electron-v34.1.0-darwin-arm64.zip -159d5c354c769bd6c67983f076a399d2c702e29341c06171185d3779dda7ea08 *electron-v34.1.0-darwin-x64-dsym-snapshot.zip -0415b02fa238326269ff32637a7c96f798ebbc5b948091231be036c1dc166260 *electron-v34.1.0-darwin-x64-dsym.zip -eeb1d0188adbeb007acc84328036d870938f1e6634c64f117a90bd05503555c0 *electron-v34.1.0-darwin-x64-symbols.zip -3a6c0ac76faee46d4d3f7be98f0b1f5b3a549589bcb996a21d1f6dccf2b3e986 *electron-v34.1.0-darwin-x64.zip -fb69e1089d16bd6cc8e66f7c517eb8447155497533b779a1becadbea942c537b *electron-v34.1.0-linux-arm64-debug.zip -f048b5257ccf022e4b0d2d69f3775dfe48286a9d375280652eb4394be45d47a0 *electron-v34.1.0-linux-arm64-symbols.zip -a15f59dbe664b3d711cc0f8a7b9693a44ad27a096341d9b9b15f05c880d65e64 *electron-v34.1.0-linux-arm64.zip -fb69e1089d16bd6cc8e66f7c517eb8447155497533b779a1becadbea942c537b *electron-v34.1.0-linux-armv7l-debug.zip -44b53952aa5730d1a1882f1e64a85e52894fb1c970ab334a49e44a4d09ac9db3 *electron-v34.1.0-linux-armv7l-symbols.zip -8136d1a88275d5e4b30c1f7fa2ffda966283d9a062460f844549e3d8cdac8cdd *electron-v34.1.0-linux-armv7l.zip -b66e225ecb4f5821bb2c0c06a587987b93a5c891bf170b2a6f18bd069ac585c2 *electron-v34.1.0-linux-x64-debug.zip -f067ae81ef451c2d5708cc55a37973072462063baca5a6bc57708f57bd7c626a *electron-v34.1.0-linux-x64-symbols.zip -ded3f8a3e603c06529b12c0400f1bbda7ae201863510a5aa8d78ae721773e5aa *electron-v34.1.0-linux-x64.zip -c5078be0a0c8acc9701b8f734909a45d8225aeb81a13a8b114cac16d016b4835 *electron-v34.1.0-mas-arm64-dsym-snapshot.zip -8a948ad2ef24fce9dc4844670a6c1a3f1af99ebb3a8410d666a1d2c8f4660d24 *electron-v34.1.0-mas-arm64-dsym.zip -8b45c07da9fb118416b6ba0217439378675045cd94e3a5340d6e02bde93c9115 *electron-v34.1.0-mas-arm64-symbols.zip -71aa2f5b0c2973b5700e75a24bdeb662d164096ed4bbbabc60629ec81e1c7abe *electron-v34.1.0-mas-arm64.zip -1f186d6067c2055b40d39baf06e4ee43f6b27711e835b86220ce95dfd3c8c29e *electron-v34.1.0-mas-x64-dsym-snapshot.zip -ab7a2fd2fd26932198603ec01194421ab141c4a9d0d9c5831457479ff4d67e3b *electron-v34.1.0-mas-x64-dsym.zip -d91423ac49e20dee1a3250ef3a24ecac48254a9e65a9d9e9826d1e675f9a98d5 *electron-v34.1.0-mas-x64-symbols.zip -c3c49687b910022d66aebecb93118c83035462d69591bc2c218cf21015700567 *electron-v34.1.0-mas-x64.zip -e395b2b2518a680c5632c0d607d04d7886583791b0041ef36e3a3aae6e4fbf18 *electron-v34.1.0-win32-arm64-pdb.zip -c85c78ff1336a7ade70bffe75f19ac71480b5b81462b4997a8bdd218921ea18f *electron-v34.1.0-win32-arm64-symbols.zip -0fb20c9c01fa1748309b4c3ef9ec0d9ec53828b333b7938d811c06923a2e3915 *electron-v34.1.0-win32-arm64-toolchain-profile.zip -b0e2c41af5146f5095c9b6f2de1de4a3332caf76f70142dfd1a2e53fd96f0f56 *electron-v34.1.0-win32-arm64.zip -f921c2c6dd52d5b550967f94d16e6029e829945102c1302e65255f46cab7ad90 *electron-v34.1.0-win32-ia32-pdb.zip -f093520765c39088adae7efd15bf2c13a70b64c6d3592cdfa9e05061b8f6e35f *electron-v34.1.0-win32-ia32-symbols.zip -0fb20c9c01fa1748309b4c3ef9ec0d9ec53828b333b7938d811c06923a2e3915 *electron-v34.1.0-win32-ia32-toolchain-profile.zip -5e5a4a87623920e03ac62b60f6a6cdbb146265437ae1deb20903f40b151eb3f0 *electron-v34.1.0-win32-ia32.zip -900b08d73c26b4277ba8abfb2ff9000b924aeffab910b3f983440f06ad8792b9 *electron-v34.1.0-win32-x64-pdb.zip -f834dc09210e2a326126ffb9b53e727af5fcaa5d900cf2e05bafa78634b3640a *electron-v34.1.0-win32-x64-symbols.zip -0fb20c9c01fa1748309b4c3ef9ec0d9ec53828b333b7938d811c06923a2e3915 *electron-v34.1.0-win32-x64-toolchain-profile.zip -059e628cb591a8eac961b255aa81d64d2b414295ff6984efd2b8fdf3dce9f40a *electron-v34.1.0-win32-x64.zip -52191fc329244f20416d07f2b7353a8eaa1d9e532587ede1211cede0bfbe5f71 *electron.d.ts -6f52f192329cc1df54e35ec94f7d8167790dcedbb1fb5ba7dcb90933df703bac *ffmpeg-v34.1.0-darwin-arm64.zip -801a45f22dfcd70b161b4a90b8cf011f7d7645ff4e140f8e4b214be2fe4dfe41 *ffmpeg-v34.1.0-darwin-x64.zip -3a4c3b1eeac3fdfc6258946ce072e2ef22668463baa3d45e142322f90ed55ebc *ffmpeg-v34.1.0-linux-arm64.zip -5b9e86fc74ecf4fd40f06792b69e402367d51e59be2ad2de1e93ed544c13fcd0 *ffmpeg-v34.1.0-linux-armv7l.zip -ce4a651403b866404353a9a2e2b9e385daf111888040664f3af955c602754b23 *ffmpeg-v34.1.0-linux-x64.zip -8b3aabc3d468229895592e0945222ead389f2c4bf31c2c52d062c1adcb21fbe6 *ffmpeg-v34.1.0-mas-arm64.zip -f5ed2bc39da590aeaf63c5f2ea8fc08b212dcd8d3cb0439802eb7e12d88dada4 *ffmpeg-v34.1.0-mas-x64.zip -e001821ce780541bb36fe6a101060ded7cff20537ea1ca5cdbf4e4a89fb93d45 *ffmpeg-v34.1.0-win32-arm64.zip -f1ef2f7a751b7894cc0bd08a01f8bc13999c769d5453ac1024b2df6d0dfa662d *ffmpeg-v34.1.0-win32-ia32.zip -8edefb7f2920ac87bef2407ff4c3486b295e0e48613cbacdf4211a0090396526 *ffmpeg-v34.1.0-win32-x64.zip -ce2a30a8784c19c446f8a2173c7fd23d0606443cef7c5b2d17bdb3a237d92481 *hunspell_dictionaries.zip -8ceacc2f2230da0431d9f8f7308fd30070a945cb4b95f84eeb977cec942c0019 *libcxx-objects-v34.1.0-linux-arm64.zip -0b6d462f60fd2cdc2282ae7dd239900568019b1b0b22b72b2536227a5c3a2b62 *libcxx-objects-v34.1.0-linux-armv7l.zip -bc53041d9a7cf26dd804e061e5ce72db84bdaa32c1646d7b8baa8bf3203984b3 *libcxx-objects-v34.1.0-linux-x64.zip -2934be982e54f49d9a0e1071cfb1f07890611a9505e9fa3885c534af7b521a12 *libcxx_headers.zip -23be04f992ee37ce8f29ffb9e53bd5bf461c50a91b6eddedaecc9f20b8967937 *libcxxabi_headers.zip -e45742b0a3f607840b7617dcd3ebcb6d3b5f5f88bbb252aba8d519e8287e28c1 *mksnapshot-v34.1.0-darwin-arm64.zip -74c4e22d2a23fa924da5ea3647755e1c24f279656017a9be9d0d494cd6bd6f02 *mksnapshot-v34.1.0-darwin-x64.zip -6eff2e983909137c89672dde61c4382f7f23e4848f3c1c767af90eda65e09c34 *mksnapshot-v34.1.0-linux-arm64-x64.zip -60221b726687a31f629ab703ce4adfa52381170ad23a5fa442ad875b1c231458 *mksnapshot-v34.1.0-linux-armv7l-x64.zip -3bd0ae894f8f124350be423ea25d34b959d13232f95d0843f4da361408d33894 *mksnapshot-v34.1.0-linux-x64.zip -422b1dee280db0b18ad501ede5feb19e41f6e69239d813ecafd49a6e38ebcba7 *mksnapshot-v34.1.0-mas-arm64.zip -78d3201cdcda856dc8b8adf12582e3a270b82e9aff50e070fd0fc3269ec98f22 *mksnapshot-v34.1.0-mas-x64.zip -175d9b23829676b5df18f996ed8d26750d865340302a4cfc98f19bd100e0c194 *mksnapshot-v34.1.0-win32-arm64-x64.zip -1fdb2437733687f079eb6bda27c8df30c358b2bb2b794d0f46c3de1119e7c6e1 *mksnapshot-v34.1.0-win32-ia32.zip -58e2ee0656fae9401b9dbb3e3521bcd414b817eb0894d6167d5a14827acdb76f *mksnapshot-v34.1.0-win32-x64.zip +c9b82c9f381742e839fea00aeb14f24519bcaf38a0f4eed25532191701f9535b *chromedriver-v34.3.2-darwin-arm64.zip +d556c1e2b06f1bf131e83c2fb981de755c28e1083a884d257eb964815be16b0c *chromedriver-v34.3.2-darwin-x64.zip +1cabad4f3303ac2ff172a9f22185f64944dbaa6fc68271609077158eaefdee35 *chromedriver-v34.3.2-linux-arm64.zip +4213ce52c72ef414179b5c5c22ae8423847ff030d438296bd6c2aac763930a7b *chromedriver-v34.3.2-linux-armv7l.zip +3c64c08221fdfc0f4be60ea8b1b126f2ecca45f60001b63778522f711022c6ea *chromedriver-v34.3.2-linux-x64.zip +e8388734d88e011cb6cd79795431de9206820749219d80565ee49d90501d2bf3 *chromedriver-v34.3.2-mas-arm64.zip +3ad1dd37bd6e0bb37e8503898db7aedd56bd5213e6d6760b05c3d11f4625062b *chromedriver-v34.3.2-mas-x64.zip +d567b481a0f5d88e84bba7718f89fb08f56363bfc4cb5914e1c2086358a5c252 *chromedriver-v34.3.2-win32-arm64.zip +df6732e9dc61cb20a3c0b2a2de453aac7e2bd54e7cbff43512afa614852c15fa *chromedriver-v34.3.2-win32-ia32.zip +dda0765c8d064924632e18cd152014ecd767f3808fc51c8249a053bfb7ca70a2 *chromedriver-v34.3.2-win32-x64.zip +1945f15caff98f2e0f1ee539c483d352fb8d4d0c13f342caa7abe247676d828c *electron-api.json +c078bbf727b3c3026f60e07a0f4643b85c06c581b54be017d0a6c284ba6772d3 *electron-v34.3.2-darwin-arm64-dsym-snapshot.zip +35f587754d6a3272606258386bf73688d63dd53c7e572d3a7cbaae6f3f60bdae *electron-v34.3.2-darwin-arm64-dsym.zip +08b14ee02c98353de3c738120dfd017322666e82b914a7f6de9b9888dcc5c0f0 *electron-v34.3.2-darwin-arm64-symbols.zip +2a4aa7e8fa30f229e465ebd18d3e4722e2b41529dc51a68a954d333a7e556ffe *electron-v34.3.2-darwin-arm64.zip +1509ccdeb80024f5e3edd5ecf804b4cef4e47ea2bd74e33ef0b39044b0ccf892 *electron-v34.3.2-darwin-x64-dsym-snapshot.zip +3bbe5d587c3f582ed8c126b0fb635cc02ad9a14d077b04892fe6f862092445b0 *electron-v34.3.2-darwin-x64-dsym.zip +fa7ece82e6ecaf1c94ed341e8ebff98e64687c68fe113f52cd9a21400302e22f *electron-v34.3.2-darwin-x64-symbols.zip +23938c62257a65a863ed7aa7c7966ba5f257a7d3dc16c78293e826962cc39c5c *electron-v34.3.2-darwin-x64.zip +0547eecf8ab538d74fa854f591ce8b888a3dbb339256d2db3038e7bb2c6dd929 *electron-v34.3.2-linux-arm64-debug.zip +676d0dc2b1c1c85c8b2abbb8cd5376ee22ecdb910493b910d9ae5a998532136a *electron-v34.3.2-linux-arm64-symbols.zip +774e4ccb39d553e5487994a9f8c60774a90f08cdb049ff65f3963fc27c969ff2 *electron-v34.3.2-linux-arm64.zip +0547eecf8ab538d74fa854f591ce8b888a3dbb339256d2db3038e7bb2c6dd929 *electron-v34.3.2-linux-armv7l-debug.zip +ba33bf53fcb35dea568a2795f5b23ecf46c218abe8258946611c72a1f42f716c *electron-v34.3.2-linux-armv7l-symbols.zip +73ae92c8fffb351d3a455569cf57ce9a3f676f42bf61939c613c607fe0fc3bfb *electron-v34.3.2-linux-armv7l.zip +e61a9a69dd7ea6f2687708a8e83516670cdea53c728226e598e2f6f1fad5b77b *electron-v34.3.2-linux-x64-debug.zip +f1a04df7fe67dd1cd29e7b87871525458d2eb24c0cf3b5835a1c56974707562a *electron-v34.3.2-linux-x64-symbols.zip +7b74c0c4fae82e27c7e9cbca13e9763e046113dba8737d3e27de9a0b300ac87e *electron-v34.3.2-linux-x64.zip +8571a6aa83e00925ceb39fdc5a45a9f6b9aa3d92fd84951c6f252ed723aea4ae *electron-v34.3.2-mas-arm64-dsym-snapshot.zip +477410c6f9a6c5eeaedf376058a02c2996fc0a334aa40eeec7d3734c09347f4d *electron-v34.3.2-mas-arm64-dsym.zip +c2e62dcd6630cb51b2d8e2869e74e47d29bda785521cea6e82e546d0fc58aabb *electron-v34.3.2-mas-arm64-symbols.zip +a1698e8546a062fd59b7f8e5507a7f3220fb00b347f2377de83fc9a07f7f3507 *electron-v34.3.2-mas-arm64.zip +741a24ac230a3651dca81d211f9f00b835c428a5ed0c5f67d370d4e88b62f8d6 *electron-v34.3.2-mas-x64-dsym-snapshot.zip +aeff97ec9e5c9e173ac89e38acd94476025c5640d5f27be1e8c2abd54398bab3 *electron-v34.3.2-mas-x64-dsym.zip +9f14b66b1d612ac66697288e8763171c388f7f200854871a5f0ab464a6a921c2 *electron-v34.3.2-mas-x64-symbols.zip +c979d7e7175f1e8e03ca187997d4c156b878189fc3611b347fadebcb16f3e027 *electron-v34.3.2-mas-x64.zip +f43c700641e8220205dd356952e32718d113cf530520c4ed7209b59851eac266 *electron-v34.3.2-win32-arm64-pdb.zip +3ba6e01c99bffac6b5dd3fd6f122ecdb571cf6f675dc5498c65050bd7a382ef8 *electron-v34.3.2-win32-arm64-symbols.zip +c23f84aabb09c24cd2ae759a547fdba4206af19a3bb0f4554a91cd9528648ad0 *electron-v34.3.2-win32-arm64-toolchain-profile.zip +9b9cb65d75a16782088b492f9ef3bb4d27525012b819c12bf29bd27e159d749b *electron-v34.3.2-win32-arm64.zip +1006e7af4c149114b5ebc3497617aaa6cd1bb0b131e0a225fd73709ff308f9c5 *electron-v34.3.2-win32-ia32-pdb.zip +1ecb6430cd04454f08f557c9579163f3552144bfcc0b67b768dad8868b5b891d *electron-v34.3.2-win32-ia32-symbols.zip +c23f84aabb09c24cd2ae759a547fdba4206af19a3bb0f4554a91cd9528648ad0 *electron-v34.3.2-win32-ia32-toolchain-profile.zip +d004fd5f853754001fafaec33e383d1950b30c935ee71b297ec1c9e084355e9b *electron-v34.3.2-win32-ia32.zip +4e0721552fd2f09e9466e88089af8b965f1bfbc4ae00a59aaf6245b1d1efabfd *electron-v34.3.2-win32-x64-pdb.zip +9dea812a7e7cd0fb18e5fed9a99db5531959a068c24d3c0ecedceb644cd3ffa0 *electron-v34.3.2-win32-x64-symbols.zip +c23f84aabb09c24cd2ae759a547fdba4206af19a3bb0f4554a91cd9528648ad0 *electron-v34.3.2-win32-x64-toolchain-profile.zip +1785e161420fb90d2331c26e50bba3413cae9625b7db3c8524ea02ade631efba *electron-v34.3.2-win32-x64.zip +722b304c31ddac58b0083d94a241c5276464f04bd8ea4fcbfd33051d197be103 *electron.d.ts +31ce159b2e47d1de5bc907d8e1c89726b0f2ba530ec2e9d7a8e5c723b1ccf6e0 *ffmpeg-v34.3.2-darwin-arm64.zip +565539bac64a6ee9cf6f188070f520210a1507341718f5dc388ac7c454b1e1d5 *ffmpeg-v34.3.2-darwin-x64.zip +6006ea0f46ab229feb2685be086b0fafd65981e2939dd2218a078459c75ab527 *ffmpeg-v34.3.2-linux-arm64.zip +9404ce2e85df7c40f809f2cf62c7af607de299839fe6b7ae978c3015500abcc8 *ffmpeg-v34.3.2-linux-armv7l.zip +79aec96898b7e2462826780ee0b52b9ab299dc662af333e128a34fd5ddae87f1 *ffmpeg-v34.3.2-linux-x64.zip +9190743c78210574faf5d5ecb82a00f8fa15e5f2253378cb925a99ca9d39961b *ffmpeg-v34.3.2-mas-arm64.zip +48915adcb1a6342efeda896035101300f0432c0304cfb38f2378e98c6309ebae *ffmpeg-v34.3.2-mas-x64.zip +745d5ef786de6d4a720475079836e2fda7b501cfcd255819485a47de5b24b74e *ffmpeg-v34.3.2-win32-arm64.zip +d0d86d60978439dc8ae4a723d4e4c1f853891d596bfd84033440a232fa762e2f *ffmpeg-v34.3.2-win32-ia32.zip +4441539fd8c9cbe79880ff1bade9bdc0c3744c33d7409130af6404e57ee401ff *ffmpeg-v34.3.2-win32-x64.zip +39edd1eeefe881aa75af0e438204e0b1c6e6724e34fa5819109276331c0c2c9a *hunspell_dictionaries.zip +20dd417536e5f4ebc01f480221284c0673729c27b082bc04e2922f16cd571719 *libcxx-objects-v34.3.2-linux-arm64.zip +7e53c5779c04f895f8282c0450ec4a63074d15a0e910e41006cfea133d0288af *libcxx-objects-v34.3.2-linux-armv7l.zip +92e2283c924ab523ffec3ea22513beaab6417f7fc3d570f57d51a1e1ceb7f510 *libcxx-objects-v34.3.2-linux-x64.zip +9bf3c6e8ad68f523fe086fada4148dd04e5bb0b9290d104873e66f2584a5cf50 *libcxx_headers.zip +34e4b44f9c5e08b557a2caed55456ce7690abab910196a783a2a47b58d2b9ac9 *libcxxabi_headers.zip +11f67635e6659f9188198e4086c51b89890b61a22f6c17c99eff35595ee8f51d *mksnapshot-v34.3.2-darwin-arm64.zip +c0add9ef4ac27c73fa043d04b4c9635fd3fd9f5c16d7a03e961864ba05252813 *mksnapshot-v34.3.2-darwin-x64.zip +6262adf86a340d8d28059937b01ef1117b93212e945fddbceea5c18d7e7c64f0 *mksnapshot-v34.3.2-linux-arm64-x64.zip +f7db8ebe91a1cc8d24ef6aad12949a18d8e4975ac296e3e5e9ecd88c9bccb143 *mksnapshot-v34.3.2-linux-armv7l-x64.zip +6642038e86bda362980ff1c8973a404e2b02efdd87de9e35b650fc1e743833da *mksnapshot-v34.3.2-linux-x64.zip +15883bf8e8cd737c3682d1e719d7cbac92f50b525681aac324dca876861dfc7d *mksnapshot-v34.3.2-mas-arm64.zip +4da23a950bfcc377ef21c37d496017ab4c36da03f3b41049ac114042c42608ce *mksnapshot-v34.3.2-mas-x64.zip +fab59573d3c2f9bdf31146a1896d24ac0c51f736aad86d2f3c7ecef13c05a7fd *mksnapshot-v34.3.2-win32-arm64-x64.zip +66f25e07c6f8d5d2009577a129440255a3baf63c929a5b60b2e77cd52e46105b *mksnapshot-v34.3.2-win32-ia32.zip +8168bfbf61882cfac80aed1e71e364e1c7f2fccd11eac298e6abade8b46894ea *mksnapshot-v34.3.2-win32-x64.zip diff --git a/build/checksums/nodejs.txt b/build/checksums/nodejs.txt index 9e53ad908323..d394605dda3a 100644 --- a/build/checksums/nodejs.txt +++ b/build/checksums/nodejs.txt @@ -1,7 +1,7 @@ -9e92ce1032455a9cc419fe71e908b27ae477799371b45a0844eedb02279922a4 node-v20.18.1-darwin-arm64.tar.gz -c5497dd17c8875b53712edaf99052f961013cedc203964583fc0cfc0aaf93581 node-v20.18.1-darwin-x64.tar.gz -73cd297378572e0bc9dfc187c5ec8cca8d43aee6a596c10ebea1ed5f9ec682b6 node-v20.18.1-linux-arm64.tar.gz -7b7c3315818e9fe57512737c2380fada14d8717ce88945fb6f7b8baadd3cfb92 node-v20.18.1-linux-armv7l.tar.gz -259e5a8bf2e15ecece65bd2a47153262eda71c0b2c9700d5e703ce4951572784 node-v20.18.1-linux-x64.tar.gz -9a52905b5d22b08b5b34e86b8d5358cd22c03fbd0fcb9dbb37c9bd82a8f18c17 win-arm64/node.exe -06c1dec1b428927d6ff01c8f5882f119ec13b61ac77483760aa7fba215c72cf5 win-x64/node.exe +1f15b7ed18a580af31cf32bc126572292d820f547bf55bf9cdce08041a24e1d9 node-v20.18.3-darwin-arm64.tar.gz +ba668f64df9239843fefcef095ee539f5ac5aa1b0fc15a71f1ecca16abedec7a node-v20.18.3-darwin-x64.tar.gz +93a9df19238adfaa289f4784041d03edaf2fdd89fbb247faffca2fe4a1000703 node-v20.18.3-linux-arm64.tar.gz +8a84eb34287db6a273066934d7195e429f57b91686b62fc19497210204a2b3de node-v20.18.3-linux-armv7l.tar.gz +9fc3952da39b20d1fcfdb777b198cc035485afbbb1004b4df93f35245d61151e node-v20.18.3-linux-x64.tar.gz +4258e333f4b95060681d61bffa762542a8068547d3dffebe57c575b38d380dda win-arm64/node.exe +528a9aa64888a2a3ba71c6aea89434dd5ab5cb3caa9f0f31345cf5facf685ab0 win-x64/node.exe diff --git a/build/checksums/vscode-sysroot.txt b/build/checksums/vscode-sysroot.txt index 0b5f38c60ce7..67182b078ed5 100644 --- a/build/checksums/vscode-sysroot.txt +++ b/build/checksums/vscode-sysroot.txt @@ -1,6 +1,3 @@ -68a17006021975ff271a1dd615f9db9eda7c25f2cc65e750c87980dc57a06c94 aarch64-linux-gnu-glibc-2.17.tar.gz 0de422a81683cf9e8cf875dbd1e0c27545ac3c775b2d53015daf3ca2b31d3f15 aarch64-linux-gnu-glibc-2.28.tar.gz -3ced48cb479f2cdba95aa649710fcb7778685551c745bbd76ac706c3c0ead9fb arm-rpi-linux-gnueabihf-glibc-2.17.tar.gz 7aea163f7fad8cc50000c86b5108be880121d35e2f55d016ef8c96bbe54129eb arm-rpi-linux-gnueabihf-glibc-2.28.tar.gz -5aae21115f1d284c3cdf32c83db15771b59bc80793f1423032abf5a823c0d658 x86_64-linux-gnu-glibc-2.17.tar.gz dbb927408393041664a020661f2641c9785741be3d29b050b9dac58980967784 x86_64-linux-gnu-glibc-2.28.tar.gz diff --git a/build/darwin/create-universal-app.js b/build/darwin/create-universal-app.js index 535d46eb1745..7d3f9164d5f1 100644 --- a/build/darwin/create-universal-app.js +++ b/build/darwin/create-universal-app.js @@ -27,6 +27,7 @@ async function main(buildDir) { const filesToSkip = [ '**/CodeResources', '**/Credits.rtf', + '**/policies/{*.mobileconfig,**/*.plist}', // TODO: Should we consider expanding this to other files in this area? '**/node_modules/@parcel/node-addon-api/nothing.target.mk' ]; diff --git a/build/darwin/create-universal-app.ts b/build/darwin/create-universal-app.ts index 9e013cdb10ca..7872eccc63e3 100644 --- a/build/darwin/create-universal-app.ts +++ b/build/darwin/create-universal-app.ts @@ -28,6 +28,7 @@ async function main(buildDir?: string) { const filesToSkip = [ '**/CodeResources', '**/Credits.rtf', + '**/policies/{*.mobileconfig,**/*.plist}', // TODO: Should we consider expanding this to other files in this area? '**/node_modules/@parcel/node-addon-api/nothing.target.mk' ]; diff --git a/build/filters.js b/build/filters.js index 1d18e0c942a1..ece41209baf8 100644 --- a/build/filters.js +++ b/build/filters.js @@ -57,6 +57,7 @@ module.exports.unicodeFilter = [ '!extensions/**/out/**', '!extensions/**/snippets/**', '!extensions/**/colorize-fixtures/**', + '!extensions/terminal-suggest/src/shell/fishBuiltinsCache.ts', '!src/vs/base/browser/dompurify/**', '!src/vs/workbench/services/keybinding/browser/keyboardLayouts/**', @@ -89,7 +90,8 @@ module.exports.indentationFilter = [ '!test/automation/out/**', '!test/monaco/out/**', '!test/smoke/out/**', - '!extensions/terminal-suggest/src/shell/zshBuiltinsCache.json', + '!extensions/terminal-suggest/src/shell/zshBuiltinsCache.ts', + '!extensions/terminal-suggest/src/shell/fishBuiltinsCache.ts', '!extensions/terminal-suggest/src/completions/upstream/**', '!extensions/typescript-language-features/test-workspace/**', '!extensions/typescript-language-features/resources/walkthroughs/**', @@ -194,6 +196,8 @@ module.exports.tsFormattingFilter = [ '!extensions/vscode-api-tests/testWorkspace2/**', '!extensions/**/*.test.ts', '!extensions/html-language-features/server/lib/jquery.d.ts', + '!extensions/terminal-suggest/src/shell/zshBuiltinsCache.ts', + '!extensions/terminal-suggest/src/shell/fishBuiltinsCache.ts', ]; module.exports.eslintFilter = [ diff --git a/build/gulpfile.compile.js b/build/gulpfile.compile.js index e40b05f8d39e..d9584c2b54fb 100644 --- a/build/gulpfile.compile.js +++ b/build/gulpfile.compile.js @@ -13,14 +13,15 @@ const task = require('./lib/task'); const compilation = require('./lib/compilation'); /** - * @param {boolean} disableMangle + * @param {boolean} _disableMangle */ -function makeCompileBuildTask(disableMangle) { +function makeCompileBuildTask(_disableMangle) { return task.series( util.rimraf('out-build'), date.writeISODate('out-build'), compilation.compileApiProposalNamesTask, - compilation.compileTask('src', 'out-build', true, { disableMangle }) + compilation.compileTask(isAMDBuild ? 'src2' : 'src', 'out-build', true, { disableMangle: true }), + optimize.optimizeLoaderTask('out-build', 'out-build', true) ); } diff --git a/build/gulpfile.reh.js b/build/gulpfile.reh.js index e0df76f1c8f2..44add792d14c 100644 --- a/build/gulpfile.reh.js +++ b/build/gulpfile.reh.js @@ -402,13 +402,7 @@ function packageTask(type, platform, arch, sourceFolderName, destinationFolderNa ); } - if (platform === 'linux' && process.env['VSCODE_NODE_GLIBC'] === '-glibc-2.17') { - result = es.merge(result, - gulp.src(`resources/server/bin/helpers/check-requirements-linux-legacy.sh`, { base: '.' }) - .pipe(rename(`bin/helpers/check-requirements.sh`)) - .pipe(util.setExecutableBit()) - ); - } else if (platform === 'linux' || platform === 'alpine') { + if (platform === 'linux' || platform === 'alpine') { result = es.merge(result, gulp.src(`resources/server/bin/helpers/check-requirements-linux.sh`, { base: '.' }) .pipe(rename(`bin/helpers/check-requirements.sh`)) diff --git a/build/gulpfile.vscode.js b/build/gulpfile.vscode.js index a63f693c95a6..0624f5f5a673 100644 --- a/build/gulpfile.vscode.js +++ b/build/gulpfile.vscode.js @@ -372,8 +372,9 @@ function packageTask(platform, arch, sourceFolderName, destinationFolderName, op const shortcut = gulp.src('resources/darwin/bin/code.sh') .pipe(replace('@@APPNAME@@', product.applicationName)) .pipe(rename('bin/code')); - - all = es.merge(all, shortcut); + const policyDest = gulp.src('.build/policies/darwin/**', { base: '.build/policies/darwin' }) + .pipe(rename(f => f.dirname = `policies/${f.dirname}`)); + all = es.merge(all, shortcut, policyDest); } let result = all diff --git a/build/lib/layersChecker.js b/build/lib/layersChecker.js index 5cf5c58402c7..2cab7b818328 100644 --- a/build/lib/layersChecker.js +++ b/build/lib/layersChecker.js @@ -81,7 +81,9 @@ const CORE_TYPES = [ 'ImportMeta', // webcrypto has been available since Node.js 19, but still live in dom.d.ts 'Crypto', - 'SubtleCrypto' + 'SubtleCrypto', + 'JsonWebKey', + 'MessageEvent', ]; // Types that are defined in a common layer but are known to be only // available in native environments should not be allowed in browser diff --git a/build/lib/layersChecker.ts b/build/lib/layersChecker.ts index 633773289282..685dd59cb36a 100644 --- a/build/lib/layersChecker.ts +++ b/build/lib/layersChecker.ts @@ -80,7 +80,9 @@ const CORE_TYPES = [ // webcrypto has been available since Node.js 19, but still live in dom.d.ts 'Crypto', - 'SubtleCrypto' + 'SubtleCrypto', + 'JsonWebKey', + 'MessageEvent', ]; // Types that are defined in a common layer but are known to be only diff --git a/build/lib/policies.js b/build/lib/policies.js index b76d9ffe00ae..71c727f944f7 100644 --- a/build/lib/policies.js +++ b/build/lib/policies.js @@ -27,7 +27,11 @@ function isNlsStringArray(value) { } var PolicyType; (function (PolicyType) { - PolicyType[PolicyType["StringEnum"] = 0] = "StringEnum"; + PolicyType["Boolean"] = "boolean"; + PolicyType["Number"] = "number"; + PolicyType["Object"] = "object"; + PolicyType["String"] = "string"; + PolicyType["StringEnum"] = "stringEnum"; })(PolicyType || (PolicyType = {})); function renderADMLString(prefix, moduleName, nlsString, translations) { let value; @@ -42,15 +46,28 @@ function renderADMLString(prefix, moduleName, nlsString, translations) { } return `${value}`; } +function renderProfileString(_prefix, moduleName, nlsString, translations) { + let value; + if (translations) { + const moduleTranslations = translations[moduleName]; + if (moduleTranslations) { + value = moduleTranslations[nlsString.nlsKey]; + } + } + if (!value) { + value = nlsString.value; + } + return value; +} class BasePolicy { - policyType; + type; name; category; minimumVersion; description; moduleName; - constructor(policyType, name, category, minimumVersion, description, moduleName) { - this.policyType = policyType; + constructor(type, name, category, minimumVersion, description, moduleName) { + this.type = type; this.name = name; this.category = category; this.minimumVersion = minimumVersion; @@ -80,17 +97,25 @@ class BasePolicy { renderADMLPresentation() { return `${this.renderADMLPresentationContents()}`; } + renderProfile() { + return [`${this.name}`, this.renderProfileValue()]; + } + renderProfileManifest(translations) { + return ` +${this.renderProfileManifestValue(translations)} +`; + } } class BooleanPolicy extends BasePolicy { static from(name, category, minimumVersion, description, moduleName, settingNode) { - const type = getStringProperty(settingNode, 'type'); + const type = getStringProperty(moduleName, settingNode, 'type'); if (type !== 'boolean') { return undefined; } return new BooleanPolicy(name, category, minimumVersion, description, moduleName); } constructor(name, category, minimumVersion, description, moduleName) { - super(PolicyType.StringEnum, name, category, minimumVersion, description, moduleName); + super(PolicyType.Boolean, name, category, minimumVersion, description, moduleName); } renderADMXElements() { return [ @@ -102,19 +127,39 @@ class BooleanPolicy extends BasePolicy { renderADMLPresentationContents() { return `${this.name}`; } + renderProfileValue() { + return ``; + } + renderProfileManifestValue(translations) { + return `pfm_default + +pfm_description +${renderProfileString(this.name, this.moduleName, this.description, translations)} +pfm_name +${this.name} +pfm_title +${this.name} +pfm_type +boolean`; + } +} +class ParseError extends Error { + constructor(message, moduleName, node) { + super(`${message}. ${moduleName}.ts:${node.startPosition.row + 1}`); + } } -class IntPolicy extends BasePolicy { +class NumberPolicy extends BasePolicy { defaultValue; static from(name, category, minimumVersion, description, moduleName, settingNode) { - const type = getStringProperty(settingNode, 'type'); + const type = getStringProperty(moduleName, settingNode, 'type'); if (type !== 'number') { return undefined; } - const defaultValue = getIntProperty(settingNode, 'default'); + const defaultValue = getNumberProperty(moduleName, settingNode, 'default'); if (typeof defaultValue === 'undefined') { - throw new Error(`Missing required 'default' property.`); + throw new ParseError(`Missing required 'default' property.`, moduleName, settingNode); } - return new IntPolicy(name, category, minimumVersion, description, moduleName, defaultValue); + return new NumberPolicy(name, category, minimumVersion, description, moduleName, defaultValue); } constructor(name, category, minimumVersion, description, moduleName, defaultValue) { super(PolicyType.StringEnum, name, category, minimumVersion, description, moduleName); @@ -129,17 +174,32 @@ class IntPolicy extends BasePolicy { renderADMLPresentationContents() { return `${this.name}`; } + renderProfileValue() { + return `${this.defaultValue}`; + } + renderProfileManifestValue(translations) { + return `pfm_default +${this.defaultValue} +pfm_description +${renderProfileString(this.name, this.moduleName, this.description, translations)} +pfm_name +${this.name} +pfm_title +${this.name} +pfm_type +integer`; + } } class StringPolicy extends BasePolicy { static from(name, category, minimumVersion, description, moduleName, settingNode) { - const type = getStringProperty(settingNode, 'type'); + const type = getStringProperty(moduleName, settingNode, 'type'); if (type !== 'string') { return undefined; } return new StringPolicy(name, category, minimumVersion, description, moduleName); } constructor(name, category, minimumVersion, description, moduleName) { - super(PolicyType.StringEnum, name, category, minimumVersion, description, moduleName); + super(PolicyType.String, name, category, minimumVersion, description, moduleName); } renderADMXElements() { return [``]; @@ -147,17 +207,32 @@ class StringPolicy extends BasePolicy { renderADMLPresentationContents() { return ``; } + renderProfileValue() { + return ``; + } + renderProfileManifestValue(translations) { + return `pfm_default + +pfm_description +${renderProfileString(this.name, this.moduleName, this.description, translations)} +pfm_name +${this.name} +pfm_title +${this.name} +pfm_type +string`; + } } class ObjectPolicy extends BasePolicy { static from(name, category, minimumVersion, description, moduleName, settingNode) { - const type = getStringProperty(settingNode, 'type'); + const type = getStringProperty(moduleName, settingNode, 'type'); if (type !== 'object' && type !== 'array') { return undefined; } return new ObjectPolicy(name, category, minimumVersion, description, moduleName); } constructor(name, category, minimumVersion, description, moduleName) { - super(PolicyType.StringEnum, name, category, minimumVersion, description, moduleName); + super(PolicyType.Object, name, category, minimumVersion, description, moduleName); } renderADMXElements() { return [``]; @@ -165,28 +240,44 @@ class ObjectPolicy extends BasePolicy { renderADMLPresentationContents() { return ``; } + renderProfileValue() { + return ``; + } + renderProfileManifestValue(translations) { + return `pfm_default + +pfm_description +${renderProfileString(this.name, this.moduleName, this.description, translations)} +pfm_name +${this.name} +pfm_title +${this.name} +pfm_type +string +`; + } } class StringEnumPolicy extends BasePolicy { enum_; enumDescriptions; static from(name, category, minimumVersion, description, moduleName, settingNode) { - const type = getStringProperty(settingNode, 'type'); + const type = getStringProperty(moduleName, settingNode, 'type'); if (type !== 'string') { return undefined; } - const enum_ = getStringArrayProperty(settingNode, 'enum'); + const enum_ = getStringArrayProperty(moduleName, settingNode, 'enum'); if (!enum_) { return undefined; } if (!isStringArray(enum_)) { - throw new Error(`Property 'enum' should not be localized.`); + throw new ParseError(`Property 'enum' should not be localized.`, moduleName, settingNode); } - const enumDescriptions = getStringArrayProperty(settingNode, 'enumDescriptions'); + const enumDescriptions = getStringArrayProperty(moduleName, settingNode, 'enumDescriptions'); if (!enumDescriptions) { - throw new Error(`Missing required 'enumDescriptions' property.`); + throw new ParseError(`Missing required 'enumDescriptions' property.`, moduleName, settingNode); } else if (!isNlsStringArray(enumDescriptions)) { - throw new Error(`Property 'enumDescriptions' should be localized.`); + throw new ParseError(`Property 'enumDescriptions' should be localized.`, moduleName, settingNode); } return new StringEnumPolicy(name, category, minimumVersion, description, moduleName, enum_, enumDescriptions); } @@ -211,8 +302,27 @@ class StringEnumPolicy extends BasePolicy { renderADMLPresentationContents() { return ``; } + renderProfileValue() { + return `${this.enum_[0]}`; + } + renderProfileManifestValue(translations) { + return `pfm_default +${this.enum_[0]} +pfm_description +${renderProfileString(this.name, this.moduleName, this.description, translations)} +pfm_name +${this.name} +pfm_title +${this.name} +pfm_type +string +pfm_range_list + + ${this.enum_.map(e => `${e}`).join('\n ')} +`; + } } -const IntQ = { +const NumberQ = { Q: `(number) @value`, value(matches) { const match = matches[0]; @@ -260,47 +370,52 @@ const StringArrayQ = { }); } }; -function getProperty(qtype, node, key) { +function getProperty(qtype, moduleName, node, key) { const query = new tree_sitter_1.default.Query(typescript, `( (pair key: [(property_identifier)(string)] @key value: ${qtype.Q} ) - (#eq? @key ${key}) + (#any-of? @key "${key}" "'${key}'") )`); - return qtype.value(query.matches(node)); + try { + return qtype.value(query.matches(node)); + } + catch (e) { + throw new ParseError(e.message, moduleName, node); + } } -function getIntProperty(node, key) { - return getProperty(IntQ, node, key); +function getNumberProperty(moduleName, node, key) { + return getProperty(NumberQ, moduleName, node, key); } -function getStringProperty(node, key) { - return getProperty(StringQ, node, key); +function getStringProperty(moduleName, node, key) { + return getProperty(StringQ, moduleName, node, key); } -function getStringArrayProperty(node, key) { - return getProperty(StringArrayQ, node, key); +function getStringArrayProperty(moduleName, node, key) { + return getProperty(StringArrayQ, moduleName, node, key); } // TODO: add more policy types const PolicyTypes = [ BooleanPolicy, - IntPolicy, + NumberPolicy, StringEnumPolicy, StringPolicy, ObjectPolicy ]; function getPolicy(moduleName, configurationNode, settingNode, policyNode, categories) { - const name = getStringProperty(policyNode, 'name'); + const name = getStringProperty(moduleName, policyNode, 'name'); if (!name) { - throw new Error(`Missing required 'name' property.`); + throw new Error(`Missing required 'name' property. ${moduleName}.ts:${policyNode.startPosition.row + 1}`); } else if (isNlsString(name)) { - throw new Error(`Property 'name' should be a literal string.`); + throw new Error(`Property 'name' should be a literal string. ${moduleName}.ts:${policyNode.startPosition.row + 1}`); } - const categoryName = getStringProperty(configurationNode, 'title'); + const categoryName = getStringProperty(moduleName, configurationNode, 'title'); if (!categoryName) { - throw new Error(`Missing required 'title' property.`); + throw new Error(`Missing required 'title' property. ${moduleName}.ts:${configurationNode.startPosition.row + 1}`); } else if (!isNlsString(categoryName)) { - throw new Error(`Property 'title' should be localized.`); + throw new Error(`Property 'title' should be localized. ${moduleName}.ts:${configurationNode.startPosition.row + 1}`); } const categoryKey = `${categoryName.nlsKey}:${categoryName.value}`; let category = categories.get(categoryKey); @@ -308,19 +423,19 @@ function getPolicy(moduleName, configurationNode, settingNode, policyNode, categ category = { moduleName, name: categoryName }; categories.set(categoryKey, category); } - const minimumVersion = getStringProperty(policyNode, 'minimumVersion'); + const minimumVersion = getStringProperty(moduleName, policyNode, 'minimumVersion'); if (!minimumVersion) { - throw new Error(`Missing required 'minimumVersion' property.`); + throw new Error(`Missing required 'minimumVersion' property. ${moduleName}.ts:${policyNode.startPosition.row + 1}`); } else if (isNlsString(minimumVersion)) { - throw new Error(`Property 'minimumVersion' should be a literal string.`); + throw new Error(`Property 'minimumVersion' should be a literal string. ${moduleName}.ts:${policyNode.startPosition.row + 1}`); } - const description = getStringProperty(settingNode, 'description'); + const description = getStringProperty(moduleName, policyNode, 'description') ?? getStringProperty(moduleName, settingNode, 'description'); if (!description) { - throw new Error(`Missing required 'description' property.`); + throw new Error(`Missing required 'description' property. ${moduleName}.ts:${settingNode.startPosition.row + 1}`); } if (!isNlsString(description)) { - throw new Error(`Property 'description' should be localized.`); + throw new Error(`Property 'description' should be localized. ${moduleName}.ts:${settingNode.startPosition.row + 1}`); } let result; for (const policyType of PolicyTypes) { @@ -329,7 +444,7 @@ function getPolicy(moduleName, configurationNode, settingNode, policyNode, categ } } if (!result) { - throw new Error(`Failed to parse policy '${name}'.`); + throw new Error(`Failed to parse policy '${name}'. ${moduleName}.ts:${settingNode.startPosition.row + 1}`); } return result; } @@ -339,11 +454,11 @@ function getPolicies(moduleName, node) { (call_expression function: (member_expression property: (property_identifier) @registerConfigurationFn) (#eq? @registerConfigurationFn registerConfiguration) arguments: (arguments (object (pair - key: [(property_identifier)(string)] @propertiesKey (#eq? @propertiesKey properties) + key: [(property_identifier)(string)] @propertiesKey (#any-of? @propertiesKey "properties" "'properties'") value: (object (pair key: [(property_identifier)(string)(computed_property_name)] value: (object (pair - key: [(property_identifier)(string)] @policyKey (#eq? @policyKey policy) + key: [(property_identifier)(string)] @policyKey (#any-of? @policyKey "policy" "'policy'") value: (object) @policy )) @setting )) @@ -411,6 +526,186 @@ function renderADML(appName, versions, categories, policies, translations) { `; } +function renderProfileManifest(appName, bundleIdentifier, _versions, _categories, policies, translations) { + const requiredPayloadFields = ` + + pfm_default + Configure ${appName} + pfm_name + PayloadDescription + pfm_title + Payload Description + pfm_type + string + + + pfm_default + ${appName} + pfm_name + PayloadDisplayName + pfm_require + always + pfm_title + Payload Display Name + pfm_type + string + + + pfm_default + ${bundleIdentifier} + pfm_name + PayloadIdentifier + pfm_require + always + pfm_title + Payload Identifier + pfm_type + string + + + pfm_default + ${bundleIdentifier} + pfm_name + PayloadType + pfm_require + always + pfm_title + Payload Type + pfm_type + string + + + pfm_default + + pfm_name + PayloadUUID + pfm_require + always + pfm_title + Payload UUID + pfm_type + string + + + pfm_default + 1 + pfm_name + PayloadVersion + pfm_range_list + + 1 + + pfm_require + always + pfm_title + Payload Version + pfm_type + integer + + + pfm_default + Microsoft + pfm_name + PayloadOrganization + pfm_title + Payload Organization + pfm_type + string + `; + const profileManifestSubkeys = policies.map(policy => { + return policy.renderProfileManifest(translations); + }).join(''); + return ` + + + + pfm_app_url + https://code.visualstudio.com/ + pfm_description + ${appName} Managed Settings + pfm_documentation_url + https://code.visualstudio.com/docs/setup/enterprise + pfm_domain + ${bundleIdentifier} + pfm_format_version + 1 + pfm_interaction + combined + pfm_last_modified + ${new Date().toISOString().replace(/\.\d+Z$/, 'Z')} + pfm_platforms + + macOS + + pfm_subkeys + + ${requiredPayloadFields} + ${profileManifestSubkeys} + + pfm_title + ${appName} + pfm_unique + + pfm_version + 1 + +`; +} +function renderMacOSPolicy(policies, translations) { + const appName = product.nameLong; + const bundleIdentifier = product.darwinBundleIdentifier; + const payloadUUID = product.darwinProfilePayloadUUID; + const UUID = product.darwinProfileUUID; + const versions = [...new Set(policies.map(p => p.minimumVersion)).values()].sort(); + const categories = [...new Set(policies.map(p => p.category))]; + const policyEntries = policies.map(policy => policy.renderProfile()) + .flat() + .map(entry => `\t\t\t\t${entry}`) + .join('\n'); + return { + profile: ` + + + + PayloadContent + + + PayloadDisplayName + ${appName} + PayloadIdentifier + ${bundleIdentifier}.${UUID} + PayloadType + ${bundleIdentifier} + PayloadUUID + ${UUID} + PayloadVersion + 1 +${policyEntries} + + + PayloadDescription + This profile manages ${appName}. For more information see https://code.visualstudio.com/docs/setup/enterprise + PayloadDisplayName + ${appName} + PayloadIdentifier + ${bundleIdentifier} + PayloadOrganization + Microsoft + PayloadType + Configuration + PayloadUUID + ${payloadUUID} + PayloadVersion + 1 + TargetDeviceType + 5 + +`, + manifests: [{ languageId: 'en-us', contents: renderProfileManifest(appName, bundleIdentifier, versions, categories, policies) }, + ...translations.map(({ languageId, languageTranslations }) => ({ languageId, contents: renderProfileManifest(appName, bundleIdentifier, versions, categories, policies, languageTranslations) })) + ] + }; +} function renderGP(policies, translations) { const appName = product.nameLong; const regKey = product.win32RegValueName; @@ -526,10 +821,9 @@ async function getTranslations() { return await Promise.all(languageIds.map(languageId => getNLS(extensionGalleryServiceUrl, resourceUrlTemplate, languageId, version) .then(languageTranslations => ({ languageId, languageTranslations })))); } -async function main() { - const [policies, translations] = await Promise.all([parsePolicies(), getTranslations()]); - const { admx, adml } = await renderGP(policies, translations); +async function windowsMain(policies, translations) { const root = '.build/policies/win32'; + const { admx, adml } = await renderGP(policies, translations); await fs_1.promises.rm(root, { recursive: true, force: true }); await fs_1.promises.mkdir(root, { recursive: true }); await fs_1.promises.writeFile(path_1.default.join(root, `${product.win32RegValueName}.admx`), admx.replace(/\r?\n/g, '\n')); @@ -539,9 +833,44 @@ async function main() { await fs_1.promises.writeFile(path_1.default.join(languagePath, `${product.win32RegValueName}.adml`), contents.replace(/\r?\n/g, '\n')); } } +async function darwinMain(policies, translations) { + const bundleIdentifier = product.darwinBundleIdentifier; + if (!bundleIdentifier || !product.darwinProfilePayloadUUID || !product.darwinProfileUUID) { + throw new Error(`Missing required product information.`); + } + const root = '.build/policies/darwin'; + const { profile, manifests } = await renderMacOSPolicy(policies, translations); + await fs_1.promises.rm(root, { recursive: true, force: true }); + await fs_1.promises.mkdir(root, { recursive: true }); + await fs_1.promises.writeFile(path_1.default.join(root, `${bundleIdentifier}.mobileconfig`), profile.replace(/\r?\n/g, '\n')); + for (const { languageId, contents } of manifests) { + const languagePath = path_1.default.join(root, languageId === 'en-us' ? 'en-us' : Languages[languageId]); + await fs_1.promises.mkdir(languagePath, { recursive: true }); + await fs_1.promises.writeFile(path_1.default.join(languagePath, `${bundleIdentifier}.plist`), contents.replace(/\r?\n/g, '\n')); + } +} +async function main() { + const [policies, translations] = await Promise.all([parsePolicies(), getTranslations()]); + const platform = process.argv[2]; + if (platform === 'darwin') { + await darwinMain(policies, translations); + } + else if (platform === 'win32') { + await windowsMain(policies, translations); + } + else { + console.error(`Usage: node build/lib/policies `); + process.exit(1); + } +} if (require.main === module) { main().catch(err => { - console.error(err); + if (err instanceof ParseError) { + console.error(`Parse Error:`, err.message); + } + else { + console.error(err); + } process.exit(1); }); } diff --git a/build/lib/policies.ts b/build/lib/policies.ts index 2488920ce268..6df9ef84c02a 100644 --- a/build/lib/policies.ts +++ b/build/lib/policies.ts @@ -33,15 +33,24 @@ interface Category { } enum PolicyType { - StringEnum + Boolean = 'boolean', + Number = 'number', + Object = 'object', + String = 'string', + StringEnum = 'stringEnum', } interface Policy { + readonly name: string; + readonly type: PolicyType; readonly category: Category; readonly minimumVersion: string; renderADMX(regKey: string): string[]; renderADMLStrings(translations?: LanguageTranslations): string[]; renderADMLPresentation(): string; + renderProfile(): string[]; + // https://github.com/ProfileManifests/ProfileManifests/wiki/Manifest-Format + renderProfileManifest(translations?: LanguageTranslations): string; } function renderADMLString(prefix: string, moduleName: string, nlsString: NlsString, translations?: LanguageTranslations): string { @@ -62,10 +71,28 @@ function renderADMLString(prefix: string, moduleName: string, nlsString: NlsStri return `${value}`; } +function renderProfileString(_prefix: string, moduleName: string, nlsString: NlsString, translations?: LanguageTranslations): string { + let value: string | undefined; + + if (translations) { + const moduleTranslations = translations[moduleName]; + + if (moduleTranslations) { + value = moduleTranslations[nlsString.nlsKey]; + } + } + + if (!value) { + value = nlsString.value; + } + + return value; +} + abstract class BasePolicy implements Policy { constructor( - protected policyType: PolicyType, - protected name: string, + readonly type: PolicyType, + readonly name: string, readonly category: Category, readonly minimumVersion: string, protected description: NlsString, @@ -102,6 +129,19 @@ abstract class BasePolicy implements Policy { } protected abstract renderADMLPresentationContents(): string; + + renderProfile() { + return [`${this.name}`, this.renderProfileValue()]; + } + + renderProfileManifest(translations?: LanguageTranslations): string { + return ` +${this.renderProfileManifestValue(translations)} +`; + } + + abstract renderProfileValue(): string; + abstract renderProfileManifestValue(translations?: LanguageTranslations): string; } class BooleanPolicy extends BasePolicy { @@ -114,7 +154,7 @@ class BooleanPolicy extends BasePolicy { moduleName: string, settingNode: Parser.SyntaxNode ): BooleanPolicy | undefined { - const type = getStringProperty(settingNode, 'type'); + const type = getStringProperty(moduleName, settingNode, 'type'); if (type !== 'boolean') { return undefined; @@ -130,7 +170,7 @@ class BooleanPolicy extends BasePolicy { description: NlsString, moduleName: string, ) { - super(PolicyType.StringEnum, name, category, minimumVersion, description, moduleName); + super(PolicyType.Boolean, name, category, minimumVersion, description, moduleName); } protected renderADMXElements(): string[] { @@ -144,9 +184,32 @@ class BooleanPolicy extends BasePolicy { renderADMLPresentationContents() { return `${this.name}`; } + + renderProfileValue(): string { + return ``; + } + + renderProfileManifestValue(translations?: LanguageTranslations): string { + return `pfm_default + +pfm_description +${renderProfileString(this.name, this.moduleName, this.description, translations)} +pfm_name +${this.name} +pfm_title +${this.name} +pfm_type +boolean`; + } } -class IntPolicy extends BasePolicy { +class ParseError extends Error { + constructor(message: string, moduleName: string, node: Parser.SyntaxNode) { + super(`${message}. ${moduleName}.ts:${node.startPosition.row + 1}`); + } +} + +class NumberPolicy extends BasePolicy { static from( name: string, @@ -155,20 +218,20 @@ class IntPolicy extends BasePolicy { description: NlsString, moduleName: string, settingNode: Parser.SyntaxNode - ): IntPolicy | undefined { - const type = getStringProperty(settingNode, 'type'); + ): NumberPolicy | undefined { + const type = getStringProperty(moduleName, settingNode, 'type'); if (type !== 'number') { return undefined; } - const defaultValue = getIntProperty(settingNode, 'default'); + const defaultValue = getNumberProperty(moduleName, settingNode, 'default'); if (typeof defaultValue === 'undefined') { - throw new Error(`Missing required 'default' property.`); + throw new ParseError(`Missing required 'default' property.`, moduleName, settingNode); } - return new IntPolicy(name, category, minimumVersion, description, moduleName, defaultValue); + return new NumberPolicy(name, category, minimumVersion, description, moduleName, defaultValue); } private constructor( @@ -192,6 +255,23 @@ class IntPolicy extends BasePolicy { renderADMLPresentationContents() { return `${this.name}`; } + + renderProfileValue() { + return `${this.defaultValue}`; + } + + renderProfileManifestValue(translations?: LanguageTranslations) { + return `pfm_default +${this.defaultValue} +pfm_description +${renderProfileString(this.name, this.moduleName, this.description, translations)} +pfm_name +${this.name} +pfm_title +${this.name} +pfm_type +integer`; + } } class StringPolicy extends BasePolicy { @@ -204,7 +284,7 @@ class StringPolicy extends BasePolicy { moduleName: string, settingNode: Parser.SyntaxNode ): StringPolicy | undefined { - const type = getStringProperty(settingNode, 'type'); + const type = getStringProperty(moduleName, settingNode, 'type'); if (type !== 'string') { return undefined; @@ -220,7 +300,7 @@ class StringPolicy extends BasePolicy { description: NlsString, moduleName: string, ) { - super(PolicyType.StringEnum, name, category, minimumVersion, description, moduleName); + super(PolicyType.String, name, category, minimumVersion, description, moduleName); } protected renderADMXElements(): string[] { @@ -230,6 +310,23 @@ class StringPolicy extends BasePolicy { renderADMLPresentationContents() { return ``; } + + renderProfileValue(): string { + return ``; + } + + renderProfileManifestValue(translations?: LanguageTranslations): string { + return `pfm_default + +pfm_description +${renderProfileString(this.name, this.moduleName, this.description, translations)} +pfm_name +${this.name} +pfm_title +${this.name} +pfm_type +string`; + } } class ObjectPolicy extends BasePolicy { @@ -242,7 +339,7 @@ class ObjectPolicy extends BasePolicy { moduleName: string, settingNode: Parser.SyntaxNode ): ObjectPolicy | undefined { - const type = getStringProperty(settingNode, 'type'); + const type = getStringProperty(moduleName, settingNode, 'type'); if (type !== 'object' && type !== 'array') { return undefined; @@ -258,7 +355,7 @@ class ObjectPolicy extends BasePolicy { description: NlsString, moduleName: string, ) { - super(PolicyType.StringEnum, name, category, minimumVersion, description, moduleName); + super(PolicyType.Object, name, category, minimumVersion, description, moduleName); } protected renderADMXElements(): string[] { @@ -268,6 +365,24 @@ class ObjectPolicy extends BasePolicy { renderADMLPresentationContents() { return ``; } + + renderProfileValue(): string { + return ``; + } + + renderProfileManifestValue(translations?: LanguageTranslations): string { + return `pfm_default + +pfm_description +${renderProfileString(this.name, this.moduleName, this.description, translations)} +pfm_name +${this.name} +pfm_title +${this.name} +pfm_type +string +`; + } } class StringEnumPolicy extends BasePolicy { @@ -280,28 +395,28 @@ class StringEnumPolicy extends BasePolicy { moduleName: string, settingNode: Parser.SyntaxNode ): StringEnumPolicy | undefined { - const type = getStringProperty(settingNode, 'type'); + const type = getStringProperty(moduleName, settingNode, 'type'); if (type !== 'string') { return undefined; } - const enum_ = getStringArrayProperty(settingNode, 'enum'); + const enum_ = getStringArrayProperty(moduleName, settingNode, 'enum'); if (!enum_) { return undefined; } if (!isStringArray(enum_)) { - throw new Error(`Property 'enum' should not be localized.`); + throw new ParseError(`Property 'enum' should not be localized.`, moduleName, settingNode); } - const enumDescriptions = getStringArrayProperty(settingNode, 'enumDescriptions'); + const enumDescriptions = getStringArrayProperty(moduleName, settingNode, 'enumDescriptions'); if (!enumDescriptions) { - throw new Error(`Missing required 'enumDescriptions' property.`); + throw new ParseError(`Missing required 'enumDescriptions' property.`, moduleName, settingNode); } else if (!isNlsStringArray(enumDescriptions)) { - throw new Error(`Property 'enumDescriptions' should be localized.`); + throw new ParseError(`Property 'enumDescriptions' should be localized.`, moduleName, settingNode); } return new StringEnumPolicy(name, category, minimumVersion, description, moduleName, enum_, enumDescriptions); @@ -337,6 +452,27 @@ class StringEnumPolicy extends BasePolicy { renderADMLPresentationContents() { return ``; } + + renderProfileValue() { + return `${this.enum_[0]}`; + } + + renderProfileManifestValue(translations?: LanguageTranslations): string { + return `pfm_default +${this.enum_[0]} +pfm_description +${renderProfileString(this.name, this.moduleName, this.description, translations)} +pfm_name +${this.name} +pfm_title +${this.name} +pfm_type +string +pfm_range_list + + ${this.enum_.map(e => `${e}`).join('\n ')} +`; + } } interface QType { @@ -344,7 +480,7 @@ interface QType { value(matches: Parser.QueryMatch[]): T | undefined; } -const IntQ: QType = { +const NumberQ: QType = { Q: `(number) @value`, value(matches: Parser.QueryMatch[]): number | undefined { @@ -407,7 +543,7 @@ const StringArrayQ: QType<(string | NlsString)[]> = { } }; -function getProperty(qtype: QType, node: Parser.SyntaxNode, key: string): T | undefined { +function getProperty(qtype: QType, moduleName: string, node: Parser.SyntaxNode, key: string): T | undefined { const query = new Parser.Query( typescript, `( @@ -415,29 +551,33 @@ function getProperty(qtype: QType, node: Parser.SyntaxNode, key: string): key: [(property_identifier)(string)] @key value: ${qtype.Q} ) - (#eq? @key ${key}) + (#any-of? @key "${key}" "'${key}'") )` ); - return qtype.value(query.matches(node)); + try { + return qtype.value(query.matches(node)); + } catch (e) { + throw new ParseError(e.message, moduleName, node); + } } -function getIntProperty(node: Parser.SyntaxNode, key: string): number | undefined { - return getProperty(IntQ, node, key); +function getNumberProperty(moduleName: string, node: Parser.SyntaxNode, key: string): number | undefined { + return getProperty(NumberQ, moduleName, node, key); } -function getStringProperty(node: Parser.SyntaxNode, key: string): string | NlsString | undefined { - return getProperty(StringQ, node, key); +function getStringProperty(moduleName: string, node: Parser.SyntaxNode, key: string): string | NlsString | undefined { + return getProperty(StringQ, moduleName, node, key); } -function getStringArrayProperty(node: Parser.SyntaxNode, key: string): (string | NlsString)[] | undefined { - return getProperty(StringArrayQ, node, key); +function getStringArrayProperty(moduleName: string, node: Parser.SyntaxNode, key: string): (string | NlsString)[] | undefined { + return getProperty(StringArrayQ, moduleName, node, key); } // TODO: add more policy types const PolicyTypes = [ BooleanPolicy, - IntPolicy, + NumberPolicy, StringEnumPolicy, StringPolicy, ObjectPolicy @@ -450,20 +590,20 @@ function getPolicy( policyNode: Parser.SyntaxNode, categories: Map ): Policy { - const name = getStringProperty(policyNode, 'name'); + const name = getStringProperty(moduleName, policyNode, 'name'); if (!name) { - throw new Error(`Missing required 'name' property.`); + throw new Error(`Missing required 'name' property. ${moduleName}.ts:${policyNode.startPosition.row + 1}`); } else if (isNlsString(name)) { - throw new Error(`Property 'name' should be a literal string.`); + throw new Error(`Property 'name' should be a literal string. ${moduleName}.ts:${policyNode.startPosition.row + 1}`); } - const categoryName = getStringProperty(configurationNode, 'title'); + const categoryName = getStringProperty(moduleName, configurationNode, 'title'); if (!categoryName) { - throw new Error(`Missing required 'title' property.`); + throw new Error(`Missing required 'title' property. ${moduleName}.ts:${configurationNode.startPosition.row + 1}`); } else if (!isNlsString(categoryName)) { - throw new Error(`Property 'title' should be localized.`); + throw new Error(`Property 'title' should be localized. ${moduleName}.ts:${configurationNode.startPosition.row + 1}`); } const categoryKey = `${categoryName.nlsKey}:${categoryName.value}`; @@ -474,20 +614,20 @@ function getPolicy( categories.set(categoryKey, category); } - const minimumVersion = getStringProperty(policyNode, 'minimumVersion'); + const minimumVersion = getStringProperty(moduleName, policyNode, 'minimumVersion'); if (!minimumVersion) { - throw new Error(`Missing required 'minimumVersion' property.`); + throw new Error(`Missing required 'minimumVersion' property. ${moduleName}.ts:${policyNode.startPosition.row + 1}`); } else if (isNlsString(minimumVersion)) { - throw new Error(`Property 'minimumVersion' should be a literal string.`); + throw new Error(`Property 'minimumVersion' should be a literal string. ${moduleName}.ts:${policyNode.startPosition.row + 1}`); } - const description = getStringProperty(settingNode, 'description'); + const description = getStringProperty(moduleName, policyNode, 'description') ?? getStringProperty(moduleName, settingNode, 'description'); if (!description) { - throw new Error(`Missing required 'description' property.`); + throw new Error(`Missing required 'description' property. ${moduleName}.ts:${settingNode.startPosition.row + 1}`); } if (!isNlsString(description)) { - throw new Error(`Property 'description' should be localized.`); + throw new Error(`Property 'description' should be localized. ${moduleName}.ts:${settingNode.startPosition.row + 1}`); } let result: Policy | undefined; @@ -499,7 +639,7 @@ function getPolicy( } if (!result) { - throw new Error(`Failed to parse policy '${name}'.`); + throw new Error(`Failed to parse policy '${name}'. ${moduleName}.ts:${settingNode.startPosition.row + 1}`); } return result; @@ -511,11 +651,11 @@ function getPolicies(moduleName: string, node: Parser.SyntaxNode): Policy[] { (call_expression function: (member_expression property: (property_identifier) @registerConfigurationFn) (#eq? @registerConfigurationFn registerConfiguration) arguments: (arguments (object (pair - key: [(property_identifier)(string)] @propertiesKey (#eq? @propertiesKey properties) + key: [(property_identifier)(string)] @propertiesKey (#any-of? @propertiesKey "properties" "'properties'") value: (object (pair key: [(property_identifier)(string)(computed_property_name)] value: (object (pair - key: [(property_identifier)(string)] @policyKey (#eq? @policyKey policy) + key: [(property_identifier)(string)] @policyKey (#any-of? @policyKey "policy" "'policy'") value: (object) @policy )) @setting )) @@ -590,6 +730,197 @@ function renderADML(appName: string, versions: string[], categories: Category[], `; } +function renderProfileManifest(appName: string, bundleIdentifier: string, _versions: string[], _categories: Category[], policies: Policy[], translations?: LanguageTranslations) { + + const requiredPayloadFields = ` + + pfm_default + Configure ${appName} + pfm_name + PayloadDescription + pfm_title + Payload Description + pfm_type + string + + + pfm_default + ${appName} + pfm_name + PayloadDisplayName + pfm_require + always + pfm_title + Payload Display Name + pfm_type + string + + + pfm_default + ${bundleIdentifier} + pfm_name + PayloadIdentifier + pfm_require + always + pfm_title + Payload Identifier + pfm_type + string + + + pfm_default + ${bundleIdentifier} + pfm_name + PayloadType + pfm_require + always + pfm_title + Payload Type + pfm_type + string + + + pfm_default + + pfm_name + PayloadUUID + pfm_require + always + pfm_title + Payload UUID + pfm_type + string + + + pfm_default + 1 + pfm_name + PayloadVersion + pfm_range_list + + 1 + + pfm_require + always + pfm_title + Payload Version + pfm_type + integer + + + pfm_default + Microsoft + pfm_name + PayloadOrganization + pfm_title + Payload Organization + pfm_type + string + `; + + const profileManifestSubkeys = policies.map(policy => { + return policy.renderProfileManifest(translations); + }).join(''); + + return ` + + + + pfm_app_url + https://code.visualstudio.com/ + pfm_description + ${appName} Managed Settings + pfm_documentation_url + https://code.visualstudio.com/docs/setup/enterprise + pfm_domain + ${bundleIdentifier} + pfm_format_version + 1 + pfm_interaction + combined + pfm_last_modified + ${new Date().toISOString().replace(/\.\d+Z$/, 'Z')} + pfm_platforms + + macOS + + pfm_subkeys + + ${requiredPayloadFields} + ${profileManifestSubkeys} + + pfm_title + ${appName} + pfm_unique + + pfm_version + 1 + +`; +} + +function renderMacOSPolicy(policies: Policy[], translations: Translations) { + const appName = product.nameLong; + const bundleIdentifier = product.darwinBundleIdentifier; + const payloadUUID = product.darwinProfilePayloadUUID; + const UUID = product.darwinProfileUUID; + + const versions = [...new Set(policies.map(p => p.minimumVersion)).values()].sort(); + const categories = [...new Set(policies.map(p => p.category))]; + + const policyEntries = + policies.map(policy => policy.renderProfile()) + .flat() + .map(entry => `\t\t\t\t${entry}`) + .join('\n'); + + + return { + profile: ` + + + + PayloadContent + + + PayloadDisplayName + ${appName} + PayloadIdentifier + ${bundleIdentifier}.${UUID} + PayloadType + ${bundleIdentifier} + PayloadUUID + ${UUID} + PayloadVersion + 1 +${policyEntries} + + + PayloadDescription + This profile manages ${appName}. For more information see https://code.visualstudio.com/docs/setup/enterprise + PayloadDisplayName + ${appName} + PayloadIdentifier + ${bundleIdentifier} + PayloadOrganization + Microsoft + PayloadType + Configuration + PayloadUUID + ${payloadUUID} + PayloadVersion + 1 + TargetDeviceType + 5 + +`, + manifests: [{ languageId: 'en-us', contents: renderProfileManifest(appName, bundleIdentifier, versions, categories, policies) }, + ...translations.map(({ languageId, languageTranslations }) => + ({ languageId, contents: renderProfileManifest(appName, bundleIdentifier, versions, categories, policies, languageTranslations) })) + ] + }; +} + function renderGP(policies: Policy[], translations: Translations) { const appName = product.nameLong; const regKey = product.win32RegValueName; @@ -735,11 +1066,10 @@ async function getTranslations(): Promise { )); } -async function main() { - const [policies, translations] = await Promise.all([parsePolicies(), getTranslations()]); +async function windowsMain(policies: Policy[], translations: Translations) { + const root = '.build/policies/win32'; const { admx, adml } = await renderGP(policies, translations); - const root = '.build/policies/win32'; await fs.rm(root, { recursive: true, force: true }); await fs.mkdir(root, { recursive: true }); @@ -752,9 +1082,46 @@ async function main() { } } +async function darwinMain(policies: Policy[], translations: Translations) { + const bundleIdentifier = product.darwinBundleIdentifier; + if (!bundleIdentifier || !product.darwinProfilePayloadUUID || !product.darwinProfileUUID) { + throw new Error(`Missing required product information.`); + } + const root = '.build/policies/darwin'; + const { profile, manifests } = await renderMacOSPolicy(policies, translations); + + await fs.rm(root, { recursive: true, force: true }); + await fs.mkdir(root, { recursive: true }); + await fs.writeFile(path.join(root, `${bundleIdentifier}.mobileconfig`), profile.replace(/\r?\n/g, '\n')); + + for (const { languageId, contents } of manifests) { + const languagePath = path.join(root, languageId === 'en-us' ? 'en-us' : Languages[languageId as keyof typeof Languages]); + await fs.mkdir(languagePath, { recursive: true }); + await fs.writeFile(path.join(languagePath, `${bundleIdentifier}.plist`), contents.replace(/\r?\n/g, '\n')); + } +} + +async function main() { + const [policies, translations] = await Promise.all([parsePolicies(), getTranslations()]); + const platform = process.argv[2]; + + if (platform === 'darwin') { + await darwinMain(policies, translations); + } else if (platform === 'win32') { + await windowsMain(policies, translations); + } else { + console.error(`Usage: node build/lib/policies `); + process.exit(1); + } +} + if (require.main === module) { main().catch(err => { - console.error(err); + if (err instanceof ParseError) { + console.error(`Parse Error:`, err.message); + } else { + console.error(err); + } process.exit(1); }); } diff --git a/build/lib/propertyInitOrderChecker.js b/build/lib/propertyInitOrderChecker.js new file mode 100644 index 000000000000..dbca887bc227 --- /dev/null +++ b/build/lib/propertyInitOrderChecker.js @@ -0,0 +1,367 @@ +"use strict"; +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || (function () { + var ownKeys = function(o) { + ownKeys = Object.getOwnPropertyNames || function (o) { + var ar = []; + for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k; + return ar; + }; + return ownKeys(o); + }; + return function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]); + __setModuleDefault(result, mod); + return result; + }; +})(); +Object.defineProperty(exports, "__esModule", { value: true }); +exports.EntryKind = void 0; +const ts = __importStar(require("typescript")); +const path = __importStar(require("path")); +const fs = __importStar(require("fs")); +const TS_CONFIG_PATH = path.join(__dirname, '../../', 'src', 'tsconfig.json'); +// +// ############################################################################################# +// +// A custom typescript checker that ensure constructor properties are NOT used to initialize +// defined properties. This is needed for the times when `useDefineForClassFields` is gone. +// +// see https://github.com/microsoft/vscode/issues/243049, https://github.com/microsoft/vscode/issues/186726, +// https://github.com/microsoft/vscode/pull/241544 +// +// ############################################################################################# +// +const ignored = new Set([ + 'vs/base/common/arrays.ts', + 'vs/platform/extensionManagement/common/extensionsScannerService.ts', + 'vs/platform/configuration/common/configurations.ts', + 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/tokenizer.ts', + 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/bracketPairsTree.ts', + 'vs/editor/common/model/textModelTokens.ts', + 'vs/editor/common/model/tokenizationTextModelPart.ts', + 'vs/editor/common/core/textEdit.ts', + 'vs/workbench/contrib/debug/common/debugStorage.ts', + 'vs/workbench/contrib/debug/common/debugModel.ts', + 'vs/workbench/api/common/extHostCommands.ts', + 'vs/editor/browser/view/viewLayer.ts', + 'vs/editor/browser/controller/editContext/textArea/textAreaEditContextInput.ts', + 'vs/platform/accessibilitySignal/browser/accessibilitySignalService.ts', + 'vs/editor/browser/widget/diffEditor/utils.ts', + 'vs/editor/browser/observableCodeEditor.ts', + 'vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/diffEditorViewZones.ts', + 'vs/editor/browser/widget/diffEditor/diffEditorOptions.ts', + 'vs/editor/browser/widget/diffEditor/components/diffEditorEditors.ts', + 'vs/editor/browser/widget/diffEditor/features/movedBlocksLinesFeature.ts', + 'vs/editor/browser/widget/diffEditor/components/diffEditorSash.ts', + 'vs/editor/browser/widget/diffEditor/utils/editorGutter.ts', + 'vs/editor/browser/widget/diffEditor/features/gutterFeature.ts', + 'vs/editor/browser/widget/diffEditor/features/revertButtonsFeature.ts', + 'vs/editor/browser/widget/diffEditor/diffEditorWidget.ts', + 'vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsSource.ts', + 'vs/editor/contrib/inlineCompletions/browser/model/suggestWidgetAdapter.ts', + 'vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts', + 'vs/editor/contrib/inlineCompletions/browser/hintsWidget/inlineCompletionsHintsWidget.ts', + 'vs/editor/contrib/inlayHints/browser/inlayHintsController.ts', + 'vs/editor/contrib/inlineCompletions/browser/model/changeRecorder.ts', + 'vs/editor/contrib/inlineCompletions/browser/view/ghostText/ghostTextView.ts', + 'vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditWithChanges.ts', + 'vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/components/gutterIndicatorView.ts', + 'vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsWordReplacementView.ts', + 'vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsLineReplacementView.ts', + 'vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsSideBySideView.ts', + 'vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/originalEditorInlineDiffView.ts', + 'vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsView.ts', + 'vs/editor/contrib/inlineCompletions/browser/view/inlineCompletionsView.ts', + 'vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.ts', + 'vs/editor/contrib/inlineCompletions/browser/inlineCompletionsAccessibleView.ts', + 'vs/editor/contrib/placeholderText/browser/placeholderTextContribution.ts', + 'vs/editor/contrib/unicodeHighlighter/browser/unicodeHighlighter.ts', + 'vs/workbench/contrib/chat/common/promptSyntax/parsers/basePromptParser.ts', + 'vs/workbench/contrib/files/browser/views/openEditorsView.ts', + 'vs/workbench/contrib/chat/browser/chatContentParts/chatAttachmentsContentPart.ts', + 'vs/workbench/contrib/chat/browser/contrib/chatImplicitContext.ts', + 'vs/workbench/contrib/chat/browser/chatInputPart.ts', + 'vs/workbench/contrib/mergeEditor/browser/model/modifiedBaseRange.ts', + 'vs/workbench/contrib/mergeEditor/browser/model/diffComputer.ts', + 'vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel.ts', + 'vs/workbench/contrib/mergeEditor/browser/view/editorGutter.ts', + 'vs/workbench/contrib/mergeEditor/browser/view/editors/codeEditorView.ts', + 'vs/workbench/contrib/mergeEditor/browser/view/editors/inputCodeEditorView.ts', + 'vs/workbench/contrib/mergeEditor/browser/view/viewModel.ts', + 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInputModel.ts', + 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts', + 'vs/editor/browser/widget/multiDiffEditor/multiDiffEditorViewModel.ts', + 'vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput.ts', + 'vs/platform/terminal/common/capabilities/commandDetectionCapability.ts', + 'vs/workbench/contrib/testing/common/testExclusions.ts', + 'vs/workbench/contrib/testing/common/testResultStorage.ts', + 'vs/workbench/services/userDataProfile/browser/snippetsResource.ts', + 'vs/platform/quickinput/browser/quickInputController.ts', + 'vs/platform/userDataSync/common/abstractSynchronizer.ts', + 'vs/workbench/services/authentication/browser/authenticationExtensionsService.ts', + 'vs/workbench/services/textMate/browser/backgroundTokenization/textMateWorkerTokenizerController.ts', + 'vs/workbench/services/textMate/browser/textMateTokenizationFeatureImpl.ts', + 'vs/workbench/contrib/notebook/browser/services/notebookServiceImpl.ts', + 'vs/workbench/contrib/notebook/browser/contrib/multicursor/notebookMulticursor.ts', + 'vs/editor/browser/widget/multiDiffEditor/diffEditorItemTemplate.ts', + 'vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidgetImpl.ts', + 'vs/workbench/contrib/notebook/browser/diff/notebookMultiDiffEditor.ts', + 'vs/workbench/contrib/chat/common/promptSyntax/contentProviders/textModelContentsProvider.ts', + 'vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.ts', + 'vs/workbench/contrib/search/common/cacheState.ts', + 'vs/workbench/contrib/codeEditor/browser/quickaccess/gotoSymbolQuickAccess.ts', + 'vs/workbench/contrib/search/browser/anythingQuickAccess.ts', + 'vs/workbench/contrib/chat/browser/chatEditing/chatEditingSession.ts', + 'vs/workbench/contrib/testing/browser/testResultsView/testResultsOutput.ts', + 'vs/workbench/contrib/testing/common/testExplorerFilterState.ts', + 'vs/workbench/contrib/testing/browser/testResultsView/testResultsTree.ts', + 'vs/workbench/contrib/testing/browser/testingOutputPeek.ts', + 'vs/workbench/contrib/testing/browser/explorerProjections/index.ts', + 'vs/workbench/contrib/testing/browser/testingExplorerFilter.ts', + 'vs/workbench/contrib/testing/browser/testingExplorerView.ts', + 'vs/workbench/contrib/testing/common/testServiceImpl.ts', + 'vs/platform/quickinput/browser/commandsQuickAccess.ts', + 'vs/workbench/contrib/quickaccess/browser/commandsQuickAccess.ts', + 'vs/workbench/contrib/multiDiffEditor/browser/scmMultiDiffSourceResolver.ts', + 'vs/workbench/contrib/debug/browser/debugMemory.ts', + 'vs/workbench/contrib/markers/browser/markersViewActions.ts', + 'vs/workbench/contrib/mergeEditor/browser/view/viewZones.ts', + 'vs/workbench/contrib/mergeEditor/browser/view/mergeEditor.ts', + 'vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts', + 'vs/workbench/contrib/output/browser/outputServices.ts', + 'vs/workbench/contrib/terminalContrib/typeAhead/browser/terminalTypeAheadAddon.ts', + 'vs/workbench/contrib/codeEditor/browser/quickaccess/gotoLineQuickAccess.ts', + 'vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts', + 'vs/workbench/contrib/accessibilitySignals/browser/editorTextPropertySignalsContribution.ts', + 'vs/workbench/contrib/inlineCompletions/browser/inlineCompletionLanguageStatusBarContribution.ts', + 'vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts', + 'vs/workbench/contrib/welcomeDialog/browser/welcomeWidget.ts', + 'vs/editor/standalone/browser/quickInput/standaloneQuickInputService.ts', + 'vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsWordInsertView.ts', + 'vs/platform/terminal/node/ptyService.ts', + 'vs/workbench/api/common/extHostLanguageFeatures.ts', + 'vs/workbench/api/common/extHostSearch.ts', + 'vs/workbench/contrib/testing/test/common/testStubs.ts' +]); +const cancellationToken = { + isCancellationRequested: () => false, + throwIfCancellationRequested: () => { }, +}; +const seenFiles = new Set(); +let errorCount = 0; +function createProgram(tsconfigPath) { + const tsConfig = ts.readConfigFile(tsconfigPath, ts.sys.readFile); + const configHostParser = { fileExists: fs.existsSync, readDirectory: ts.sys.readDirectory, readFile: file => fs.readFileSync(file, 'utf8'), useCaseSensitiveFileNames: process.platform === 'linux' }; + const tsConfigParsed = ts.parseJsonConfigFileContent(tsConfig.config, configHostParser, path.resolve(path.dirname(tsconfigPath)), { noEmit: true }); + const compilerHost = ts.createCompilerHost(tsConfigParsed.options, true); + return ts.createProgram(tsConfigParsed.fileNames, tsConfigParsed.options, compilerHost); +} +const program = createProgram(TS_CONFIG_PATH); +program.getTypeChecker(); +for (const file of program.getSourceFiles()) { + if (!file || file.isDeclarationFile) { + continue; + } + const relativePath = path.relative(path.dirname(TS_CONFIG_PATH), file.fileName).replace(/\\/g, '/'); + if (ignored.has(relativePath)) { + continue; + } + visit(file); +} +if (seenFiles.size) { + console.log(); + console.log(`Found ${errorCount} error${errorCount === 1 ? '' : 's'} in ${seenFiles.size} file${seenFiles.size === 1 ? '' : 's'}.`); + process.exit(errorCount); +} +function visit(node) { + if (ts.isParameter(node) && ts.isParameterPropertyDeclaration(node, node.parent)) { + checkParameterPropertyDeclaration(node); + } + ts.forEachChild(node, visit); +} +function checkParameterPropertyDeclaration(param) { + const uses = [...collectReferences(param.name, [])]; + if (!uses.length) { + return; + } + const sourceFile = param.getSourceFile(); + if (!seenFiles.has(sourceFile)) { + if (seenFiles.size) { + console.log(``); + } + console.log(`${formatFileName(param)}:`); + seenFiles.add(sourceFile); + } + else { + console.log(``); + } + console.log(` Parameter property '${param.name.getText()}' is used before its declaration.`); + for (const { stack, container } of uses) { + const use = stack[stack.length - 1]; + console.log(` at ${formatLocation(use)}: ${formatMember(container)} -> ${formatStack(stack)}`); + errorCount++; + } +} +function* collectReferences(node, stack, requiresInvocationDepth = 0, seen = new Set()) { + for (const use of findAllReferencesInClass(node)) { + const container = findContainer(use); + if (!container || seen.has(container) || ts.isConstructorDeclaration(container)) { + continue; + } + seen.add(container); + const nextStack = [...stack, use]; + let nextRequiresInvocationDepth = requiresInvocationDepth; + if (isInvocation(use) && nextRequiresInvocationDepth > 0) { + nextRequiresInvocationDepth--; + } + if (ts.isPropertyDeclaration(container) && nextRequiresInvocationDepth === 0) { + yield { stack: nextStack, container }; + } + else if (requiresInvocation(container)) { + nextRequiresInvocationDepth++; + } + yield* collectReferences(container.name ?? container, nextStack, nextRequiresInvocationDepth, seen); + } +} +function requiresInvocation(definition) { + return ts.isMethodDeclaration(definition) || ts.isFunctionDeclaration(definition) || ts.isFunctionExpression(definition) || ts.isArrowFunction(definition); +} +function isInvocation(use) { + let location = use; + if (ts.isPropertyAccessExpression(location.parent) && location.parent.name === location) { + location = location.parent; + } + else if (ts.isElementAccessExpression(location.parent) && location.parent.argumentExpression === location) { + location = location.parent; + } + return ts.isCallExpression(location.parent) && location.parent.expression === location + || ts.isTaggedTemplateExpression(location.parent) && location.parent.tag === location; +} +function formatFileName(node) { + const sourceFile = node.getSourceFile(); + return path.resolve(sourceFile.fileName); +} +function formatLocation(node) { + const sourceFile = node.getSourceFile(); + const { line, character } = ts.getLineAndCharacterOfPosition(sourceFile, node.pos); + return `${formatFileName(sourceFile)}(${line + 1},${character + 1})`; +} +function formatStack(stack) { + return stack.slice().reverse().map((use) => formatUse(use)).join(' -> '); +} +function formatMember(container) { + const name = container.name?.getText(); + if (name) { + const className = findClass(container)?.name?.getText(); + if (className) { + return `${className}.${name}`; + } + return name; + } + return ''; +} +function formatUse(use) { + let text = use.getText(); + if (use.parent && ts.isPropertyAccessExpression(use.parent) && use.parent.name === use) { + if (use.parent.expression.kind === ts.SyntaxKind.ThisKeyword) { + text = `this.${text}`; + } + use = use.parent; + } + else if (use.parent && ts.isElementAccessExpression(use.parent) && use.parent.argumentExpression === use) { + if (use.parent.expression.kind === ts.SyntaxKind.ThisKeyword) { + text = `this['${text}']`; + } + use = use.parent; + } + if (ts.isCallExpression(use.parent)) { + text = `${text}(...)`; + } + return text; +} +function findContainer(node) { + return ts.findAncestor(node, ancestor => { + switch (ancestor.kind) { + case ts.SyntaxKind.PropertyDeclaration: + case ts.SyntaxKind.MethodDeclaration: + case ts.SyntaxKind.GetAccessor: + case ts.SyntaxKind.SetAccessor: + case ts.SyntaxKind.Constructor: + case ts.SyntaxKind.ClassStaticBlockDeclaration: + case ts.SyntaxKind.ArrowFunction: + case ts.SyntaxKind.FunctionExpression: + case ts.SyntaxKind.FunctionDeclaration: + case ts.SyntaxKind.Parameter: + return true; + } + return false; + }); +} +function findClass(node) { + return ts.findAncestor(node, ts.isClassLike); +} +function* findAllReferencesInClass(node) { + const classDecl = findClass(node); + if (!classDecl) { + return []; + } + for (const ref of findAllReferences(node)) { + for (const entry of ref.references) { + if (entry.kind !== 1 /* EntryKind.Node */ || entry.node === node) { + continue; + } + if (findClass(entry.node) === classDecl) { + yield entry.node; + } + } + } +} +// NOTE: The following uses TypeScript internals and are subject to change from version to version. +function findAllReferences(node) { + const sourceFile = node.getSourceFile(); + const position = node.getStart(); + const name = ts.getTouchingPropertyName(sourceFile, position); + const options = { use: ts.FindAllReferences.FindReferencesUse.References }; + return ts.FindAllReferences.Core.getReferencedSymbolsForNode(position, name, program, [sourceFile], cancellationToken, options) ?? []; +} +var DefinitionKind; +(function (DefinitionKind) { + DefinitionKind[DefinitionKind["Symbol"] = 0] = "Symbol"; + DefinitionKind[DefinitionKind["Label"] = 1] = "Label"; + DefinitionKind[DefinitionKind["Keyword"] = 2] = "Keyword"; + DefinitionKind[DefinitionKind["This"] = 3] = "This"; + DefinitionKind[DefinitionKind["String"] = 4] = "String"; + DefinitionKind[DefinitionKind["TripleSlashReference"] = 5] = "TripleSlashReference"; +})(DefinitionKind || (DefinitionKind = {})); +/** @internal */ +var EntryKind; +(function (EntryKind) { + EntryKind[EntryKind["Span"] = 0] = "Span"; + EntryKind[EntryKind["Node"] = 1] = "Node"; + EntryKind[EntryKind["StringLiteral"] = 2] = "StringLiteral"; + EntryKind[EntryKind["SearchedLocalFoundProperty"] = 3] = "SearchedLocalFoundProperty"; + EntryKind[EntryKind["SearchedPropertyFoundLocal"] = 4] = "SearchedPropertyFoundLocal"; +})(EntryKind || (exports.EntryKind = EntryKind = {})); +//# sourceMappingURL=propertyInitOrderChecker.js.map \ No newline at end of file diff --git a/build/lib/propertyInitOrderChecker.ts b/build/lib/propertyInitOrderChecker.ts new file mode 100644 index 000000000000..dc18213566f5 --- /dev/null +++ b/build/lib/propertyInitOrderChecker.ts @@ -0,0 +1,417 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + + +import * as ts from 'typescript'; +import * as path from 'path'; +import * as fs from 'fs'; + +const TS_CONFIG_PATH = path.join(__dirname, '../../', 'src', 'tsconfig.json'); + +// +// ############################################################################################# +// +// A custom typescript checker that ensure constructor properties are NOT used to initialize +// defined properties. This is needed for the times when `useDefineForClassFields` is gone. +// +// see https://github.com/microsoft/vscode/issues/243049, https://github.com/microsoft/vscode/issues/186726, +// https://github.com/microsoft/vscode/pull/241544 +// +// ############################################################################################# +// + +const ignored = new Set([ + 'vs/base/common/arrays.ts', + 'vs/platform/extensionManagement/common/extensionsScannerService.ts', + 'vs/platform/configuration/common/configurations.ts', + 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/tokenizer.ts', + 'vs/editor/common/model/bracketPairsTextModelPart/bracketPairsTree/bracketPairsTree.ts', + 'vs/editor/common/model/textModelTokens.ts', + 'vs/editor/common/model/tokenizationTextModelPart.ts', + 'vs/editor/common/core/textEdit.ts', + 'vs/workbench/contrib/debug/common/debugStorage.ts', + 'vs/workbench/contrib/debug/common/debugModel.ts', + 'vs/workbench/api/common/extHostCommands.ts', + 'vs/editor/browser/view/viewLayer.ts', + 'vs/editor/browser/controller/editContext/textArea/textAreaEditContextInput.ts', + 'vs/platform/accessibilitySignal/browser/accessibilitySignalService.ts', + 'vs/editor/browser/widget/diffEditor/utils.ts', + 'vs/editor/browser/observableCodeEditor.ts', + 'vs/editor/browser/widget/diffEditor/components/diffEditorViewZones/diffEditorViewZones.ts', + 'vs/editor/browser/widget/diffEditor/diffEditorOptions.ts', + 'vs/editor/browser/widget/diffEditor/components/diffEditorEditors.ts', + 'vs/editor/browser/widget/diffEditor/features/movedBlocksLinesFeature.ts', + 'vs/editor/browser/widget/diffEditor/components/diffEditorSash.ts', + 'vs/editor/browser/widget/diffEditor/utils/editorGutter.ts', + 'vs/editor/browser/widget/diffEditor/features/gutterFeature.ts', + 'vs/editor/browser/widget/diffEditor/features/revertButtonsFeature.ts', + 'vs/editor/browser/widget/diffEditor/diffEditorWidget.ts', + 'vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsSource.ts', + 'vs/editor/contrib/inlineCompletions/browser/model/suggestWidgetAdapter.ts', + 'vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts', + 'vs/editor/contrib/inlineCompletions/browser/hintsWidget/inlineCompletionsHintsWidget.ts', + 'vs/editor/contrib/inlayHints/browser/inlayHintsController.ts', + 'vs/editor/contrib/inlineCompletions/browser/model/changeRecorder.ts', + 'vs/editor/contrib/inlineCompletions/browser/view/ghostText/ghostTextView.ts', + 'vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditWithChanges.ts', + 'vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/components/gutterIndicatorView.ts', + 'vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsWordReplacementView.ts', + 'vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsLineReplacementView.ts', + 'vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsSideBySideView.ts', + 'vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/originalEditorInlineDiffView.ts', + 'vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsView.ts', + 'vs/editor/contrib/inlineCompletions/browser/view/inlineCompletionsView.ts', + 'vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.ts', + 'vs/editor/contrib/inlineCompletions/browser/inlineCompletionsAccessibleView.ts', + 'vs/editor/contrib/placeholderText/browser/placeholderTextContribution.ts', + 'vs/editor/contrib/unicodeHighlighter/browser/unicodeHighlighter.ts', + 'vs/workbench/contrib/chat/common/promptSyntax/parsers/basePromptParser.ts', + 'vs/workbench/contrib/files/browser/views/openEditorsView.ts', + 'vs/workbench/contrib/chat/browser/chatContentParts/chatAttachmentsContentPart.ts', + 'vs/workbench/contrib/chat/browser/contrib/chatImplicitContext.ts', + 'vs/workbench/contrib/chat/browser/chatInputPart.ts', + 'vs/workbench/contrib/mergeEditor/browser/model/modifiedBaseRange.ts', + 'vs/workbench/contrib/mergeEditor/browser/model/diffComputer.ts', + 'vs/workbench/contrib/mergeEditor/browser/model/mergeEditorModel.ts', + 'vs/workbench/contrib/mergeEditor/browser/view/editorGutter.ts', + 'vs/workbench/contrib/mergeEditor/browser/view/editors/codeEditorView.ts', + 'vs/workbench/contrib/mergeEditor/browser/view/editors/inputCodeEditorView.ts', + 'vs/workbench/contrib/mergeEditor/browser/view/viewModel.ts', + 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInputModel.ts', + 'vs/workbench/contrib/mergeEditor/browser/mergeEditorInput.ts', + 'vs/editor/browser/widget/multiDiffEditor/multiDiffEditorViewModel.ts', + 'vs/workbench/contrib/multiDiffEditor/browser/multiDiffEditorInput.ts', + 'vs/platform/terminal/common/capabilities/commandDetectionCapability.ts', + 'vs/workbench/contrib/testing/common/testExclusions.ts', + 'vs/workbench/contrib/testing/common/testResultStorage.ts', + 'vs/workbench/services/userDataProfile/browser/snippetsResource.ts', + 'vs/platform/quickinput/browser/quickInputController.ts', + 'vs/platform/userDataSync/common/abstractSynchronizer.ts', + 'vs/workbench/services/authentication/browser/authenticationExtensionsService.ts', + 'vs/workbench/services/textMate/browser/backgroundTokenization/textMateWorkerTokenizerController.ts', + 'vs/workbench/services/textMate/browser/textMateTokenizationFeatureImpl.ts', + 'vs/workbench/contrib/notebook/browser/services/notebookServiceImpl.ts', + 'vs/workbench/contrib/notebook/browser/contrib/multicursor/notebookMulticursor.ts', + 'vs/editor/browser/widget/multiDiffEditor/diffEditorItemTemplate.ts', + 'vs/editor/browser/widget/multiDiffEditor/multiDiffEditorWidgetImpl.ts', + 'vs/workbench/contrib/notebook/browser/diff/notebookMultiDiffEditor.ts', + 'vs/workbench/contrib/chat/common/promptSyntax/contentProviders/textModelContentsProvider.ts', + 'vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.ts', + 'vs/workbench/contrib/search/common/cacheState.ts', + 'vs/workbench/contrib/codeEditor/browser/quickaccess/gotoSymbolQuickAccess.ts', + 'vs/workbench/contrib/search/browser/anythingQuickAccess.ts', + 'vs/workbench/contrib/chat/browser/chatEditing/chatEditingSession.ts', + 'vs/workbench/contrib/testing/browser/testResultsView/testResultsOutput.ts', + 'vs/workbench/contrib/testing/common/testExplorerFilterState.ts', + 'vs/workbench/contrib/testing/browser/testResultsView/testResultsTree.ts', + 'vs/workbench/contrib/testing/browser/testingOutputPeek.ts', + 'vs/workbench/contrib/testing/browser/explorerProjections/index.ts', + 'vs/workbench/contrib/testing/browser/testingExplorerFilter.ts', + 'vs/workbench/contrib/testing/browser/testingExplorerView.ts', + 'vs/workbench/contrib/testing/common/testServiceImpl.ts', + 'vs/platform/quickinput/browser/commandsQuickAccess.ts', + 'vs/workbench/contrib/quickaccess/browser/commandsQuickAccess.ts', + 'vs/workbench/contrib/multiDiffEditor/browser/scmMultiDiffSourceResolver.ts', + 'vs/workbench/contrib/debug/browser/debugMemory.ts', + 'vs/workbench/contrib/markers/browser/markersViewActions.ts', + 'vs/workbench/contrib/mergeEditor/browser/view/viewZones.ts', + 'vs/workbench/contrib/mergeEditor/browser/view/mergeEditor.ts', + 'vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts', + 'vs/workbench/contrib/output/browser/outputServices.ts', + 'vs/workbench/contrib/terminalContrib/typeAhead/browser/terminalTypeAheadAddon.ts', + 'vs/workbench/contrib/codeEditor/browser/quickaccess/gotoLineQuickAccess.ts', + 'vs/workbench/contrib/editSessions/browser/editSessionsStorageService.ts', + 'vs/workbench/contrib/accessibilitySignals/browser/editorTextPropertySignalsContribution.ts', + 'vs/workbench/contrib/inlineCompletions/browser/inlineCompletionLanguageStatusBarContribution.ts', + 'vs/workbench/services/extensionManagement/common/webExtensionManagementService.ts', + 'vs/workbench/contrib/welcomeDialog/browser/welcomeWidget.ts', + 'vs/editor/standalone/browser/quickInput/standaloneQuickInputService.ts', + 'vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsWordInsertView.ts', + 'vs/platform/terminal/node/ptyService.ts', + 'vs/workbench/api/common/extHostLanguageFeatures.ts', + 'vs/workbench/api/common/extHostSearch.ts', + 'vs/workbench/contrib/testing/test/common/testStubs.ts' +]); + + +const cancellationToken: ts.CancellationToken = { + isCancellationRequested: () => false, + throwIfCancellationRequested: () => { }, +}; + +const seenFiles = new Set(); +let errorCount = 0; + + + +function createProgram(tsconfigPath: string): ts.Program { + const tsConfig = ts.readConfigFile(tsconfigPath, ts.sys.readFile); + + const configHostParser: ts.ParseConfigHost = { fileExists: fs.existsSync, readDirectory: ts.sys.readDirectory, readFile: file => fs.readFileSync(file, 'utf8'), useCaseSensitiveFileNames: process.platform === 'linux' }; + const tsConfigParsed = ts.parseJsonConfigFileContent(tsConfig.config, configHostParser, path.resolve(path.dirname(tsconfigPath)), { noEmit: true }); + + const compilerHost = ts.createCompilerHost(tsConfigParsed.options, true); + + return ts.createProgram(tsConfigParsed.fileNames, tsConfigParsed.options, compilerHost); +} + +const program = createProgram(TS_CONFIG_PATH); + +program.getTypeChecker(); + +for (const file of program.getSourceFiles()) { + if (!file || file.isDeclarationFile) { + continue; + } + + const relativePath = path.relative(path.dirname(TS_CONFIG_PATH), file.fileName).replace(/\\/g, '/'); + if (ignored.has(relativePath)) { + continue; + } + + visit(file); +} + +if (seenFiles.size) { + console.log(); + console.log(`Found ${errorCount} error${errorCount === 1 ? '' : 's'} in ${seenFiles.size} file${seenFiles.size === 1 ? '' : 's'}.`); + process.exit(errorCount); +} + +function visit(node: ts.Node) { + if (ts.isParameter(node) && ts.isParameterPropertyDeclaration(node, node.parent)) { + checkParameterPropertyDeclaration(node); + } + + ts.forEachChild(node, visit); +} + +function checkParameterPropertyDeclaration(param: ts.ParameterPropertyDeclaration) { + const uses = [...collectReferences(param.name, [])]; + if (!uses.length) { + return; + } + + const sourceFile = param.getSourceFile(); + if (!seenFiles.has(sourceFile)) { + if (seenFiles.size) { + console.log(``); + } + console.log(`${formatFileName(param)}:`); + seenFiles.add(sourceFile); + } else { + console.log(``); + } + console.log(` Parameter property '${param.name.getText()}' is used before its declaration.`); + for (const { stack, container } of uses) { + const use = stack[stack.length - 1]; + console.log(` at ${formatLocation(use)}: ${formatMember(container)} -> ${formatStack(stack)}`); + errorCount++; + } +} + +interface InvalidUse { + stack: ts.Node[]; + container: ReferenceContainer; +} + +function* collectReferences(node: ts.Node, stack: ts.Node[], requiresInvocationDepth: number = 0, seen = new Set()): Generator { + for (const use of findAllReferencesInClass(node)) { + const container = findContainer(use); + if (!container || seen.has(container) || ts.isConstructorDeclaration(container)) { + continue; + } + seen.add(container); + + const nextStack = [...stack, use]; + + let nextRequiresInvocationDepth = requiresInvocationDepth; + if (isInvocation(use) && nextRequiresInvocationDepth > 0) { + nextRequiresInvocationDepth--; + } + + if (ts.isPropertyDeclaration(container) && nextRequiresInvocationDepth === 0) { + yield { stack: nextStack, container }; + } + else if (requiresInvocation(container)) { + nextRequiresInvocationDepth++; + } + + yield* collectReferences(container.name ?? container, nextStack, nextRequiresInvocationDepth, seen); + } +} + +function requiresInvocation(definition: ReferenceContainer): boolean { + return ts.isMethodDeclaration(definition) || ts.isFunctionDeclaration(definition) || ts.isFunctionExpression(definition) || ts.isArrowFunction(definition); +} + +function isInvocation(use: ts.Node): boolean { + let location = use; + if (ts.isPropertyAccessExpression(location.parent) && location.parent.name === location) { + location = location.parent; + } + else if (ts.isElementAccessExpression(location.parent) && location.parent.argumentExpression === location) { + location = location.parent; + } + return ts.isCallExpression(location.parent) && location.parent.expression === location + || ts.isTaggedTemplateExpression(location.parent) && location.parent.tag === location; +} + +function formatFileName(node: ts.Node): string { + const sourceFile = node.getSourceFile(); + return path.resolve(sourceFile.fileName); +} + +function formatLocation(node: ts.Node): string { + const sourceFile = node.getSourceFile(); + const { line, character } = ts.getLineAndCharacterOfPosition(sourceFile, node.pos); + return `${formatFileName(sourceFile)}(${line + 1},${character + 1})`; +} + +function formatStack(stack: ts.Node[]): string { + return stack.slice().reverse().map((use) => formatUse(use)).join(' -> '); +} + +function formatMember(container: ReferenceContainer): string { + const name = container.name?.getText(); + if (name) { + const className = findClass(container)?.name?.getText(); + if (className) { + return `${className}.${name}`; + } + return name; + } + return ''; +} + +function formatUse(use: ts.Node): string { + let text = use.getText(); + if (use.parent && ts.isPropertyAccessExpression(use.parent) && use.parent.name === use) { + if (use.parent.expression.kind === ts.SyntaxKind.ThisKeyword) { + text = `this.${text}`; + } + use = use.parent; + } + else if (use.parent && ts.isElementAccessExpression(use.parent) && use.parent.argumentExpression === use) { + if (use.parent.expression.kind === ts.SyntaxKind.ThisKeyword) { + text = `this['${text}']`; + } + use = use.parent; + } + if (ts.isCallExpression(use.parent)) { + text = `${text}(...)`; + } + return text; +} + +type ReferenceContainer = + | ts.PropertyDeclaration + | ts.MethodDeclaration + | ts.GetAccessorDeclaration + | ts.SetAccessorDeclaration + | ts.ConstructorDeclaration + | ts.ClassStaticBlockDeclaration + | ts.ArrowFunction + | ts.FunctionExpression + | ts.FunctionDeclaration + | ts.ParameterDeclaration; + +function findContainer(node: ts.Node): ReferenceContainer | undefined { + return ts.findAncestor(node, ancestor => { + switch (ancestor.kind) { + case ts.SyntaxKind.PropertyDeclaration: + case ts.SyntaxKind.MethodDeclaration: + case ts.SyntaxKind.GetAccessor: + case ts.SyntaxKind.SetAccessor: + case ts.SyntaxKind.Constructor: + case ts.SyntaxKind.ClassStaticBlockDeclaration: + case ts.SyntaxKind.ArrowFunction: + case ts.SyntaxKind.FunctionExpression: + case ts.SyntaxKind.FunctionDeclaration: + case ts.SyntaxKind.Parameter: + return true; + } + return false; + }) as ReferenceContainer | undefined; +} + +function findClass(node: ts.Node): ts.ClassLikeDeclaration | undefined { + return ts.findAncestor(node, ts.isClassLike); +} + +function* findAllReferencesInClass(node: ts.Node): Generator { + const classDecl = findClass(node); + if (!classDecl) { + return []; + } + for (const ref of findAllReferences(node)) { + for (const entry of ref.references) { + if (entry.kind !== EntryKind.Node || entry.node === node) { + continue; + } + if (findClass(entry.node) === classDecl) { + yield entry.node; + } + } + } +} + +// NOTE: The following uses TypeScript internals and are subject to change from version to version. + +function findAllReferences(node: ts.Node): readonly SymbolAndEntries[] { + const sourceFile = node.getSourceFile(); + const position = node.getStart(); + const name: ts.Node = (ts as any).getTouchingPropertyName(sourceFile, position); + const options = { use: (ts as any).FindAllReferences.FindReferencesUse.References }; + return (ts as any).FindAllReferences.Core.getReferencedSymbolsForNode(position, name, program, [sourceFile], cancellationToken, options) ?? []; +} + +interface SymbolAndEntries { + readonly definition: Definition | undefined; + readonly references: readonly Entry[]; +} + +const enum DefinitionKind { + Symbol, + Label, + Keyword, + This, + String, + TripleSlashReference, +} + +type Definition = + | { readonly type: DefinitionKind.Symbol; readonly symbol: ts.Symbol } + | { readonly type: DefinitionKind.Label; readonly node: ts.Identifier } + | { readonly type: DefinitionKind.Keyword; readonly node: ts.Node } + | { readonly type: DefinitionKind.This; readonly node: ts.Node } + | { readonly type: DefinitionKind.String; readonly node: ts.StringLiteralLike } + | { readonly type: DefinitionKind.TripleSlashReference; readonly reference: ts.FileReference; readonly file: ts.SourceFile }; + +/** @internal */ +export const enum EntryKind { + Span, + Node, + StringLiteral, + SearchedLocalFoundProperty, + SearchedPropertyFoundLocal, +} +type NodeEntryKind = EntryKind.Node | EntryKind.StringLiteral | EntryKind.SearchedLocalFoundProperty | EntryKind.SearchedPropertyFoundLocal; +type Entry = NodeEntry | SpanEntry; +interface ContextWithStartAndEndNode { + start: ts.Node; + end: ts.Node; +} +type ContextNode = ts.Node | ContextWithStartAndEndNode; +interface NodeEntry { + readonly kind: NodeEntryKind; + readonly node: ts.Node; + readonly context?: ContextNode; +} +interface SpanEntry { + readonly kind: EntryKind.Span; + readonly fileName: string; + readonly textSpan: ts.TextSpan; +} diff --git a/build/lib/stylelint/vscode-known-variables.json b/build/lib/stylelint/vscode-known-variables.json index 92fe96c874f3..6b8419291cb6 100644 --- a/build/lib/stylelint/vscode-known-variables.json +++ b/build/lib/stylelint/vscode-known-variables.json @@ -232,6 +232,8 @@ "--vscode-editorGutter-commentGlyphForeground", "--vscode-editorGutter-commentRangeForeground", "--vscode-editorGutter-commentUnresolvedGlyphForeground", + "--vscode-editorGutter-itemGlyphForeground", + "--vscode-editorGutter-itemBackground", "--vscode-editorGutter-deletedBackground", "--vscode-editorGutter-foldingControlForeground", "--vscode-editorGutter-modifiedBackground", @@ -279,6 +281,7 @@ "--vscode-editorMarkerNavigationInfo-headerBackground", "--vscode-editorMarkerNavigationWarning-background", "--vscode-editorMarkerNavigationWarning-headerBackground", + "--vscode-editorMinimap-inlineChatInserted", "--vscode-editorMultiCursor-primary-background", "--vscode-editorMultiCursor-primary-foreground", "--vscode-editorMultiCursor-secondary-background", @@ -349,6 +352,13 @@ "--vscode-extensionIcon-verifiedForeground", "--vscode-focusBorder", "--vscode-foreground", + "--vscode-gauge-background", + "--vscode-gauge-border", + "--vscode-gauge-errorBackground", + "--vscode-gauge-errorForeground", + "--vscode-gauge-foreground", + "--vscode-gauge-warningBackground", + "--vscode-gauge-warningForeground", "--vscode-icon-foreground", "--vscode-inlineChat-background", "--vscode-inlineChat-border", @@ -360,17 +370,26 @@ "--vscode-inlineChatInput-border", "--vscode-inlineChatInput-focusBorder", "--vscode-inlineChatInput-placeholderForeground", - "--vscode-inlineEdit-border", + "--vscode-inlineEdit-gutterIndicator-background", + "--vscode-inlineEdit-gutterIndicator-primaryBackground", + "--vscode-inlineEdit-gutterIndicator-primaryForeground", + "--vscode-inlineEdit-gutterIndicator-secondaryBackground", + "--vscode-inlineEdit-gutterIndicator-secondaryForeground", + "--vscode-inlineEdit-gutterIndicator-successfulBackground", + "--vscode-inlineEdit-gutterIndicator-successfulForeground", "--vscode-inlineEdit-indicator-background", "--vscode-inlineEdit-indicator-border", "--vscode-inlineEdit-indicator-foreground", "--vscode-inlineEdit-modifiedBackground", + "--vscode-inlineEdit-modifiedBorder", "--vscode-inlineEdit-modifiedChangedLineBackground", "--vscode-inlineEdit-modifiedChangedTextBackground", "--vscode-inlineEdit-originalBackground", + "--vscode-inlineEdit-originalBorder", "--vscode-inlineEdit-originalChangedLineBackground", "--vscode-inlineEdit-originalChangedTextBackground", - "--vscode-inlineEdit-acceptedBackground", + "--vscode-inlineEdit-tabWillAcceptBorder", + "--vscode-inlineEdit-wordReplacementView-background", "--vscode-input-background", "--vscode-input-border", "--vscode-input-foreground", @@ -455,6 +474,7 @@ "--vscode-mergeEditor-conflict-unhandledUnfocused-border", "--vscode-mergeEditor-conflictingLines-background", "--vscode-minimap-background", + "--vscode-minimap-chatEditHighlight", "--vscode-minimap-errorHighlight", "--vscode-minimap-findMatchHighlight", "--vscode-minimap-foregroundOpacity", @@ -764,6 +784,8 @@ "--vscode-terminalStickyScroll-background", "--vscode-terminalStickyScroll-border", "--vscode-terminalStickyScrollHover-background", + "--vscode-terminalSymbolIcon-aliasForeground", + "--vscode-terminalSymbolIcon-flagForeground", "--vscode-testing-coverCountBadgeBackground", "--vscode-testing-coverCountBadgeForeground", "--vscode-testing-coveredBackground", @@ -919,4 +941,4 @@ "--monaco-editor-warning-decoration", "--animation-opacity" ] -} +} \ No newline at end of file diff --git a/build/linux/dependencies-generator.js b/build/linux/dependencies-generator.js index 913de4df7d7c..448ab38c4a43 100644 --- a/build/linux/dependencies-generator.js +++ b/build/linux/dependencies-generator.js @@ -26,7 +26,7 @@ const product = require("../../product.json"); // 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/132.0.6834.194:chrome/installer/linux/BUILD.gn;l=64-80 +// Based on https://source.chromium.org/chromium/chromium/src/+/refs/tags/132.0.6834.210: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 4796980b96e3..6c1f7b7570b5 100644 --- a/build/linux/dependencies-generator.ts +++ b/build/linux/dependencies-generator.ts @@ -25,7 +25,7 @@ import product = require('../../product.json'); // 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/132.0.6834.194:chrome/installer/linux/BUILD.gn;l=64-80 +// Based on https://source.chromium.org/chromium/chromium/src/+/refs/tags/132.0.6834.210: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 13f323760376..f45b6f34b843 100644 --- a/build/linux/rpm/dep-lists.js +++ b/build/linux/rpm/dep-lists.js @@ -46,6 +46,7 @@ exports.referenceGeneratedDepsByArch = { 'libc.so.6(GLIBC_2.18)(64bit)', 'libc.so.6(GLIBC_2.2.5)(64bit)', 'libc.so.6(GLIBC_2.25)(64bit)', + 'libc.so.6(GLIBC_2.27)(64bit)', 'libc.so.6(GLIBC_2.28)(64bit)', 'libc.so.6(GLIBC_2.3)(64bit)', 'libc.so.6(GLIBC_2.3.2)(64bit)', @@ -140,6 +141,7 @@ exports.referenceGeneratedDepsByArch = { 'libc.so.6(GLIBC_2.17)', 'libc.so.6(GLIBC_2.18)', 'libc.so.6(GLIBC_2.25)', + 'libc.so.6(GLIBC_2.27)', 'libc.so.6(GLIBC_2.28)', 'libc.so.6(GLIBC_2.4)', 'libc.so.6(GLIBC_2.6)', @@ -240,6 +242,7 @@ exports.referenceGeneratedDepsByArch = { 'libc.so.6(GLIBC_2.17)(64bit)', 'libc.so.6(GLIBC_2.18)(64bit)', 'libc.so.6(GLIBC_2.25)(64bit)', + 'libc.so.6(GLIBC_2.27)(64bit)', 'libc.so.6(GLIBC_2.28)(64bit)', 'libcairo.so.2()(64bit)', 'libcurl.so.4()(64bit)', diff --git a/build/linux/rpm/dep-lists.ts b/build/linux/rpm/dep-lists.ts index 63b47522d50d..d277ca7e6647 100644 --- a/build/linux/rpm/dep-lists.ts +++ b/build/linux/rpm/dep-lists.ts @@ -45,6 +45,7 @@ export const referenceGeneratedDepsByArch = { 'libc.so.6(GLIBC_2.18)(64bit)', 'libc.so.6(GLIBC_2.2.5)(64bit)', 'libc.so.6(GLIBC_2.25)(64bit)', + 'libc.so.6(GLIBC_2.27)(64bit)', 'libc.so.6(GLIBC_2.28)(64bit)', 'libc.so.6(GLIBC_2.3)(64bit)', 'libc.so.6(GLIBC_2.3.2)(64bit)', @@ -139,6 +140,7 @@ export const referenceGeneratedDepsByArch = { 'libc.so.6(GLIBC_2.17)', 'libc.so.6(GLIBC_2.18)', 'libc.so.6(GLIBC_2.25)', + 'libc.so.6(GLIBC_2.27)', 'libc.so.6(GLIBC_2.28)', 'libc.so.6(GLIBC_2.4)', 'libc.so.6(GLIBC_2.6)', @@ -239,6 +241,7 @@ export const referenceGeneratedDepsByArch = { 'libc.so.6(GLIBC_2.17)(64bit)', 'libc.so.6(GLIBC_2.18)(64bit)', 'libc.so.6(GLIBC_2.25)(64bit)', + 'libc.so.6(GLIBC_2.27)(64bit)', 'libc.so.6(GLIBC_2.28)(64bit)', 'libcairo.so.2()(64bit)', 'libcurl.so.4()(64bit)', diff --git a/build/npm/gyp/package-lock.json b/build/npm/gyp/package-lock.json index a20d85c70dca..a6b04265a2a8 100644 --- a/build/npm/gyp/package-lock.json +++ b/build/npm/gyp/package-lock.json @@ -9,7 +9,30 @@ "version": "1.0.0", "license": "MIT", "devDependencies": { - "node-gyp": "^10.1.0" + "node-gyp": "^12.1.0" + } + }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" } }, "node_modules/@isaacs/cliui": { @@ -30,88 +53,73 @@ "node": ">=12" } }, + "node_modules/@isaacs/fs-minipass": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/fs-minipass/-/fs-minipass-4.0.1.tgz", + "integrity": "sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==", + "dev": true, + "license": "ISC", + "dependencies": { + "minipass": "^7.0.4" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@npmcli/agent": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-2.2.2.tgz", - "integrity": "sha512-OrcNPXdpSl9UX7qPVRWbmWMCSXrcDa2M9DvrbOTj7ao1S4PlqVFYv9/yLKMkrJKZ/V5A/kDBC690or307i26Og==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/agent/-/agent-4.0.0.tgz", + "integrity": "sha512-kAQTcEN9E8ERLVg5AsGwLNoFb+oEG6engbqAU2P43gD4JEIkNGMHdVQ096FsOAAYpZPB0RSt0zgInKIAS1l5QA==", "dev": true, "license": "ISC", "dependencies": { "agent-base": "^7.1.0", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.1", - "lru-cache": "^10.0.1", + "lru-cache": "^11.2.1", "socks-proxy-agent": "^8.0.3" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/@npmcli/fs": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-3.1.1.tgz", - "integrity": "sha512-q9CRWjpHCMIh5sVyefoD1cA7PkvILqCZsnSOEUUivORLjxCO/Irmue2DprETiNgEqktDBZaM1Bi+jrarx1XdCg==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@npmcli/fs/-/fs-4.0.0.tgz", + "integrity": "sha512-/xGlezI6xfGO9NwuJlnwz/K14qD1kCSAGtacBHnGzeAIuJGazcp45KP5NuyARXoKb7cwulAGWVsbeSxdG/cb0Q==", "dev": true, "license": "ISC", "dependencies": { "semver": "^7.3.5" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" - } - }, - "node_modules/@pkgjs/parseargs": { - "version": "0.11.0", - "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", - "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", - "dev": true, - "license": "MIT", - "optional": true, - "engines": { - "node": ">=14" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/abbrev": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", - "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-4.0.0.tgz", + "integrity": "sha512-a1wflyaL0tHtJSmLSOVybYhy22vRih4eduhhrkcjgrWGnRfrZtovJ2FRjxuTtkkj47O/baf0R86QU5OuYpz8fA==", "dev": true, "license": "ISC", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/agent-base": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", "dev": true, "license": "MIT", - "dependencies": { - "debug": "^4.3.4" - }, "engines": { "node": ">= 14" } }, - "node_modules/aggregate-error": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.1.0.tgz", - "integrity": "sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==", - "dev": true, - "license": "MIT", - "dependencies": { - "clean-stack": "^2.0.0", - "indent-string": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/ansi-regex": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", - "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", "dev": true, "license": "MIT", "engines": { @@ -122,9 +130,9 @@ } }, "node_modules/ansi-styles": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", - "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", "dev": true, "license": "MIT", "engines": { @@ -134,65 +142,37 @@ "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", - "dev": true, - "license": "MIT" - }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "license": "MIT", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, "node_modules/cacache": { - "version": "18.0.3", - "resolved": "https://registry.npmjs.org/cacache/-/cacache-18.0.3.tgz", - "integrity": "sha512-qXCd4rh6I07cnDqh8V48/94Tc/WSfj+o3Gn6NZ0aZovS255bUx8O13uKxRFd2eWG0xgsco7+YItQNPaa5E85hg==", + "version": "20.0.2", + "resolved": "https://registry.npmjs.org/cacache/-/cacache-20.0.2.tgz", + "integrity": "sha512-rVWvqtWcgSzB22wImrVto+7PmE+lUqv5dYzRHD0QJsfpSwTkW+GIqA4ykSt/CCjQlQle8USn8CO8vcWNrIqktg==", "dev": true, "license": "ISC", "dependencies": { - "@npmcli/fs": "^3.1.0", + "@npmcli/fs": "^4.0.0", "fs-minipass": "^3.0.0", - "glob": "^10.2.2", - "lru-cache": "^10.0.1", + "glob": "^11.0.3", + "lru-cache": "^11.1.0", "minipass": "^7.0.3", "minipass-collect": "^2.0.1", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", - "p-map": "^4.0.0", - "ssri": "^10.0.0", - "tar": "^6.1.11", - "unique-filename": "^3.0.0" + "p-map": "^7.0.2", + "ssri": "^13.0.0", + "unique-filename": "^4.0.0" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/chownr": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", - "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=10" - } - }, - "node_modules/clean-stack": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz", - "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-3.0.0.tgz", + "integrity": "sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==", "dev": true, - "license": "MIT", + "license": "BlueOak-1.0.0", "engines": { - "node": ">=6" + "node": ">=18" } }, "node_modules/color-convert": { @@ -216,9 +196,9 @@ "license": "MIT" }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "license": "MIT", "dependencies": { @@ -254,13 +234,13 @@ } }, "node_modules/debug": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.5.tgz", - "integrity": "sha512-pt0bNEmneDIvdL1Xsd9oDQ/wrQRkXDT4AUWlNZNPKvW5x/jyO9VFXkJUP07vQ2upmw5PlaITaPKc31jK13V+jg==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -320,14 +300,32 @@ "dev": true, "license": "Apache-2.0" }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, "node_modules/foreground-child": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.2.1.tgz", - "integrity": "sha512-PXUUyLqrR2XCWICfv6ukppP96sdFwWbNEnfEMt7jNsISjMsvaLNinAHNDYyvkyU+SZG2BTSbT5NjG+vZslfGTA==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "dev": true, "license": "ISC", "dependencies": { - "cross-spawn": "^7.0.0", + "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" }, "engines": { @@ -351,24 +349,24 @@ } }, "node_modules/glob": { - "version": "10.4.2", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.4.2.tgz", - "integrity": "sha512-GwMlUF6PkPo3Gk21UxkCohOv0PLcIXVtKyLlpEI28R/cO/4eNOdmLk3CMW1wROV/WR/EsZOWAfBbBOqYvs88/w==", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", + "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^3.1.2", - "minimatch": "^9.0.4", + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.1.1", "minipass": "^7.1.2", "package-json-from-dist": "^1.0.0", - "path-scurry": "^1.11.1" + "path-scurry": "^2.0.0" }, "bin": { "glob": "dist/esm/bin.mjs" }, "engines": { - "node": ">=16 || 14 >=14.18" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -382,9 +380,9 @@ "license": "ISC" }, "node_modules/http-cache-semantics": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", - "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", "dev": true, "license": "BSD-2-Clause" }, @@ -403,13 +401,13 @@ } }, "node_modules/https-proxy-agent": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", - "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "dev": true, "license": "MIT", "dependencies": { - "agent-base": "^7.0.2", + "agent-base": "^7.1.2", "debug": "4" }, "engines": { @@ -440,26 +438,12 @@ "node": ">=0.8.19" } }, - "node_modules/indent-string": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz", - "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=8" - } - }, "node_modules/ip-address": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-9.0.5.tgz", - "integrity": "sha512-zHtQzGojZXTwZTHQqra+ETKd4Sn3vgi7uBmlPoXVWZqYvuKmtI0l/VZTjqGmJY9x88GGOaZ9+G9ES8hC4T4X8g==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/ip-address/-/ip-address-10.1.0.tgz", + "integrity": "sha512-XXADHxXmvT9+CRxhXg56LJovE+bmWnEWB78LB83VZTprKTmaC5QfruXocxzTZ2Kl0DNwKuBdlIhjL8LeY8Sf8Q==", "dev": true, "license": "MIT", - "dependencies": { - "jsbn": "1.1.0", - "sprintf-js": "^1.1.3" - }, "engines": { "node": ">= 12" } @@ -474,13 +458,6 @@ "node": ">=8" } }, - "node_modules/is-lambda": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-lambda/-/is-lambda-1.0.1.tgz", - "integrity": "sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==", - "dev": true, - "license": "MIT" - }, "node_modules/isexe": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", @@ -492,86 +469,65 @@ } }, "node_modules/jackspeak": { - "version": "3.4.0", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.0.tgz", - "integrity": "sha512-JVYhQnN59LVPFCEcVa2C3CrEKYacvjRfqIQl+h8oi91aLYQVWRYbxjPcv1bUiUy/kLmQaANrYfNMCO3kuEDHfw==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", + "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" }, "engines": { - "node": ">=14" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" - }, - "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/jsbn": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-1.1.0.tgz", - "integrity": "sha512-4bYVV3aAMtDTTu4+xsDYa6sy9GyJ69/amsu9sYF2zqjiEoZA5xJi3BrfX3uY+/IekIu7MwdObdbDWpoZdBv3/A==", - "dev": true, - "license": "MIT" - }, "node_modules/lru-cache": { - "version": "10.3.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.3.0.tgz", - "integrity": "sha512-CQl19J/g+Hbjbv4Y3mFNNXFEL/5t/KCg8POCuUqd4rMKjGG+j1ybER83hxV58zL+dFI1PTkt3GNFSHRt+d8qEQ==", + "version": "11.2.2", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.2.tgz", + "integrity": "sha512-F9ODfyqML2coTIsQpSkRHnLSZMtkU8Q+mSfcaIyKwy58u+8k5nvAYeiNhsyMARvzNcXJ9QfWVrcPsC9e9rAxtg==", "dev": true, "license": "ISC", "engines": { - "node": "14 || >=16.14" + "node": "20 || >=22" } }, "node_modules/make-fetch-happen": { - "version": "13.0.1", - "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-13.0.1.tgz", - "integrity": "sha512-cKTUFc/rbKUd/9meOvgrpJ2WrNzymt6jfRDdwg5UCnVzv9dTpEj9JS5m3wtziXVCjluIXyL8pcaukYqezIzZQA==", + "version": "15.0.3", + "resolved": "https://registry.npmjs.org/make-fetch-happen/-/make-fetch-happen-15.0.3.tgz", + "integrity": "sha512-iyyEpDty1mwW3dGlYXAJqC/azFn5PPvgKVwXayOGBSmKLxhKZ9fg4qIan2ePpp1vJIwfFiO34LAPZgq9SZW9Aw==", "dev": true, "license": "ISC", "dependencies": { - "@npmcli/agent": "^2.0.0", - "cacache": "^18.0.0", + "@npmcli/agent": "^4.0.0", + "cacache": "^20.0.1", "http-cache-semantics": "^4.1.1", - "is-lambda": "^1.0.1", "minipass": "^7.0.2", - "minipass-fetch": "^3.0.0", + "minipass-fetch": "^5.0.0", "minipass-flush": "^1.0.5", "minipass-pipeline": "^1.2.4", - "negotiator": "^0.6.3", - "proc-log": "^4.2.0", + "negotiator": "^1.0.0", + "proc-log": "^6.0.0", "promise-retry": "^2.0.1", - "ssri": "^10.0.0" + "ssri": "^13.0.0" }, "engines": { - "node": "^16.14.0 || >=18.0.0" - } - }, - "node_modules/make-fetch-happen/node_modules/proc-log": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-4.2.0.tgz", - "integrity": "sha512-g8+OnU/L2v+wyiVK+D5fA34J7EH8jZ8DDlvwhRCMxmMj7UCBvxiO1mGeN+36JXIKF4zevU4kRBd8lVgG9vLelA==", - "dev": true, - "license": "ISC", - "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/minimatch": { - "version": "9.0.5", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", - "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^2.0.1" + "@isaacs/brace-expansion": "^5.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -601,18 +557,18 @@ } }, "node_modules/minipass-fetch": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-3.0.5.tgz", - "integrity": "sha512-2N8elDQAtSnFV0Dk7gt15KHsS0Fyz6CbYZ360h0WTYV1Ty46li3rAXVOQj1THMNLdmrD9Vt5pBPtWtVkpwGBqg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/minipass-fetch/-/minipass-fetch-5.0.0.tgz", + "integrity": "sha512-fiCdUALipqgPWrOVTz9fw0XhcazULXOSU6ie40DDbX1F49p1dBrSRBuswndTx1x3vEb/g0FT7vC4c4C2u/mh3A==", "dev": true, "license": "MIT", "dependencies": { "minipass": "^7.0.3", "minipass-sized": "^1.0.3", - "minizlib": "^2.1.2" + "minizlib": "^3.0.1" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" }, "optionalDependencies": { "encoding": "^0.1.13" @@ -644,6 +600,13 @@ "node": ">=8" } }, + "node_modules/minipass-flush/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, "node_modules/minipass-pipeline": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/minipass-pipeline/-/minipass-pipeline-1.2.4.tgz", @@ -670,6 +633,13 @@ "node": ">=8" } }, + "node_modules/minipass-pipeline/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true, + "license": "ISC" + }, "node_modules/minipass-sized": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/minipass-sized/-/minipass-sized-1.0.3.tgz", @@ -696,57 +666,37 @@ "node": ">=8" } }, - "node_modules/minizlib": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-2.1.2.tgz", - "integrity": "sha512-bAxsR8BVfj60DWXHE3u30oHzfl4G7khkSuPW+qvpd7jFRHm7dLxOjUk1EHACJ/hxLY8phGJ0YhYHZo7jil7Qdg==", - "dev": true, - "license": "MIT", - "dependencies": { - "minipass": "^3.0.0", - "yallist": "^4.0.0" - }, - "engines": { - "node": ">= 8" - } - }, - "node_modules/minizlib/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", + "node_modules/minipass-sized/node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=8" - } + "license": "ISC" }, - "node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", + "node_modules/minizlib": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-3.1.0.tgz", + "integrity": "sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==", "dev": true, "license": "MIT", - "bin": { - "mkdirp": "bin/cmd.js" + "dependencies": { + "minipass": "^7.1.2" }, "engines": { - "node": ">=10" + "node": ">= 18" } }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true, "license": "MIT" }, "node_modules/negotiator": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", - "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-1.0.0.tgz", + "integrity": "sha512-8Ofs/AUQh8MaEcrlq5xOX0CQ9ypTF5dl78mjlMNfOK08fzpgTHQRQPBxcPlEtIw0yRpws+Zo/3r+5WRby7u3Gg==", "dev": true, "license": "MIT", "engines": { @@ -754,66 +704,63 @@ } }, "node_modules/node-gyp": { - "version": "10.1.0", - "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-10.1.0.tgz", - "integrity": "sha512-B4J5M1cABxPc5PwfjhbV5hoy2DP9p8lFXASnEN6hugXOa61416tnTZ29x9sSwAd0o99XNIcpvDDy1swAExsVKA==", + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/node-gyp/-/node-gyp-12.1.0.tgz", + "integrity": "sha512-W+RYA8jBnhSr2vrTtlPYPc1K+CSjGpVDRZxcqJcERZ8ND3A1ThWPHRwctTx3qC3oW99jt726jhdz3Y6ky87J4g==", "dev": true, "license": "MIT", "dependencies": { "env-paths": "^2.2.0", "exponential-backoff": "^3.1.1", - "glob": "^10.3.10", "graceful-fs": "^4.2.6", - "make-fetch-happen": "^13.0.0", - "nopt": "^7.0.0", - "proc-log": "^3.0.0", + "make-fetch-happen": "^15.0.0", + "nopt": "^9.0.0", + "proc-log": "^6.0.0", "semver": "^7.3.5", - "tar": "^6.1.2", - "which": "^4.0.0" + "tar": "^7.5.2", + "tinyglobby": "^0.2.12", + "which": "^6.0.0" }, "bin": { "node-gyp": "bin/node-gyp.js" }, "engines": { - "node": "^16.14.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/nopt": { - "version": "7.2.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", - "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-9.0.0.tgz", + "integrity": "sha512-Zhq3a+yFKrYwSBluL4H9XP3m3y5uvQkB/09CwDruCiRmR/UJYnn9W4R48ry0uGC70aeTPKLynBtscP9efFFcPw==", "dev": true, "license": "ISC", "dependencies": { - "abbrev": "^2.0.0" + "abbrev": "^4.0.0" }, "bin": { "nopt": "bin/nopt.js" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/p-map": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz", - "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz", + "integrity": "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==", "dev": true, "license": "MIT", - "dependencies": { - "aggregate-error": "^3.0.0" - }, "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/package-json-from-dist": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", - "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", "dev": true, "license": "BlueOak-1.0.0" }, @@ -828,30 +775,43 @@ } }, "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz", + "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", "dev": true, "license": "BlueOak-1.0.0", "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" }, "engines": { - "node": ">=16 || 14 >=14.18" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/proc-log": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-3.0.0.tgz", - "integrity": "sha512-++Vn7NS4Xf9NacaU9Xq3URUuqZETPsf8L4j5/ckhaRYsfPeRyzGw+iDjFhV/Jr3uNmTvvddEJFWh5R1gRgUH8A==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/proc-log/-/proc-log-6.0.0.tgz", + "integrity": "sha512-KG/XsTDN901PNfPfAMmj6N/Ywg9tM+bHK8pAz+27fS4N4Pcr+4zoYBOcGSBu6ceXYNPxkLpa4ohtfxV1XcLAfA==", "dev": true, "license": "ISC", "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/promise-retry": { @@ -887,14 +847,11 @@ "optional": true }, "node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "dev": true, "license": "ISC", - "dependencies": { - "lru-cache": "^6.0.0" - }, "bin": { "semver": "bin/semver.js" }, @@ -902,19 +859,6 @@ "node": ">=10" } }, - "node_modules/semver/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -963,13 +907,13 @@ } }, "node_modules/socks": { - "version": "2.8.3", - "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.3.tgz", - "integrity": "sha512-l5x7VUUWbjVFbafGLxPWkYsHIhEvmF85tbIeFZWc8ZPtoMyybuEhL7Jye/ooC4/d48FgOjSJXgsF/AJPYCW8Zw==", + "version": "2.8.7", + "resolved": "https://registry.npmjs.org/socks/-/socks-2.8.7.tgz", + "integrity": "sha512-HLpt+uLy/pxB+bum/9DzAgiKS8CX1EvbWxI4zlmgGCExImLdiad2iCwXT5Z4c9c3Eq8rP2318mPW2c+QbtjK8A==", "dev": true, "license": "MIT", "dependencies": { - "ip-address": "^9.0.5", + "ip-address": "^10.0.1", "smart-buffer": "^4.2.0" }, "engines": { @@ -978,13 +922,13 @@ } }, "node_modules/socks-proxy-agent": { - "version": "8.0.4", - "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.4.tgz", - "integrity": "sha512-GNAq/eg8Udq2x0eNiFkr9gRg5bA7PXEWagQdeRX4cPSG+X/8V38v637gim9bjFptMk1QWsCTr0ttrJEiXbNnRw==", + "version": "8.0.5", + "resolved": "https://registry.npmjs.org/socks-proxy-agent/-/socks-proxy-agent-8.0.5.tgz", + "integrity": "sha512-HehCEsotFqbPW9sJ8WVYB6UbmIMv7kUUORIF2Nncq4VQvBfNBLibW9YZR5dlYCSUhwcD628pRllm7n+E+YTzJw==", "dev": true, "license": "MIT", "dependencies": { - "agent-base": "^7.1.1", + "agent-base": "^7.1.2", "debug": "^4.3.4", "socks": "^2.8.3" }, @@ -992,24 +936,17 @@ "node": ">= 14" } }, - "node_modules/sprintf-js": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", - "integrity": "sha512-Oo+0REFV59/rz3gfJNKQiBlwfHaSESl1pcGyABQsnnIfWOFt6JNj5gCog2U6MLZ//IGYD+nA8nI+mTShREReaA==", - "dev": true, - "license": "BSD-3-Clause" - }, "node_modules/ssri": { - "version": "10.0.6", - "resolved": "https://registry.npmjs.org/ssri/-/ssri-10.0.6.tgz", - "integrity": "sha512-MGrFH9Z4NP9Iyhqn16sDtBpRRNJ0Y2hNa6D65h736fVSaPCHr4DM4sWUNvVaSuC+0OBGhwsrydQwmgfg5LncqQ==", + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/ssri/-/ssri-13.0.0.tgz", + "integrity": "sha512-yizwGBpbCn4YomB2lzhZqrHLJoqFGXihNbib3ozhqF/cIp5ue+xSmOQrjNasEE62hFxsCcg/V/z23t4n8jMEng==", "dev": true, "license": "ISC", "dependencies": { "minipass": "^7.0.3" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/string-width": { @@ -1077,9 +1014,9 @@ } }, "node_modules/strip-ansi": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", - "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", "dev": true, "license": "MIT", "dependencies": { @@ -1117,89 +1054,69 @@ } }, "node_modules/tar": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/tar/-/tar-6.2.1.tgz", - "integrity": "sha512-DZ4yORTwrbTj/7MZYq2w+/ZFdI6OZ/f9SFHR+71gIVUZhOQPHzVCLpvRnPgyaMpfWxxk/4ONva3GQSyNIKRv6A==", + "version": "7.5.6", + "resolved": "https://registry.npmjs.org/tar/-/tar-7.5.6.tgz", + "integrity": "sha512-xqUeu2JAIJpXyvskvU3uvQW8PAmHrtXp2KDuMJwQqW8Sqq0CaZBAQ+dKS3RBXVhU4wC5NjAdKrmh84241gO9cA==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "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" + "@isaacs/fs-minipass": "^4.0.0", + "chownr": "^3.0.0", + "minipass": "^7.1.2", + "minizlib": "^3.1.0", + "yallist": "^5.0.0" }, "engines": { - "node": ">=10" + "node": ">=18" } }, - "node_modules/tar/node_modules/fs-minipass": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-2.1.0.tgz", - "integrity": "sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==", + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "minipass": "^3.0.0" + "fdir": "^6.5.0", + "picomatch": "^4.0.3" }, "engines": { - "node": ">= 8" - } - }, - "node_modules/tar/node_modules/fs-minipass/node_modules/minipass": { - "version": "3.3.6", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-3.3.6.tgz", - "integrity": "sha512-DxiNidxSEK+tHG6zOIklvNOwm3hvCrbUrdtzY74U6HKTJxvIDfOUL5W5P2Ghd3DTkhhKPYGqeNUIh5qcM4YBfw==", - "dev": true, - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" + "node": ">=12.0.0" }, - "engines": { - "node": ">=8" - } - }, - "node_modules/tar/node_modules/minipass": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-5.0.0.tgz", - "integrity": "sha512-3FnjYuehv9k6ovOEbyOswadCDPX1piCfhV8ncmYtHOjuPwylVWsghTLo7rabjC3Rx5xD4HDx8Wm1xnMF7S5qFQ==", - "dev": true, - "license": "ISC", - "engines": { - "node": ">=8" + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" } }, "node_modules/unique-filename": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-3.0.0.tgz", - "integrity": "sha512-afXhuC55wkAmZ0P18QsVE6kp8JaxrEokN2HGIoIVv2ijHQd419H0+6EigAFcIzXeMIkcIkNBpB3L/DXB3cTS/g==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/unique-filename/-/unique-filename-4.0.0.tgz", + "integrity": "sha512-XSnEewXmQ+veP7xX2dS5Q4yZAvO40cBN2MWkJ7D/6sW4Dg6wYBNwM1Vrnz1FhH5AdeLIlUXRI9e28z1YZi71NQ==", "dev": true, "license": "ISC", "dependencies": { - "unique-slug": "^4.0.0" + "unique-slug": "^5.0.0" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/unique-slug": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-4.0.0.tgz", - "integrity": "sha512-WrcA6AyEfqDX5bWige/4NQfPZMtASNVxdmWR76WESYQVAACSgWcR6e9i0mofqqBxYFtL4oAxPIptY73/0YE1DQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/unique-slug/-/unique-slug-5.0.0.tgz", + "integrity": "sha512-9OdaqO5kwqR+1kVgHAhsp5vPNU0hnxRa26rBFNfNgM7M6pNtgzeBn3s/xbyCQL3dcjzOatcef6UUHpB/6MaETg==", "dev": true, "license": "ISC", "dependencies": { "imurmurhash": "^0.1.4" }, "engines": { - "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + "node": "^18.17.0 || >=20.5.0" } }, "node_modules/which": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", - "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.0.tgz", + "integrity": "sha512-f+gEpIKMR9faW/JgAgPK1D7mekkFoqbmiwvNzuhsHetni20QSgzg9Vhn0g2JSJkkfehQnqdUAx7/e15qS1lPxg==", "dev": true, "license": "ISC", "dependencies": { @@ -1209,7 +1126,7 @@ "node-which": "bin/which.js" }, "engines": { - "node": "^16.13.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/wrap-ansi": { @@ -1311,11 +1228,14 @@ } }, "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-5.0.0.tgz", + "integrity": "sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==", "dev": true, - "license": "ISC" + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=18" + } } } } diff --git a/build/npm/gyp/package.json b/build/npm/gyp/package.json index a1564133a1ee..1b869649d321 100644 --- a/build/npm/gyp/package.json +++ b/build/npm/gyp/package.json @@ -4,7 +4,7 @@ "private": true, "license": "MIT", "devDependencies": { - "node-gyp": "^10.1.0" + "node-gyp": "^12.1.0" }, "scripts": {} } diff --git a/build/npm/postinstall.js b/build/npm/postinstall.js index c1f22aa50024..458847afac58 100644 --- a/build/npm/postinstall.js +++ b/build/npm/postinstall.js @@ -85,7 +85,7 @@ function setNpmrcConfig(dir, env) { // the correct clang variable. So keep the version check // in preinstall sync with this logic. // Change was first introduced in https://github.com/nodejs/node/commit/6e0a2bb54c5bbeff0e9e33e1a0c683ed980a8a0f - if (dir === 'remote' && process.platform === 'darwin') { + if ((dir === 'remote' || dir === 'build') && process.platform === 'darwin') { env['npm_config_force_process_config'] = 'true'; } else { delete env['npm_config_force_process_config']; diff --git a/build/package-lock.json b/build/package-lock.json index aa939e40375f..b89cf7bba076 100644 --- a/build/package-lock.json +++ b/build/package-lock.json @@ -9,92 +9,100 @@ "version": "1.0.0", "license": "MIT", "devDependencies": { - "@azure/core-auth": "^1.9.0", - "@azure/cosmos": "^3", - "@azure/identity": "^4.2.1", - "@azure/msal-node": "^2.16.1", - "@azure/storage-blob": "^12.25.0", - "@electron/get": "^2.0.0", - "@types/ansi-colors": "^3.2.0", - "@types/byline": "^4.2.32", - "@types/debounce": "^1.0.0", - "@types/debug": "^4.1.5", - "@types/fancy-log": "^1.3.0", - "@types/fs-extra": "^9.0.12", - "@types/glob": "^7.1.1", - "@types/gulp": "^4.0.17", - "@types/gulp-filter": "^3.0.32", - "@types/gulp-gzip": "^0.0.31", - "@types/gulp-json-editor": "^2.2.31", - "@types/gulp-rename": "^0.0.33", + "@azure/core-auth": "^1.10.1", + "@azure/cosmos": "^4", + "@azure/identity": "^4.13.0", + "@azure/msal-node": "^5.0.2", + "@azure/storage-blob": "^12.30.0", + "@electron/get": "^4.0.2", + "@types/ansi-colors": "^3.2.6", + "@types/byline": "^4.2.36", + "@types/debounce": "^1.2.4", + "@types/debug": "^4.1.12", + "@types/fancy-log": "^2.0.2", + "@types/fs-extra": "^11.0.4", + "@types/glob": "^9.0.0", + "@types/gulp": "^4.0.18", + "@types/gulp-filter": "^3.0.41", + "@types/gulp-gzip": "^0.0.36", + "@types/gulp-json-editor": "^2.2.36", + "@types/gulp-rename": "^2.0.7", "@types/gulp-sort": "^2.0.4", - "@types/gulp-sourcemaps": "^0.0.32", - "@types/jws": "^3.2.10", - "@types/mime": "0.0.29", - "@types/minimatch": "^3.0.3", - "@types/minimist": "^1.2.1", - "@types/mocha": "^9.1.1", - "@types/node": "20.x", - "@types/pump": "^1.0.1", - "@types/rimraf": "^2.0.4", - "@types/through": "^0.0.29", - "@types/through2": "^2.0.36", - "@types/workerpool": "^6.4.0", - "@types/xml2js": "0.0.33", - "@vscode/iconv-lite-umd": "0.7.0", - "@vscode/ripgrep": "^1.15.10", - "@vscode/vsce": "2.20.1", + "@types/gulp-sourcemaps": "^0.0.38", + "@types/jws": "^3.2.11", + "@types/mime": "4.0.0", + "@types/minimatch": "^6.0.0", + "@types/minimist": "^1.2.5", + "@types/mocha": "^10.0.10", + "@types/node": "25.x", + "@types/pump": "^1.1.3", + "@types/rimraf": "^4.0.5", + "@types/through": "^0.0.33", + "@types/through2": "^2.0.41", + "@types/workerpool": "^9.0.0", + "@types/xml2js": "0.4.14", + "@vscode/iconv-lite-umd": "0.7.1", + "@vscode/ripgrep": "^1.17.0", + "@vscode/vsce": "3.7.1", "byline": "^5.0.0", "debug": "^4.3.2", - "electron-osx-sign": "^0.4.16", - "esbuild": "0.25.0", + "electron-osx-sign": "^0.6.0", + "esbuild": "0.27.2", "extract-zip": "^2.0.1", - "gulp-merge-json": "^2.1.1", + "gulp-merge-json": "^2.2.1", "gulp-sort": "^2.0.0", - "jsonc-parser": "^2.3.0", - "jws": "^4.0.0", - "mime": "^1.4.1", - "source-map": "0.6.1", + "jsonc-parser": "^3.3.1", + "jws": "^4.0.1", + "mime": "^4.1.0", + "source-map": "0.7.6", "ternary-stream": "^3.0.0", "through2": "^4.0.2", - "tree-sitter": "^0.20.5", + "tree-sitter": "^0.25.0", "vscode-universal-bundler": "^0.1.3", - "workerpool": "^6.4.0", - "yauzl": "^2.10.0" + "workerpool": "^10.0.1", + "yauzl": "^3.2.0" }, "optionalDependencies": { - "tree-sitter-typescript": "^0.20.5", + "tree-sitter-typescript": "^0.23.2", "vscode-gulp-watch": "^5.0.3" } }, - "node_modules/@azure/abort-controller": { + "node_modules/@azu/format-text": { "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-1.0.2.tgz", - "integrity": "sha512-XUyTo+bcyxHEf+jlN2MXA7YU9nxVehaubngHV1MIZZaqYmZqykkoeAz/JMMEeR7t3TcyDwbFa3Zw8BZywmIx4g==", + "resolved": "https://registry.npmjs.org/@azu/format-text/-/format-text-1.0.2.tgz", + "integrity": "sha512-Swi4N7Edy1Eqq82GxgEECXSSLyn6GOb5htRFPzBDdUkECGXtlf12ynO5oJSpWKPwCaUssOu7NfhDcCWpIC6Ywg==", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@azu/style-format": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@azu/style-format/-/style-format-1.0.1.tgz", + "integrity": "sha512-AHcTojlNBdD/3/KxIKlg8sxIWHfOtQszLvOpagLTO+bjC3u7SAszu1lf//u7JJC50aUSH+BVWDD/KvaA6Gfn5g==", "dev": true, + "license": "WTFPL", "dependencies": { - "tslib": "^2.0.0" - }, - "engines": { - "node": ">=8.0.0" + "@azu/format-text": "^1.0.1" } }, - "node_modules/@azure/core-auth": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.9.0.tgz", - "integrity": "sha512-FPwHpZywuyasDSLMqJ6fhbOK3TqUdviZNF8OqRGA4W5Ewib2lEEZ+pBsYcBa88B2NGO/SEnYPGhyBqNlE8ilSw==", + "node_modules/@azure-rest/core-client": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@azure-rest/core-client/-/core-client-2.5.1.tgz", + "integrity": "sha512-EHaOXW0RYDKS5CFffnixdyRPak5ytiCtU7uXDcP/uiY+A6jFRwNGzzJBiznkCzvi5EYpY+YWinieqHb0oY916A==", "dev": true, "license": "MIT", "dependencies": { - "@azure/abort-controller": "^2.0.0", - "@azure/core-util": "^1.11.0", + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.10.0", + "@azure/core-rest-pipeline": "^1.22.0", + "@azure/core-tracing": "^1.3.0", + "@typespec/ts-http-runtime": "^0.3.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, - "node_modules/@azure/core-auth/node_modules/@azure/abort-controller": { + "node_modules/@azure/abort-controller": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", @@ -107,64 +115,53 @@ "node": ">=18.0.0" } }, - "node_modules/@azure/core-client": { - "version": "1.9.2", - "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.9.2.tgz", - "integrity": "sha512-kRdry/rav3fUKHl/aDLd/pDLcB+4pOFwPPTVEExuMyaI5r+JBbMWqRbCY1pn5BniDaU3lRxO9eaQ1AmSMehl/w==", + "node_modules/@azure/core-auth": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.10.1.tgz", + "integrity": "sha512-ykRMW8PjVAn+RS6ww5cmK9U2CyH9p4Q88YJwvUslfuMmN98w/2rdGRLPqJYObapBCdzBVeDgYWdJnFPFb7qzpg==", "dev": true, "license": "MIT", "dependencies": { - "@azure/abort-controller": "^2.0.0", - "@azure/core-auth": "^1.4.0", - "@azure/core-rest-pipeline": "^1.9.1", - "@azure/core-tracing": "^1.0.0", - "@azure/core-util": "^1.6.1", - "@azure/logger": "^1.0.0", + "@azure/abort-controller": "^2.1.2", + "@azure/core-util": "^1.13.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, - "node_modules/@azure/core-client/node_modules/@azure/abort-controller": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", - "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "node_modules/@azure/core-client": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.10.1.tgz", + "integrity": "sha512-Nh5PhEOeY6PrnxNPsEHRr9eimxLwgLlpmguQaHKBinFYA/RU9+kOYVOQqOrTsCL+KSxrLLl1gD8Dk5BFW/7l/w==", "dev": true, "license": "MIT", "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.10.0", + "@azure/core-rest-pipeline": "^1.22.0", + "@azure/core-tracing": "^1.3.0", + "@azure/core-util": "^1.13.0", + "@azure/logger": "^1.3.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@azure/core-http-compat": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@azure/core-http-compat/-/core-http-compat-2.1.2.tgz", - "integrity": "sha512-5MnV1yqzZwgNLLjlizsU3QqOeQChkIXw781Fwh1xdAqJR5AA32IUaq6xv1BICJvfbHoa+JYcaij2HFkhLbNTJQ==", - "dev": true, - "license": "MIT", - "dependencies": { - "@azure/abort-controller": "^2.0.0", - "@azure/core-client": "^1.3.0", - "@azure/core-rest-pipeline": "^1.3.0" - }, - "engines": { - "node": ">=18.0.0" - } - }, - "node_modules/@azure/core-http-compat/node_modules/@azure/abort-controller": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", - "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/@azure/core-http-compat/-/core-http-compat-2.3.1.tgz", + "integrity": "sha512-az9BkXND3/d5VgdRRQVkiJb2gOmDU8Qcq4GvjtBmDICNiQ9udFmDk4ZpSB5Qq1OmtDJGlQAfBaS4palFsazQ5g==", "dev": true, "license": "MIT", "dependencies": { - "tslib": "^2.6.2" + "@azure/abort-controller": "^2.1.2", + "@azure/core-client": "^1.10.0", + "@azure/core-rest-pipeline": "^1.22.0" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, "node_modules/@azure/core-lro": { @@ -183,10 +180,10 @@ "node": ">=18.0.0" } }, - "node_modules/@azure/core-lro/node_modules/@azure/abort-controller": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", - "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "node_modules/@azure/core-paging": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.6.2.tgz", + "integrity": "sha512-YKWi9YuCU04B55h25cnOYZHxXYtEvQEbKST5vqRga7hWY9ydd3FZHdeQF8pyh+acWZvppw13M/LMGx0LABUVMA==", "dev": true, "license": "MIT", "dependencies": { @@ -196,206 +193,238 @@ "node": ">=18.0.0" } }, - "node_modules/@azure/core-paging": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/@azure/core-paging/-/core-paging-1.6.2.tgz", - "integrity": "sha512-YKWi9YuCU04B55h25cnOYZHxXYtEvQEbKST5vqRga7hWY9ydd3FZHdeQF8pyh+acWZvppw13M/LMGx0LABUVMA==", + "node_modules/@azure/core-rest-pipeline": { + "version": "1.22.2", + "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.22.2.tgz", + "integrity": "sha512-MzHym+wOi8CLUlKCQu12de0nwcq9k9Kuv43j4Wa++CsCpJwps2eeBQwD2Bu8snkxTtDKDx4GwjuR9E8yC8LNrg==", "dev": true, "license": "MIT", "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.10.0", + "@azure/core-tracing": "^1.3.0", + "@azure/core-util": "^1.13.0", + "@azure/logger": "^1.3.0", + "@typespec/ts-http-runtime": "^0.3.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, - "node_modules/@azure/core-rest-pipeline": { - "version": "1.18.0", - "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.18.0.tgz", - "integrity": "sha512-QSoGUp4Eq/gohEFNJaUOwTN7BCc2nHTjjbm75JT0aD7W65PWM1H/tItz0GsABn22uaKyGxiMhWQLt2r+FGU89Q==", + "node_modules/@azure/core-tracing": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.3.1.tgz", + "integrity": "sha512-9MWKevR7Hz8kNzzPLfX4EAtGM2b8mr50HPDBvio96bURP/9C+HjdH3sBlLSNNrvRAr5/k/svoH457gB5IKpmwQ==", "dev": true, "license": "MIT", "dependencies": { - "@azure/abort-controller": "^2.0.0", - "@azure/core-auth": "^1.8.0", - "@azure/core-tracing": "^1.0.1", - "@azure/core-util": "^1.11.0", - "@azure/logger": "^1.0.0", - "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, - "node_modules/@azure/core-rest-pipeline/node_modules/@azure/abort-controller": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", - "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "node_modules/@azure/core-util": { + "version": "1.13.1", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.13.1.tgz", + "integrity": "sha512-XPArKLzsvl0Hf0CaGyKHUyVgF7oDnhKoP85Xv6M4StF/1AhfORhZudHtOyf2s+FcbuQ9dPRAjB8J2KvRRMUK2A==", "dev": true, "license": "MIT", "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@typespec/ts-http-runtime": "^0.3.0", "tslib": "^2.6.2" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, - "node_modules/@azure/core-tracing": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.2.0.tgz", - "integrity": "sha512-UKTiEJPkWcESPYJz3X5uKRYyOcJD+4nYph+KpfdPRnQJVrZfk0KJgdnaAWKfhsBBtAf/D58Az4AvCJEmWgIBAg==", + "node_modules/@azure/core-xml": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/@azure/core-xml/-/core-xml-1.5.0.tgz", + "integrity": "sha512-D/sdlJBMJfx7gqoj66PKVmhDDaU6TKA49ptcolxdas29X7AfvLTmfAGLjAcIMBK7UZ2o4lygHIqVckOlQU3xWw==", "dev": true, "license": "MIT", "dependencies": { - "tslib": "^2.6.2" + "fast-xml-parser": "^5.0.7", + "tslib": "^2.8.1" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, - "node_modules/@azure/core-util": { - "version": "1.11.0", - "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.11.0.tgz", - "integrity": "sha512-DxOSLua+NdpWoSqULhjDyAZTXFdP/LKkqtYuxxz1SCN289zk3OG8UOpnCQAz/tygyACBtWp/BoO72ptK7msY8g==", + "node_modules/@azure/cosmos": { + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/@azure/cosmos/-/cosmos-4.9.0.tgz", + "integrity": "sha512-3LzLibuVgbhWEv6B9Hz7bMn0KAKKlr3Rn9xPulZ6Xx69GuwgWmQ4qmkrfYBBBFCJDQXoVAvVGBYkt9VlW8hmOg==", "dev": true, "license": "MIT", "dependencies": { - "@azure/abort-controller": "^2.0.0", - "tslib": "^2.6.2" + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.9.0", + "@azure/core-rest-pipeline": "^1.19.1", + "@azure/core-tracing": "^1.2.0", + "@azure/core-util": "^1.11.0", + "@azure/keyvault-keys": "^4.9.0", + "@azure/logger": "^1.1.4", + "fast-json-stable-stringify": "^2.1.0", + "priorityqueuejs": "^2.0.0", + "semaphore": "^1.1.0", + "tslib": "^2.8.1" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, - "node_modules/@azure/core-util/node_modules/@azure/abort-controller": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", - "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "node_modules/@azure/identity": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.13.0.tgz", + "integrity": "sha512-uWC0fssc+hs1TGGVkkghiaFkkS7NkTxfnCH+Hdg+yTehTpMcehpok4PgUKKdyCH+9ldu6FhiHRv84Ntqj1vVcw==", "dev": true, + "license": "MIT", "dependencies": { - "tslib": "^2.6.2" + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.9.0", + "@azure/core-client": "^1.9.2", + "@azure/core-rest-pipeline": "^1.17.0", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.11.0", + "@azure/logger": "^1.0.0", + "@azure/msal-browser": "^4.2.0", + "@azure/msal-node": "^3.5.0", + "open": "^10.1.0", + "tslib": "^2.2.0" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, - "node_modules/@azure/core-xml": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/@azure/core-xml/-/core-xml-1.4.4.tgz", - "integrity": "sha512-J4FYAqakGXcbfeZjwjMzjNcpcH4E+JtEBv+xcV1yL0Ydn/6wbQfeFKTCHh9wttAi0lmajHw7yBbHPRG+YHckZQ==", + "node_modules/@azure/identity/node_modules/@azure/msal-common": { + "version": "15.14.1", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-15.14.1.tgz", + "integrity": "sha512-IkzF7Pywt6QKTS0kwdCv/XV8x8JXknZDvSjj/IccooxnP373T5jaadO3FnOrbWo3S0UqkfIDyZNTaQ/oAgRdXw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/identity/node_modules/@azure/msal-node": { + "version": "3.8.6", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-3.8.6.tgz", + "integrity": "sha512-XTmhdItcBckcVVTy65Xp+42xG4LX5GK+9AqAsXPXk4IqUNv+LyQo5TMwNjuFYBfAB2GTG9iSQGk+QLc03vhf3w==", "dev": true, "license": "MIT", "dependencies": { - "fast-xml-parser": "^4.4.1", - "tslib": "^2.6.2" + "@azure/msal-common": "15.14.1", + "jsonwebtoken": "^9.0.0", + "uuid": "^8.3.0" }, "engines": { - "node": ">=18.0.0" + "node": ">=16" } }, - "node_modules/@azure/cosmos": { - "version": "3.17.3", - "resolved": "https://registry.npmjs.org/@azure/cosmos/-/cosmos-3.17.3.tgz", - "integrity": "sha512-wBglkQ6Irjv5Vo2iw8fd6eYj60WYRSSg4/0DBkeOP6BwQ4RA91znsOHd6s3qG6UAbNgYuzC9Nnq07vlFFZkHEw==", + "node_modules/@azure/keyvault-common": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@azure/keyvault-common/-/keyvault-common-2.0.0.tgz", + "integrity": "sha512-wRLVaroQtOqfg60cxkzUkGKrKMsCP6uYXAOomOIysSMyt1/YM0eUn9LqieAWM8DLcU4+07Fio2YGpPeqUbpP9w==", "dev": true, + "license": "MIT", "dependencies": { - "@azure/abort-controller": "^1.0.0", + "@azure/abort-controller": "^2.0.0", "@azure/core-auth": "^1.3.0", - "@azure/core-rest-pipeline": "^1.2.0", + "@azure/core-client": "^1.5.0", + "@azure/core-rest-pipeline": "^1.8.0", "@azure/core-tracing": "^1.0.0", - "debug": "^4.1.1", - "fast-json-stable-stringify": "^2.1.0", - "jsbi": "^3.1.3", - "node-abort-controller": "^3.0.0", - "priorityqueuejs": "^1.0.0", - "semaphore": "^1.0.5", - "tslib": "^2.2.0", - "universal-user-agent": "^6.0.0", - "uuid": "^8.3.0" + "@azure/core-util": "^1.10.0", + "@azure/logger": "^1.1.4", + "tslib": "^2.2.0" }, "engines": { - "node": ">=14.0.0" + "node": ">=18.0.0" } }, - "node_modules/@azure/identity": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.2.1.tgz", - "integrity": "sha512-U8hsyC9YPcEIzoaObJlRDvp7KiF0MGS7xcWbyJSVvXRkC/HXo1f0oYeBYmEvVgRfacw7GHf6D6yAoh9JHz6A5Q==", + "node_modules/@azure/keyvault-keys": { + "version": "4.10.0", + "resolved": "https://registry.npmjs.org/@azure/keyvault-keys/-/keyvault-keys-4.10.0.tgz", + "integrity": "sha512-eDT7iXoBTRZ2n3fLiftuGJFD+yjkiB1GNqzU2KbY1TLYeXeSPVTVgn2eJ5vmRTZ11978jy2Kg2wI7xa9Tyr8ag==", "dev": true, + "license": "MIT", "dependencies": { - "@azure/abort-controller": "^1.0.0", - "@azure/core-auth": "^1.5.0", - "@azure/core-client": "^1.4.0", - "@azure/core-rest-pipeline": "^1.1.0", - "@azure/core-tracing": "^1.0.0", - "@azure/core-util": "^1.3.0", - "@azure/logger": "^1.0.0", - "@azure/msal-browser": "^3.11.1", - "@azure/msal-node": "^2.9.2", - "events": "^3.0.0", - "jws": "^4.0.0", - "open": "^8.0.0", - "stoppable": "^1.1.0", - "tslib": "^2.2.0" + "@azure-rest/core-client": "^2.3.3", + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.9.0", + "@azure/core-http-compat": "^2.2.0", + "@azure/core-lro": "^2.7.2", + "@azure/core-paging": "^1.6.2", + "@azure/core-rest-pipeline": "^1.19.0", + "@azure/core-tracing": "^1.2.0", + "@azure/core-util": "^1.11.0", + "@azure/keyvault-common": "^2.0.0", + "@azure/logger": "^1.1.4", + "tslib": "^2.8.1" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@azure/logger": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.0.1.tgz", - "integrity": "sha512-QYQeaJ+A5x6aMNu8BG5qdsVBnYBop9UMwgUvGihSjf1PdZZXB+c/oMdM2ajKwzobLBh9e9QuMQkN9iL+IxLBLA==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.3.0.tgz", + "integrity": "sha512-fCqPIfOcLE+CGqGPd66c8bZpwAji98tZ4JI9i/mlTNTlsIWslCfpg48s/ypyLxZTump5sypjrKn2/kY7q8oAbA==", "dev": true, + "license": "MIT", "dependencies": { - "tslib": "^2.0.0" + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" }, "engines": { - "node": ">=8.0.0" + "node": ">=20.0.0" } }, "node_modules/@azure/msal-browser": { - "version": "3.17.0", - "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-3.17.0.tgz", - "integrity": "sha512-csccKXmW2z7EkZ0I3yAoW/offQt+JECdTIV/KrnRoZyM7wCSsQWODpwod8ZhYy7iOyamcHApR9uCh0oD1M+0/A==", + "version": "4.27.0", + "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-4.27.0.tgz", + "integrity": "sha512-bZ8Pta6YAbdd0o0PEaL1/geBsPrLEnyY/RDWqvF1PP9RUH8EMLvUMGoZFYS6jSlUan6KZ9IMTLCnwpWWpQRK/w==", "dev": true, + "license": "MIT", "dependencies": { - "@azure/msal-common": "14.12.0" + "@azure/msal-common": "15.13.3" }, "engines": { "node": ">=0.8.0" } }, "node_modules/@azure/msal-common": { - "version": "14.12.0", - "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.12.0.tgz", - "integrity": "sha512-IDDXmzfdwmDkv4SSmMEyAniJf6fDu3FJ7ncOjlxkDuT85uSnLEhZi3fGZpoR7T4XZpOMx9teM9GXBgrfJgyeBw==", + "version": "15.13.3", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-15.13.3.tgz", + "integrity": "sha512-shSDU7Ioecya+Aob5xliW9IGq1Ui8y4EVSdWGyI1Gbm4Vg61WpP95LuzcY214/wEjSn6w4PZYD4/iVldErHayQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8.0" } }, "node_modules/@azure/msal-node": { - "version": "2.16.1", - "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-2.16.1.tgz", - "integrity": "sha512-1NEFpTmMMT2A7RnZuvRl/hUmJU+GLPjh+ShyIqPktG2PvSd2yvPnzGd/BxIBAAvJG5nr9lH4oYcQXepDbaE7fg==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-5.0.2.tgz", + "integrity": "sha512-3tHeJghckgpTX98TowJoXOjKGuds0L+FKfeHJtoZFl2xvwE6RF65shZJzMQ5EQZWXzh3sE1i9gE+m3aRMachjA==", "dev": true, "license": "MIT", "dependencies": { - "@azure/msal-common": "14.16.0", + "@azure/msal-common": "16.0.2", "jsonwebtoken": "^9.0.0", "uuid": "^8.3.0" }, "engines": { - "node": ">=16" + "node": ">=20" } }, "node_modules/@azure/msal-node/node_modules/@azure/msal-common": { - "version": "14.16.0", - "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.16.0.tgz", - "integrity": "sha512-1KOZj9IpcDSwpNiQNjt0jDYZpQvNZay7QAEi/5DLubay40iGYtLzya/jbjRPLyOTZhEKyL1MzPuw2HqBCjceYA==", + "version": "16.0.2", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-16.0.2.tgz", + "integrity": "sha512-ZJ/UR7lyqIntURrIJCyvScwJFanM9QhJYcJCheB21jZofGKpP9QxWgvADANo7UkresHKzV+6YwoeZYP7P7HvUg==", "dev": true, "license": "MIT", "engines": { @@ -403,41 +432,75 @@ } }, "node_modules/@azure/storage-blob": { - "version": "12.25.0", - "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.25.0.tgz", - "integrity": "sha512-oodouhA3nCCIh843tMMbxty3WqfNT+Vgzj3Xo5jqR9UPnzq3d7mzLjlHAYz7lW+b4km3SIgz+NAgztvhm7Z6kQ==", + "version": "12.30.0", + "resolved": "https://registry.npmjs.org/@azure/storage-blob/-/storage-blob-12.30.0.tgz", + "integrity": "sha512-peDCR8blSqhsAKDbpSP/o55S4sheNwSrblvCaHUZ5xUI73XA7ieUGGwrONgD/Fng0EoDe1VOa3fAQ7+WGB3Ocg==", "dev": true, "license": "MIT", "dependencies": { "@azure/abort-controller": "^2.1.2", - "@azure/core-auth": "^1.4.0", - "@azure/core-client": "^1.6.2", - "@azure/core-http-compat": "^2.0.0", + "@azure/core-auth": "^1.9.0", + "@azure/core-client": "^1.9.3", + "@azure/core-http-compat": "^2.2.0", "@azure/core-lro": "^2.2.0", - "@azure/core-paging": "^1.1.1", - "@azure/core-rest-pipeline": "^1.10.1", - "@azure/core-tracing": "^1.1.2", - "@azure/core-util": "^1.6.1", - "@azure/core-xml": "^1.4.3", - "@azure/logger": "^1.0.0", + "@azure/core-paging": "^1.6.2", + "@azure/core-rest-pipeline": "^1.19.1", + "@azure/core-tracing": "^1.2.0", + "@azure/core-util": "^1.11.0", + "@azure/core-xml": "^1.4.5", + "@azure/logger": "^1.1.4", + "@azure/storage-common": "^12.2.0", "events": "^3.0.0", - "tslib": "^2.2.0" + "tslib": "^2.8.1" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" } }, - "node_modules/@azure/storage-blob/node_modules/@azure/abort-controller": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", - "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", + "node_modules/@azure/storage-common": { + "version": "12.2.0", + "resolved": "https://registry.npmjs.org/@azure/storage-common/-/storage-common-12.2.0.tgz", + "integrity": "sha512-YZLxiJ3vBAAnFbG3TFuAMUlxZRexjQX5JDQxOkFGb6e2TpoxH3xyHI6idsMe/QrWtj41U/KoqBxlayzhS+LlwA==", "dev": true, "license": "MIT", "dependencies": { - "tslib": "^2.6.2" + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.9.0", + "@azure/core-http-compat": "^2.2.0", + "@azure/core-rest-pipeline": "^1.19.1", + "@azure/core-tracing": "^1.2.0", + "@azure/core-util": "^1.11.0", + "@azure/logger": "^1.1.4", + "events": "^3.3.0", + "tslib": "^2.8.1" }, "engines": { - "node": ">=18.0.0" + "node": ">=20.0.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", + "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/helper-validator-identifier": "^7.27.1", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" } }, "node_modules/@electron/asar": { @@ -467,30 +530,31 @@ } }, "node_modules/@electron/get": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/@electron/get/-/get-2.0.3.tgz", - "integrity": "sha512-Qkzpg2s9GnVV2I2BjRksUi43U5e6+zaQMcjoJy0C+C5oxaKl+fmckGDQFtRpZpZV0NQekuZZ+tGz7EA9TVnQtQ==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-4.0.2.tgz", + "integrity": "sha512-n9fRt/nzzOOZdDtTP3kT6GVdo0ro9FgMKCoS520kQMIiKBhpGmPny6yK/lER3tOCKr+wLYW1O25D9oI6ZinwCA==", "dev": true, + "license": "MIT", "dependencies": { "debug": "^4.1.1", - "env-paths": "^2.2.0", - "fs-extra": "^8.1.0", - "got": "^11.8.5", + "env-paths": "^3.0.0", + "got": "^14.4.5", + "graceful-fs": "^4.2.11", "progress": "^2.0.3", - "semver": "^6.2.0", + "semver": "^7.6.3", "sumchecker": "^3.0.1" }, "engines": { - "node": ">=12" + "node": ">=22.12.0" }, "optionalDependencies": { "global-agent": "^3.0.0" } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz", - "integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", + "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==", "cpu": [ "ppc64" ], @@ -505,9 +569,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz", - "integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz", + "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==", "cpu": [ "arm" ], @@ -522,9 +586,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz", - "integrity": "sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz", + "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==", "cpu": [ "arm64" ], @@ -539,9 +603,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.0.tgz", - "integrity": "sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz", + "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==", "cpu": [ "x64" ], @@ -556,9 +620,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz", - "integrity": "sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz", + "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==", "cpu": [ "arm64" ], @@ -573,9 +637,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz", - "integrity": "sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz", + "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==", "cpu": [ "x64" ], @@ -590,9 +654,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz", - "integrity": "sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz", + "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==", "cpu": [ "arm64" ], @@ -607,9 +671,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz", - "integrity": "sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz", + "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==", "cpu": [ "x64" ], @@ -624,9 +688,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz", - "integrity": "sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz", + "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==", "cpu": [ "arm" ], @@ -641,9 +705,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz", - "integrity": "sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz", + "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==", "cpu": [ "arm64" ], @@ -658,9 +722,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz", - "integrity": "sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz", + "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==", "cpu": [ "ia32" ], @@ -675,9 +739,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz", - "integrity": "sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz", + "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==", "cpu": [ "loong64" ], @@ -692,9 +756,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz", - "integrity": "sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz", + "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==", "cpu": [ "mips64el" ], @@ -709,9 +773,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz", - "integrity": "sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz", + "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==", "cpu": [ "ppc64" ], @@ -726,9 +790,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz", - "integrity": "sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz", + "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==", "cpu": [ "riscv64" ], @@ -743,9 +807,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz", - "integrity": "sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz", + "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==", "cpu": [ "s390x" ], @@ -760,9 +824,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz", - "integrity": "sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz", + "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==", "cpu": [ "x64" ], @@ -777,9 +841,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz", - "integrity": "sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz", + "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==", "cpu": [ "arm64" ], @@ -794,9 +858,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz", - "integrity": "sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz", + "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==", "cpu": [ "x64" ], @@ -811,9 +875,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz", - "integrity": "sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz", + "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==", "cpu": [ "arm64" ], @@ -828,9 +892,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz", - "integrity": "sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz", + "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==", "cpu": [ "x64" ], @@ -844,10 +908,27 @@ "node": ">=18" } }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz", + "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/sunos-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz", - "integrity": "sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz", + "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==", "cpu": [ "x64" ], @@ -862,9 +943,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz", - "integrity": "sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz", + "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==", "cpu": [ "arm64" ], @@ -879,9 +960,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz", - "integrity": "sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz", + "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==", "cpu": [ "ia32" ], @@ -896,9 +977,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz", - "integrity": "sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz", + "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==", "cpu": [ "x64" ], @@ -912,6 +993,79 @@ "node": ">=18" } }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "license": "ISC", + "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" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@keyv/serialize": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.1.1.tgz", + "integrity": "sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==", + "dev": true, + "license": "MIT" + }, "node_modules/@malept/cross-spawn-promise": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-2.0.0.tgz", @@ -934,68 +1088,445 @@ "node": ">= 12.13.0" } }, - "node_modules/@sindresorhus/is": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", - "integrity": "sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==", - "dev": true, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sindresorhus/is?sponsor=1" - } - }, - "node_modules/@szmarczak/http-timer": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-4.0.6.tgz", - "integrity": "sha512-4BAffykYOgO+5nzBWYwE3W90sBgLJoUPRWWcL8wlyiM8IB8ipJz3UMJ9KXQd1RKQXpKp8Tutn80HZtWsu2u76w==", + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, + "license": "MIT", "dependencies": { - "defer-to-connect": "^2.0.0" + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" }, "engines": { - "node": ">=10" + "node": ">= 8" } }, - "node_modules/@types/ansi-colors": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/@types/ansi-colors/-/ansi-colors-3.2.0.tgz", - "integrity": "sha512-0caWAhXht9N2lOdMzJLXybsSkYCx1QOdxx6pae48tswI9QV3DFX26AoOpy0JxwhCb+zISTqmd6H8t9Zby9BoZg==", - "dev": true + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } }, - "node_modules/@types/byline": { - "version": "4.2.32", - "resolved": "https://registry.npmjs.org/@types/byline/-/byline-4.2.32.tgz", - "integrity": "sha512-qtlm/J6XOO9p+Ep/ZB5+mCFEDhzWDDHWU4a1eReN7lkPZXW9rkloq2jcAhvKKmlO5tL2GSvKROb+PTsNVhBiyQ==", + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, + "license": "MIT", "dependencies": { - "@types/node": "*" + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" } }, - "node_modules/@types/cacheable-request": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/@types/cacheable-request/-/cacheable-request-6.0.3.tgz", - "integrity": "sha512-IQ3EbTzGxIigb1I3qPZc1rWJnH0BmSKv5QYTalEwweFvyBDLSAe24zP0le/hyi7ecGfZVlIVAg4BZqb8WBwKqw==", + "node_modules/@sec-ant/readable-stream": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", + "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", "dev": true, + "license": "MIT" + }, + "node_modules/@secretlint/config-creator": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/config-creator/-/config-creator-10.2.2.tgz", + "integrity": "sha512-BynOBe7Hn3LJjb3CqCHZjeNB09s/vgf0baBaHVw67w7gHF0d25c3ZsZ5+vv8TgwSchRdUCRrbbcq5i2B1fJ2QQ==", + "dev": true, + "license": "MIT", "dependencies": { - "@types/http-cache-semantics": "*", - "@types/keyv": "^3.1.4", - "@types/node": "*", - "@types/responselike": "^1.0.0" + "@secretlint/types": "^10.2.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/config-loader": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/config-loader/-/config-loader-10.2.2.tgz", + "integrity": "sha512-ndjjQNgLg4DIcMJp4iaRD6xb9ijWQZVbd9694Ol2IszBIbGPPkwZHzJYKICbTBmh6AH/pLr0CiCaWdGJU7RbpQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/profiler": "^10.2.2", + "@secretlint/resolver": "^10.2.2", + "@secretlint/types": "^10.2.2", + "ajv": "^8.17.1", + "debug": "^4.4.1", + "rc-config-loader": "^4.1.3" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/core": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/core/-/core-10.2.2.tgz", + "integrity": "sha512-6rdwBwLP9+TO3rRjMVW1tX+lQeo5gBbxl1I5F8nh8bgGtKwdlCMhMKsBWzWg1ostxx/tIG7OjZI0/BxsP8bUgw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/profiler": "^10.2.2", + "@secretlint/types": "^10.2.2", + "debug": "^4.4.1", + "structured-source": "^4.0.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/formatter": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/formatter/-/formatter-10.2.2.tgz", + "integrity": "sha512-10f/eKV+8YdGKNQmoDUD1QnYL7TzhI2kzyx95vsJKbEa8akzLAR5ZrWIZ3LbcMmBLzxlSQMMccRmi05yDQ5YDA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/resolver": "^10.2.2", + "@secretlint/types": "^10.2.2", + "@textlint/linter-formatter": "^15.2.0", + "@textlint/module-interop": "^15.2.0", + "@textlint/types": "^15.2.0", + "chalk": "^5.4.1", + "debug": "^4.4.1", + "pluralize": "^8.0.0", + "strip-ansi": "^7.1.0", + "table": "^6.9.0", + "terminal-link": "^4.0.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/formatter/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@secretlint/node": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/node/-/node-10.2.2.tgz", + "integrity": "sha512-eZGJQgcg/3WRBwX1bRnss7RmHHK/YlP/l7zOQsrjexYt6l+JJa5YhUmHbuGXS94yW0++3YkEJp0kQGYhiw1DMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/config-loader": "^10.2.2", + "@secretlint/core": "^10.2.2", + "@secretlint/formatter": "^10.2.2", + "@secretlint/profiler": "^10.2.2", + "@secretlint/source-creator": "^10.2.2", + "@secretlint/types": "^10.2.2", + "debug": "^4.4.1", + "p-map": "^7.0.3" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/profiler": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/profiler/-/profiler-10.2.2.tgz", + "integrity": "sha512-qm9rWfkh/o8OvzMIfY8a5bCmgIniSpltbVlUVl983zDG1bUuQNd1/5lUEeWx5o/WJ99bXxS7yNI4/KIXfHexig==", + "dev": true, + "license": "MIT" + }, + "node_modules/@secretlint/resolver": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/resolver/-/resolver-10.2.2.tgz", + "integrity": "sha512-3md0cp12e+Ae5V+crPQYGd6aaO7ahw95s28OlULGyclyyUtf861UoRGS2prnUrKh7MZb23kdDOyGCYb9br5e4w==", + "dev": true, + "license": "MIT" + }, + "node_modules/@secretlint/secretlint-formatter-sarif": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/secretlint-formatter-sarif/-/secretlint-formatter-sarif-10.2.2.tgz", + "integrity": "sha512-ojiF9TGRKJJw308DnYBucHxkpNovDNu1XvPh7IfUp0A12gzTtxuWDqdpuVezL7/IP8Ua7mp5/VkDMN9OLp1doQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "node-sarif-builder": "^3.2.0" + } + }, + "node_modules/@secretlint/secretlint-rule-no-dotenv": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/secretlint-rule-no-dotenv/-/secretlint-rule-no-dotenv-10.2.2.tgz", + "integrity": "sha512-KJRbIShA9DVc5Va3yArtJ6QDzGjg3PRa1uYp9As4RsyKtKSSZjI64jVca57FZ8gbuk4em0/0Jq+uy6485wxIdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/types": "^10.2.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/secretlint-rule-preset-recommend": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/secretlint-rule-preset-recommend/-/secretlint-rule-preset-recommend-10.2.2.tgz", + "integrity": "sha512-K3jPqjva8bQndDKJqctnGfwuAxU2n9XNCPtbXVI5JvC7FnQiNg/yWlQPbMUlBXtBoBGFYp08A94m6fvtc9v+zA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/source-creator": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/source-creator/-/source-creator-10.2.2.tgz", + "integrity": "sha512-h6I87xJfwfUTgQ7irWq7UTdq/Bm1RuQ/fYhA3dtTIAop5BwSFmZyrchph4WcoEvbN460BWKmk4RYSvPElIIvxw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/types": "^10.2.2", + "istextorbinary": "^9.5.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/types": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/types/-/types-10.2.2.tgz", + "integrity": "sha512-Nqc90v4lWCXyakD6xNyNACBJNJ0tNCwj2WNk/7ivyacYHxiITVgmLUFXTBOeCdy79iz6HtN9Y31uw/jbLrdOAg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@sindresorhus/is": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-7.2.0.tgz", + "integrity": "sha512-P1Cz1dWaFfR4IR+U13mqqiGsLFf1KbayybWwdd2vfctdV6hDpUkgCY0nKOLLTMSoRd/jJNjtbqzf13K8DCCXQw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", + "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@textlint/ast-node-types": { + "version": "15.5.0", + "resolved": "https://registry.npmjs.org/@textlint/ast-node-types/-/ast-node-types-15.5.0.tgz", + "integrity": "sha512-K0LEuuTo4rza8yDrlYkRdXLao8Iz/QBMsQdIxRrOOrLYb4HAtZaypZ78c+J6rDA1UlGxadZVLmkkiv4KV5fMKQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@textlint/linter-formatter": { + "version": "15.5.0", + "resolved": "https://registry.npmjs.org/@textlint/linter-formatter/-/linter-formatter-15.5.0.tgz", + "integrity": "sha512-DPTm2+VXKID41qKQWagg/4JynM6hEEpvbq0PlGsEoC4Xm7IqXIxFym3mSf5+ued0cuiIV1hR9kgXjqGdP035tw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azu/format-text": "^1.0.2", + "@azu/style-format": "^1.0.1", + "@textlint/module-interop": "15.5.0", + "@textlint/resolver": "15.5.0", + "@textlint/types": "15.5.0", + "chalk": "^4.1.2", + "debug": "^4.4.3", + "js-yaml": "^4.1.1", + "lodash": "^4.17.21", + "pluralize": "^2.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "table": "^6.9.0", + "text-table": "^0.2.0" + } + }, + "node_modules/@textlint/linter-formatter/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@textlint/linter-formatter/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@textlint/linter-formatter/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@textlint/linter-formatter/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/@textlint/linter-formatter/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/@textlint/linter-formatter/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@textlint/linter-formatter/node_modules/pluralize": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-2.0.0.tgz", + "integrity": "sha512-TqNZzQCD4S42De9IfnnBvILN7HAW7riLqsCyp8lgjXeysyPlX5HhqKAcJHHHb9XskE4/a+7VGC9zzx8Ls0jOAw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@textlint/linter-formatter/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@textlint/linter-formatter/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@textlint/module-interop": { + "version": "15.5.0", + "resolved": "https://registry.npmjs.org/@textlint/module-interop/-/module-interop-15.5.0.tgz", + "integrity": "sha512-rqfouEhBEgZlR9umswWXXRBcmmSM28Trpr9b0duzgehKYVc7wSQCuQMagr6YBJa2NRMfRNinupusbJXMg0ij2A==", + "dev": true, + "license": "MIT" + }, + "node_modules/@textlint/resolver": { + "version": "15.5.0", + "resolved": "https://registry.npmjs.org/@textlint/resolver/-/resolver-15.5.0.tgz", + "integrity": "sha512-kK5nFbg5N3kVoZExQI/dnYjCInmTltvXDnuCRrBxHI01i6kO/o8R7Lc2aFkAZ6/NUZuRPalkyDdwZJke4/R2wg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@textlint/types": { + "version": "15.5.0", + "resolved": "https://registry.npmjs.org/@textlint/types/-/types-15.5.0.tgz", + "integrity": "sha512-EjAPbuA+3NyQ9WyFP7iUlddi35F3mGrf4tb4cZM0nWywbtEJ3+XAYqL+5RsF0qFeSguxGir09NdZOWrG9wVOUQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@textlint/ast-node-types": "15.5.0" + } + }, + "node_modules/@types/ansi-colors": { + "version": "3.2.6", + "resolved": "https://registry.npmjs.org/@types/ansi-colors/-/ansi-colors-3.2.6.tgz", + "integrity": "sha512-IAfWMGE7pYvqt22qPZ5Amtd16XtAHipTFlJ1S+ztpUu3WudDbqGna40ktsOQcTrLOO+DODGRCJFFrqfV+0fNQA==", + "deprecated": "This is a stub types definition. ansi-colors provides its own type definitions, so you do not need this installed.", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "*" + } + }, + "node_modules/@types/byline": { + "version": "4.2.36", + "resolved": "https://registry.npmjs.org/@types/byline/-/byline-4.2.36.tgz", + "integrity": "sha512-dO55KDSaOSE+3T8TwP66mzn0u/PM/aSedVMr1tby7WBNjfLIuS6IbYXi1mlau49sVSVB+gXKJscWE0JO3tlXDw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" } }, "node_modules/@types/debounce": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@types/debounce/-/debounce-1.0.0.tgz", - "integrity": "sha1-QXVgIAMx4buE1y2oU5EQLC/NYbc= sha512-B7FcD9ry40L831t7iuHQyDfYi+qVEV75qkEI2ROOyfjb2PfkAspL+NK6B2A0BceMuNhiYRmtKTNnNP7Ul4N2Pg==", - "dev": true + "version": "1.2.4", + "resolved": "https://registry.npmjs.org/@types/debounce/-/debounce-1.2.4.tgz", + "integrity": "sha512-jBqiORIzKDOToaF63Fm//haOCHuwQuLa2202RK4MozpA6lh93eCBc+/8+wZn5OzjJt3ySdc+74SXWXB55Ewtyw==", + "dev": true, + "license": "MIT" }, "node_modules/@types/debug": { - "version": "4.1.9", - "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.9.tgz", - "integrity": "sha512-8Hz50m2eoS56ldRlepxSBa6PWEVCtzUo/92HgLc2qTMnotJNIm7xP+UZhyWoYsyOdd5dxZ+NZLb24rsKyFs2ow==", + "version": "4.1.12", + "resolved": "https://registry.npmjs.org/@types/debug/-/debug-4.1.12.tgz", + "integrity": "sha512-vIChWdVG3LG1SMxEvI/AK+FWJthlrqlTu7fbrlywTkkaONwk/UAGaULXRlf8vkzFBLVm0zkMdCquhL5aOjhXPQ==", "dev": true, + "license": "MIT", "dependencies": { "@types/ms": "*" } @@ -1013,29 +1544,32 @@ "dev": true }, "node_modules/@types/fancy-log": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/@types/fancy-log/-/fancy-log-1.3.0.tgz", - "integrity": "sha512-mQjDxyOM1Cpocd+vm1kZBP7smwKZ4TNokFeds9LV7OZibmPJFEzY3+xZMrKfUdNT71lv8GoCPD6upKwHxubClw==", - "dev": true + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/@types/fancy-log/-/fancy-log-2.0.2.tgz", + "integrity": "sha512-SXVJvqWjsl90VwBfp7w4iQ0iO+vxAjQImglcpwbV9GkqNoUD5/p9Wsgetl40F1WL7pzWFN/eZPTF1g5FZXJsIw==", + "dev": true, + "license": "MIT" }, "node_modules/@types/fs-extra": { - "version": "9.0.12", - "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-9.0.12.tgz", - "integrity": "sha512-I+bsBr67CurCGnSenZZ7v94gd3tc3+Aj2taxMT4yu4ABLuOgOjeFxX3dokG24ztSRg5tnT00sL8BszO7gSMoIw==", + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/@types/fs-extra/-/fs-extra-11.0.4.tgz", + "integrity": "sha512-yTbItCNreRooED33qjunPthRcSjERP1r4MqCZc7wv0u2sUkzTFp45tgUfS5+r7FrZPdmCCNflLhVSP/o+SemsQ==", "dev": true, + "license": "MIT", "dependencies": { + "@types/jsonfile": "*", "@types/node": "*" } }, "node_modules/@types/glob": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.1.tgz", - "integrity": "sha512-1Bh06cbWJUHMC97acuD6UMG29nMt0Aqz1vF3guLfG+kHHJhy3AyohZFFxYk2f7Q1SQIrNwvncxAE0N/9s70F2w==", + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@types/glob/-/glob-9.0.0.tgz", + "integrity": "sha512-00UxlRaIUvYm4R4W9WYkN8/J+kV8fmOQ7okeH6YFtGWFMt3odD45tpG5yA5wnL7HE6lLgjaTW5n14ju2hl2NNA==", + "deprecated": "This is a stub types definition. glob provides its own type definitions, so you do not need this installed.", "dev": true, + "license": "MIT", "dependencies": { - "@types/events": "*", - "@types/minimatch": "*", - "@types/node": "*" + "glob": "*" } }, "node_modules/@types/glob-stream": { @@ -1049,10 +1583,11 @@ } }, "node_modules/@types/gulp": { - "version": "4.0.17", - "resolved": "https://registry.npmjs.org/@types/gulp/-/gulp-4.0.17.tgz", - "integrity": "sha512-+pKQynu2C/HS16kgmDlAicjtFYP8kaa86eE9P0Ae7GB5W29we/E2TIdbOWtEZD5XkpY+jr8fyqfwO6SWZecLpQ==", + "version": "4.0.18", + "resolved": "https://registry.npmjs.org/@types/gulp/-/gulp-4.0.18.tgz", + "integrity": "sha512-IqkYa4sXkwH2uwqO2aXYOoAisJpLX13BPaS6lmEAoG4BbgOay3qqGQFsT9LMSSQVMQlEKU7wTUW0sPV46V0olw==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*", "@types/undertaker": ">=1.2.6", @@ -1061,42 +1596,54 @@ } }, "node_modules/@types/gulp-filter": { - "version": "3.0.32", - "resolved": "https://registry.npmjs.org/@types/gulp-filter/-/gulp-filter-3.0.32.tgz", - "integrity": "sha512-JvY4qTxXehoK2yCUxYVxTMvckVbDM5TWHWeUoYJyX31gwFqw4YDD6JNzhuTxI3yHPUTY4BBRTqgm6puQEZVCNg==", + "version": "3.0.41", + "resolved": "https://registry.npmjs.org/@types/gulp-filter/-/gulp-filter-3.0.41.tgz", + "integrity": "sha512-v+7p521daAT0lXbMqciybyAktHF8plxW5+OHez7cM2JZitJN7DPw0QaU6FQkeLjBTZHEhQS/1HeQOwMiBlRFEg==", "dev": true, + "license": "MIT", "dependencies": { - "@types/minimatch": "*", + "@types/minimatch": "<=5", "@types/node": "*", "@types/vinyl": "*" } }, + "node_modules/@types/gulp-filter/node_modules/@types/minimatch": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", + "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/gulp-gzip": { - "version": "0.0.31", - "resolved": "https://registry.npmjs.org/@types/gulp-gzip/-/gulp-gzip-0.0.31.tgz", - "integrity": "sha512-KQjHz1FTqLse8/EiktfhN/vo33vamX4gVAQhMTp55STDBA7UToW5CJqYyP7iRorkHK9/aJ2nL2hLkNZkY4M8+w==", + "version": "0.0.36", + "resolved": "https://registry.npmjs.org/@types/gulp-gzip/-/gulp-gzip-0.0.36.tgz", + "integrity": "sha512-zGdc+LGAvcqVXu9ydx0OJLL39C23QYmlptqkVEnWz+03YfPMWBBjMGE0VOvr6AApm08r1G4r5QItRJaGjJQWuA==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } }, "node_modules/@types/gulp-json-editor": { - "version": "2.2.31", - "resolved": "https://registry.npmjs.org/@types/gulp-json-editor/-/gulp-json-editor-2.2.31.tgz", - "integrity": "sha512-piis0ImYAy0dt18R4EtTbAY+RV8jwTq5VisnUV6OfP8kD4743aHGkAdAd08No4NY3rFa5mD6ytIu8L0YU7nL9w==", + "version": "2.2.36", + "resolved": "https://registry.npmjs.org/@types/gulp-json-editor/-/gulp-json-editor-2.2.36.tgz", + "integrity": "sha512-BX6KjseCoZ09thBmF1QPZnDGtsLndNuhAziUbKqX8x+UDvlFjv2JLX5IeNOqMU2Sc5RkgaClyAU6JA0r19FEKw==", "dev": true, + "license": "MIT", "dependencies": { "@types/js-beautify": "*", "@types/node": "*" } }, "node_modules/@types/gulp-rename": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/@types/gulp-rename/-/gulp-rename-0.0.33.tgz", - "integrity": "sha512-FIZQvbZJj6V1gHPTzO+g/BCWpDur7fJrroae4gwV3LaoHBQ+MrR9sB+2HssK8fHv4WdY6hVNxkcft9bYatuPIA==", + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/gulp-rename/-/gulp-rename-2.0.7.tgz", + "integrity": "sha512-W1xIGdui1w4AB1ylt/b8jrpV1MTNfeKmMFfJXsN/NOtSHjRg2w4Wp9H96J+Ld9tZenTtU0u5LatpGFJvOBN5EA==", "dev": true, + "license": "MIT", "dependencies": { - "@types/node": "*" + "@types/node": "*", + "@types/vinyl": "*" } }, "node_modules/@types/gulp-sort": { @@ -1110,12 +1657,14 @@ } }, "node_modules/@types/gulp-sourcemaps": { - "version": "0.0.32", - "resolved": "https://registry.npmjs.org/@types/gulp-sourcemaps/-/gulp-sourcemaps-0.0.32.tgz", - "integrity": "sha512-+7BAmptW2bxyJnJcCEuie7vLoop3FwWgCdBMzyv7MYXED/HeNMeQuX7uPCkp4vfU1TTu4CYFH0IckNPvo0VePA==", + "version": "0.0.38", + "resolved": "https://registry.npmjs.org/@types/gulp-sourcemaps/-/gulp-sourcemaps-0.0.38.tgz", + "integrity": "sha512-9wm+P44eM/VKZSYwGDwalOH6rnvNckwfPgz0gEUlTzjKH1OsSbexdvE8aKBt+8syEkq827Fu+4gV17/EyUtH9w==", "dev": true, + "license": "MIT", "dependencies": { - "@types/node": "*" + "@types/node": "*", + "@types/vinyl": "*" } }, "node_modules/@types/gulp-util": { @@ -1134,7 +1683,8 @@ "version": "4.0.4", "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/js-beautify": { "version": "1.8.0", @@ -1142,48 +1692,61 @@ "integrity": "sha512-/siF86XrwDKLuHe8l7h6NhrAWgLdgqbxmjZv9NvGWmgYRZoTipkjKiWb0SQHy/jcR+ee0GvbG6uGd+LEBMGNvA==", "dev": true }, - "node_modules/@types/jws": { - "version": "3.2.10", - "resolved": "https://registry.npmjs.org/@types/jws/-/jws-3.2.10.tgz", - "integrity": "sha512-cOevhttJmssERB88/+XvZXvsq5m9JLKZNUiGfgjUb5lcPRdV2ZQciU6dU76D/qXXFYpSqkP3PrSg4hMTiafTZw==", + "node_modules/@types/jsonfile": { + "version": "6.1.4", + "resolved": "https://registry.npmjs.org/@types/jsonfile/-/jsonfile-6.1.4.tgz", + "integrity": "sha512-D5qGUYwjvnNNextdU59/+fI+spnwtTFmyQP0h+PfIOSkNfpU6AOICUOkm4i0OnSk+NyjdPJrxCDro0sJsWlRpQ==", "dev": true, "license": "MIT", "dependencies": { "@types/node": "*" } }, - "node_modules/@types/keyv": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/@types/keyv/-/keyv-3.1.4.tgz", - "integrity": "sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==", + "node_modules/@types/jws": { + "version": "3.2.11", + "resolved": "https://registry.npmjs.org/@types/jws/-/jws-3.2.11.tgz", + "integrity": "sha512-OOaTrLV6XdF1XvBgMeH1MjNuOaGCrRZWNSIds1AQaRgLdOWlAk2yMsfrJn+ekLgUow3xksWIM231lyFab7mHHw==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } }, "node_modules/@types/mime": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/mime/-/mime-0.0.29.tgz", - "integrity": "sha1-+8/TMFc7kS71nu7hRgK/rOYwdUs= sha512-EqWQSlonwbNgLMq2dMkokuzv/pwevb4q0JrPjfc7zzieG/cpqt+HsCE9dYoQd1snp2zlksl6k3rQ4LLfyQbQdA==", - "dev": true + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@types/mime/-/mime-4.0.0.tgz", + "integrity": "sha512-5eEkJZ/BLvTE3vXGKkWlyTSUVZuzj23Wj8PoyOq2lt5I3CYbiLBOPb3XmCW6QcuOibIUE6emHXHt9E/F/rCa6w==", + "deprecated": "This is a stub types definition. mime provides its own type definitions, so you do not need this installed.", + "dev": true, + "license": "MIT", + "dependencies": { + "mime": "*" + } }, "node_modules/@types/minimatch": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.3.tgz", - "integrity": "sha512-tHq6qdbT9U1IRSGf14CL0pUlULksvY9OZ+5eEgl1N7t+OA3tGvNpxJCzuKQlsNgCVwbAs670L1vcVQi8j9HjnA==", - "dev": true + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-6.0.0.tgz", + "integrity": "sha512-zmPitbQ8+6zNutpwgcQuLcsEpn/Cj54Kbn7L5pX0Os5kdWplB7xPgEh/g+SWOB/qmows2gpuCaPyduq8ZZRnxA==", + "deprecated": "This is a stub types definition. minimatch provides its own type definitions, so you do not need this installed.", + "dev": true, + "license": "MIT", + "dependencies": { + "minimatch": "*" + } }, "node_modules/@types/minimist": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.1.tgz", - "integrity": "sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg==", - "dev": true + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==", + "dev": true, + "license": "MIT" }, "node_modules/@types/mocha": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", - "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==", - "dev": true + "version": "10.0.10", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz", + "integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==", + "dev": true, + "license": "MIT" }, "node_modules/@types/ms": { "version": "0.7.32", @@ -1192,205 +1755,516 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/pump": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@types/pump/-/pump-1.1.3.tgz", + "integrity": "sha512-ZyooTTivmOwPfOwLVaszkF8Zq6mvavgjuHYitZhrIjfQAJDH+kIP3N+MzpG1zDAslsHvVz6Q8ECfivix3qLJaQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/rimraf": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/@types/rimraf/-/rimraf-4.0.5.tgz", + "integrity": "sha512-DTCZoIQotB2SUJnYgrEx43cQIUYOlNZz0AZPbKU4PSLYTUdML5Gox0++z4F9kQocxStrCmRNhi4x5x/UlwtKUA==", + "deprecated": "This is a stub types definition. rimraf provides its own type definitions, so you do not need this installed.", + "dev": true, + "license": "MIT", + "dependencies": { + "rimraf": "*" + } + }, + "node_modules/@types/sarif": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@types/sarif/-/sarif-2.1.7.tgz", + "integrity": "sha512-kRz0VEkJqWLf1LLVN4pT1cg1Z9wAuvI6L97V3m2f5B76Tg8d413ddvLBPTEHAZJlnn4XSvu0FkZtViCQGVyrXQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@types/through": { + "version": "0.0.33", + "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.33.tgz", + "integrity": "sha512-HsJ+z3QuETzP3cswwtzt2vEIiHBk/dCcHGhbmG5X3ecnwFD/lPrMpliGXxSCg03L9AhrdwA4Oz/qfspkDW+xGQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/through2": { + "version": "2.0.41", + "resolved": "https://registry.npmjs.org/@types/through2/-/through2-2.0.41.tgz", + "integrity": "sha512-ryQ0tidWkb1O1JuYvWKyMLYEtOWDqF5mHerJzKz/gQpoAaJq2l/dsMPBF0B5BNVT34rbARYJ5/tsZwLfUi2kwQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@types/undertaker": { + "version": "1.2.11", + "resolved": "https://registry.npmjs.org/@types/undertaker/-/undertaker-1.2.11.tgz", + "integrity": "sha512-j1Z0V2ByRHr8ZK7eOeGq0LGkkdthNFW0uAZGY22iRkNQNL9/vAV0yFPr1QN3FM/peY5bxs9P+1f0PYJTQVa5iA==", + "dev": true, + "dependencies": { + "@types/node": "*", + "@types/undertaker-registry": "*", + "async-done": "~1.3.2" + } + }, + "node_modules/@types/undertaker-registry": { "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/pump/-/pump-1.0.1.tgz", - "integrity": "sha1-roFXzv7wTRpNJMHMkdQDwvXaXNA= sha512-WGcg4jdczx60mEh0pWLUhw/2215BFYfSob5fHp/fJFZ+UFOiOxIqCnqRGSLWRsJ6Hvh4u2SBokxHDQ0wF9ujqQ==", + "resolved": "https://registry.npmjs.org/@types/undertaker-registry/-/undertaker-registry-1.0.1.tgz", + "integrity": "sha512-Z4TYuEKn9+RbNVk1Ll2SS4x1JeLHecolIbM/a8gveaHsW0Hr+RQMraZACwTO2VD7JvepgA6UO1A1VrbktQrIbQ==", + "dev": true + }, + "node_modules/@types/vinyl": { + "version": "2.0.12", + "resolved": "https://registry.npmjs.org/@types/vinyl/-/vinyl-2.0.12.tgz", + "integrity": "sha512-Sr2fYMBUVGYq8kj3UthXFAu5UN6ZW+rYr4NACjZQJvHvj+c8lYv0CahmZ2P/r7iUkN44gGUBwqxZkrKXYPb7cw==", + "dev": true, + "dependencies": { + "@types/expect": "^1.20.4", + "@types/node": "*" + } + }, + "node_modules/@types/vinyl-fs": { + "version": "2.4.9", + "resolved": "https://registry.npmjs.org/@types/vinyl-fs/-/vinyl-fs-2.4.9.tgz", + "integrity": "sha512-Q0EXd6c1fORjiOuK4ZaKdfFcMyFzJlTi56dqktwaWVLIDAzE49wUs3bKnYbZwzyMWoH+NcMWnRuR73S9A0jnRA==", + "dev": true, + "dependencies": { + "@types/events": "*", + "@types/glob-stream": "*", + "@types/node": "*", + "@types/vinyl": "*" + } + }, + "node_modules/@types/workerpool": { + "version": "9.0.0", + "resolved": "https://registry.npmjs.org/@types/workerpool/-/workerpool-9.0.0.tgz", + "integrity": "sha512-gEiZU7P4NzkhqcSwHlSx1CddtzzEjFD/uWegp2tV5DXQABOE4qlH4H8ptCm/S5DDvCViqFj7yMrN3yMBh1pkQg==", + "deprecated": "This is a stub types definition. workerpool provides its own type definitions, so you do not need this installed.", "dev": true, + "license": "MIT", + "dependencies": { + "workerpool": "*" + } + }, + "node_modules/@types/xml2js": { + "version": "0.4.14", + "resolved": "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.4.14.tgz", + "integrity": "sha512-4YnrRemBShWRO2QjvUin8ESA41rH+9nQGLUGZV/1IDhi3SL9OhdpNC/MrulTWuptXKwhx/aDxE7toV0f/ypIXQ==", + "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } }, - "node_modules/@types/responselike": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.3.tgz", - "integrity": "sha512-H/+L+UkTV33uf49PH5pCAUBVPNj2nDBXTN+qS1dOwyyg24l3CcicicCA7ca+HMvJBZcFgl5r8e+RR6elsb4Lyw==", + "node_modules/@types/yauzl": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.2.tgz", + "integrity": "sha512-8uALY5LTvSuHgloDVUvWP3pIauILm+8/0pDMokuDYIoNsOkSwd5AiHBTSEJjKTDcZr5z8UpgOWZkxBF4iJftoA==", + "dev": true, + "optional": true, + "dependencies": { + "@types/node": "*" + } + }, + "node_modules/@typespec/ts-http-runtime": { + "version": "0.3.2", + "resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.2.tgz", + "integrity": "sha512-IlqQ/Gv22xUC1r/WQm4StLkYQmaaTsXAhUVsNE0+xiyf0yRFiH5++q78U3bw6bLKDCTmh0uqKB9eG9+Bt75Dkg==", + "dev": true, + "license": "MIT", + "dependencies": { + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@vscode/iconv-lite-umd": { + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@vscode/iconv-lite-umd/-/iconv-lite-umd-0.7.1.tgz", + "integrity": "sha512-tK6k0DXFHW7q5+GGuGZO+phpAqpxO4WXl+BLc/8/uOk3RsM2ssAL3CQUQDb1TGfwltjsauhN6S4ghYZzs4sPFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/@vscode/ripgrep": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/@vscode/ripgrep/-/ripgrep-1.17.0.tgz", + "integrity": "sha512-mBRKm+ASPkUcw4o9aAgfbusIu6H4Sdhw09bjeP1YOBFTJEZAnrnk6WZwzv8NEjgC82f7ILvhmb1WIElSugea6g==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "https-proxy-agent": "^7.0.2", + "proxy-from-env": "^1.1.0", + "yauzl": "^2.9.2" + } + }, + "node_modules/@vscode/ripgrep/node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, + "node_modules/@vscode/vsce": { + "version": "3.7.1", + "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-3.7.1.tgz", + "integrity": "sha512-OTm2XdMt2YkpSn2Nx7z2EJtSuhRHsTPYsSK59hr3v8jRArK+2UEoju4Jumn1CmpgoBLGI6ReHLJ/czYltNUW3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "@azure/identity": "^4.1.0", + "@secretlint/node": "^10.1.2", + "@secretlint/secretlint-formatter-sarif": "^10.1.2", + "@secretlint/secretlint-rule-no-dotenv": "^10.1.2", + "@secretlint/secretlint-rule-preset-recommend": "^10.1.2", + "@vscode/vsce-sign": "^2.0.0", + "azure-devops-node-api": "^12.5.0", + "chalk": "^4.1.2", + "cheerio": "^1.0.0-rc.9", + "cockatiel": "^3.1.2", + "commander": "^12.1.0", + "form-data": "^4.0.0", + "glob": "^11.0.0", + "hosted-git-info": "^4.0.2", + "jsonc-parser": "^3.2.0", + "leven": "^3.1.0", + "markdown-it": "^14.1.0", + "mime": "^1.3.4", + "minimatch": "^3.0.3", + "parse-semver": "^1.1.1", + "read": "^1.0.7", + "secretlint": "^10.1.2", + "semver": "^7.5.2", + "tmp": "^0.2.3", + "typed-rest-client": "^1.8.4", + "url-join": "^4.0.1", + "xml2js": "^0.5.0", + "yauzl": "^2.3.1", + "yazl": "^2.2.2" + }, + "bin": { + "vsce": "vsce" + }, + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "keytar": "^7.7.0" + } + }, + "node_modules/@vscode/vsce-sign": { + "version": "2.0.9", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign/-/vsce-sign-2.0.9.tgz", + "integrity": "sha512-8IvaRvtFyzUnGGl3f5+1Cnor3LqaUWvhaUjAYO8Y39OUYlOf3cRd+dowuQYLpZcP3uwSG+mURwjEBOSq4SOJ0g==", + "dev": true, + "hasInstallScript": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optionalDependencies": { + "@vscode/vsce-sign-alpine-arm64": "2.0.6", + "@vscode/vsce-sign-alpine-x64": "2.0.6", + "@vscode/vsce-sign-darwin-arm64": "2.0.6", + "@vscode/vsce-sign-darwin-x64": "2.0.6", + "@vscode/vsce-sign-linux-arm": "2.0.6", + "@vscode/vsce-sign-linux-arm64": "2.0.6", + "@vscode/vsce-sign-linux-x64": "2.0.6", + "@vscode/vsce-sign-win32-arm64": "2.0.6", + "@vscode/vsce-sign-win32-x64": "2.0.6" + } + }, + "node_modules/@vscode/vsce-sign-alpine-arm64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-alpine-arm64/-/vsce-sign-alpine-arm64-2.0.6.tgz", + "integrity": "sha512-wKkJBsvKF+f0GfsUuGT0tSW0kZL87QggEiqNqK6/8hvqsXvpx8OsTEc3mnE1kejkh5r+qUyQ7PtF8jZYN0mo8Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "alpine" + ] + }, + "node_modules/@vscode/vsce-sign-alpine-x64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-alpine-x64/-/vsce-sign-alpine-x64-2.0.6.tgz", + "integrity": "sha512-YoAGlmdK39vKi9jA18i4ufBbd95OqGJxRvF3n6ZbCyziwy3O+JgOpIUPxv5tjeO6gQfx29qBivQ8ZZTUF2Ba0w==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "alpine" + ] + }, + "node_modules/@vscode/vsce-sign-darwin-arm64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-darwin-arm64/-/vsce-sign-darwin-arm64-2.0.6.tgz", + "integrity": "sha512-5HMHaJRIQuozm/XQIiJiA0W9uhdblwwl2ZNDSSAeXGO9YhB9MH5C4KIHOmvyjUnKy4UCuiP43VKpIxW1VWP4tQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@vscode/vsce-sign-darwin-x64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-darwin-x64/-/vsce-sign-darwin-x64-2.0.6.tgz", + "integrity": "sha512-25GsUbTAiNfHSuRItoQafXOIpxlYj+IXb4/qarrXu7kmbH94jlm5sdWSCKrrREs8+GsXF1b+l3OB7VJy5jsykw==", + "cpu": [ + "x64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@vscode/vsce-sign-linux-arm": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-linux-arm/-/vsce-sign-linux-arm-2.0.6.tgz", + "integrity": "sha512-UndEc2Xlq4HsuMPnwu7420uqceXjs4yb5W8E2/UkaHBB9OWCwMd3/bRe/1eLe3D8kPpxzcaeTyXiK3RdzS/1CA==", + "cpu": [ + "arm" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@vscode/vsce-sign-linux-arm64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-linux-arm64/-/vsce-sign-linux-arm64-2.0.6.tgz", + "integrity": "sha512-cfb1qK7lygtMa4NUl2582nP7aliLYuDEVpAbXJMkDq1qE+olIw/es+C8j1LJwvcRq1I2yWGtSn3EkDp9Dq5FdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@vscode/vsce-sign-linux-x64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-linux-x64/-/vsce-sign-linux-x64-2.0.6.tgz", + "integrity": "sha512-/olerl1A4sOqdP+hjvJ1sbQjKN07Y3DVnxO4gnbn/ahtQvFrdhUi0G1VsZXDNjfqmXw57DmPi5ASnj/8PGZhAA==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "@types/node": "*" - } + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "linux" + ] }, - "node_modules/@types/rimraf": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/@types/rimraf/-/rimraf-2.0.4.tgz", - "integrity": "sha512-8gBudvllD2A/c0CcEX/BivIDorHFt5UI5m46TsNj8DjWCCTTZT74kEe4g+QsY7P/B9WdO98d82zZgXO/RQzu2Q==", + "node_modules/@vscode/vsce-sign-win32-arm64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-win32-arm64/-/vsce-sign-win32-arm64-2.0.6.tgz", + "integrity": "sha512-ivM/MiGIY0PJNZBoGtlRBM/xDpwbdlCWomUWuLmIxbi1Cxe/1nooYrEQoaHD8ojVRgzdQEUzMsRbyF5cJJgYOg==", + "cpu": [ + "arm64" + ], "dev": true, - "dependencies": { - "@types/glob": "*", - "@types/node": "*" - } + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "win32" + ] }, - "node_modules/@types/through": { - "version": "0.0.29", - "resolved": "https://registry.npmjs.org/@types/through/-/through-0.0.29.tgz", - "integrity": "sha512-9a7C5VHh+1BKblaYiq+7Tfc+EOmjMdZaD1MYtkQjSoxgB69tBjW98ry6SKsi4zEIWztLOMRuL87A3bdT/Fc/4w==", + "node_modules/@vscode/vsce-sign-win32-x64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-win32-x64/-/vsce-sign-win32-x64-2.0.6.tgz", + "integrity": "sha512-mgth9Kvze+u8CruYMmhHw6Zgy3GRX2S+Ed5oSokDEK5vPEwGGKnmuXua9tmFhomeAnhgJnL4DCna3TiNuGrBTQ==", + "cpu": [ + "x64" + ], "dev": true, - "dependencies": { - "@types/node": "*" - } + "license": "SEE LICENSE IN LICENSE.txt", + "optional": true, + "os": [ + "win32" + ] }, - "node_modules/@types/through2": { - "version": "2.0.36", - "resolved": "https://registry.npmjs.org/@types/through2/-/through2-2.0.36.tgz", - "integrity": "sha512-vuifQksQHJXhV9McpVsXKuhnf3lsoX70PnhcqIAbs9dqLH2NgrGz0DzZPDY3+Yh6eaRqcE1gnCQ6QhBn1/PT5A==", + "node_modules/@vscode/vsce/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, + "license": "MIT", "dependencies": { - "@types/node": "*" + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, - "node_modules/@types/undertaker": { - "version": "1.2.11", - "resolved": "https://registry.npmjs.org/@types/undertaker/-/undertaker-1.2.11.tgz", - "integrity": "sha512-j1Z0V2ByRHr8ZK7eOeGq0LGkkdthNFW0uAZGY22iRkNQNL9/vAV0yFPr1QN3FM/peY5bxs9P+1f0PYJTQVa5iA==", + "node_modules/@vscode/vsce/node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "license": "MIT", "dependencies": { - "@types/node": "*", - "@types/undertaker-registry": "*", - "async-done": "~1.3.2" + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/@types/undertaker-registry": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@types/undertaker-registry/-/undertaker-registry-1.0.1.tgz", - "integrity": "sha512-Z4TYuEKn9+RbNVk1Ll2SS4x1JeLHecolIbM/a8gveaHsW0Hr+RQMraZACwTO2VD7JvepgA6UO1A1VrbktQrIbQ==", - "dev": true - }, - "node_modules/@types/vinyl": { - "version": "2.0.12", - "resolved": "https://registry.npmjs.org/@types/vinyl/-/vinyl-2.0.12.tgz", - "integrity": "sha512-Sr2fYMBUVGYq8kj3UthXFAu5UN6ZW+rYr4NACjZQJvHvj+c8lYv0CahmZ2P/r7iUkN44gGUBwqxZkrKXYPb7cw==", + "node_modules/@vscode/vsce/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, + "license": "MIT", "dependencies": { - "@types/expect": "^1.20.4", - "@types/node": "*" + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "node_modules/@types/vinyl-fs": { - "version": "2.4.9", - "resolved": "https://registry.npmjs.org/@types/vinyl-fs/-/vinyl-fs-2.4.9.tgz", - "integrity": "sha512-Q0EXd6c1fORjiOuK4ZaKdfFcMyFzJlTi56dqktwaWVLIDAzE49wUs3bKnYbZwzyMWoH+NcMWnRuR73S9A0jnRA==", + "node_modules/@vscode/vsce/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true, - "dependencies": { - "@types/events": "*", - "@types/glob-stream": "*", - "@types/node": "*", - "@types/vinyl": "*" - } + "license": "MIT" }, - "node_modules/@types/workerpool": { - "version": "6.4.0", - "resolved": "https://registry.npmjs.org/@types/workerpool/-/workerpool-6.4.0.tgz", - "integrity": "sha512-SIF2/169pDsLKeM8GQGHkOFifGalDbZgiBSaLUnnlVSRsAOenkAvQ6h4uhV2W+PZZczS+8LQxACwNkSykdT91A==", + "node_modules/@vscode/vsce/node_modules/glob": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", + "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { - "@types/node": "*" + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.1.1", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@types/xml2js": { - "version": "0.0.33", - "resolved": "https://registry.npmjs.org/@types/xml2js/-/xml2js-0.0.33.tgz", - "integrity": "sha1-IMXdZGAkUoTWSlVpABW5XkCft94= sha512-6dx6V6EdddqLjhxGdQrNdSu+O+3F7tOlyj660SpkO4/5uDSZM+LXcGQKAFnIJbvTzkQ6d2g3rWxyEXVwYAUoJg==", - "dev": true - }, - "node_modules/@types/yauzl": { - "version": "2.9.2", - "resolved": "https://registry.npmjs.org/@types/yauzl/-/yauzl-2.9.2.tgz", - "integrity": "sha512-8uALY5LTvSuHgloDVUvWP3pIauILm+8/0pDMokuDYIoNsOkSwd5AiHBTSEJjKTDcZr5z8UpgOWZkxBF4iJftoA==", + "node_modules/@vscode/vsce/node_modules/glob/node_modules/minimatch": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", "dev": true, - "optional": true, + "license": "BlueOak-1.0.0", "dependencies": { - "@types/node": "*" + "@isaacs/brace-expansion": "^5.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@vscode/iconv-lite-umd": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@vscode/iconv-lite-umd/-/iconv-lite-umd-0.7.0.tgz", - "integrity": "sha512-bRRFxLfg5dtAyl5XyiVWz/ZBPahpOpPrNYnnHpOpUZvam4tKH35wdhP4Kj6PbM0+KdliOsPzbGWpkxcdpNB/sg==", - "dev": true - }, - "node_modules/@vscode/ripgrep": { - "version": "1.15.10", - "resolved": "https://registry.npmjs.org/@vscode/ripgrep/-/ripgrep-1.15.10.tgz", - "integrity": "sha512-83Q6qFrELpFgf88bPOcwSWDegfY2r/cb6bIfdLTSZvN73Dg1wviSfO+1v6lTFMd0mAvUYYcTUu+Mn5xMroZMxA==", + "node_modules/@vscode/vsce/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, - "hasInstallScript": true, "license": "MIT", - "dependencies": { - "https-proxy-agent": "^7.0.2", - "proxy-from-env": "^1.1.0", - "yauzl": "^2.9.2" + "engines": { + "node": ">=8" } }, - "node_modules/@vscode/vsce": { - "version": "2.20.1", - "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-2.20.1.tgz", - "integrity": "sha512-ilbvoqvR/1/zseRPBAzYR6aKqSJ+jvda4/BqIwOqTxajpvLtEpK3kMLs77+dJdrlygS+VrP7Yhad8j0ukyD96g==", + "node_modules/@vscode/vsce/node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", "dev": true, - "dependencies": { - "azure-devops-node-api": "^11.0.1", - "chalk": "^2.4.2", - "cheerio": "^1.0.0-rc.9", - "commander": "^6.1.0", - "glob": "^7.0.6", - "hosted-git-info": "^4.0.2", - "jsonc-parser": "^3.2.0", - "leven": "^3.1.0", - "markdown-it": "^12.3.2", - "mime": "^1.3.4", - "minimatch": "^3.0.3", - "parse-semver": "^1.1.1", - "read": "^1.0.7", - "semver": "^7.5.2", - "tmp": "^0.2.1", - "typed-rest-client": "^1.8.4", - "url-join": "^4.0.1", - "xml2js": "^0.5.0", - "yauzl": "^2.3.1", - "yazl": "^2.2.2" - }, + "license": "MIT", "bin": { - "vsce": "vsce" + "mime": "cli.js" }, "engines": { - "node": ">= 14" - }, - "optionalDependencies": { - "keytar": "^7.7.0" + "node": ">=4" } }, - "node_modules/@vscode/vsce/node_modules/commander": { - "version": "6.2.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", - "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", + "node_modules/@vscode/vsce/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, "engines": { - "node": ">= 6" + "node": ">=8" } }, - "node_modules/@vscode/vsce/node_modules/jsonc-parser": { - "version": "3.3.1", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", - "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", - "dev": true - }, - "node_modules/@vscode/vsce/node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "node_modules/@vscode/vsce/node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", "dev": true, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" } }, "node_modules/@vscode/vsce/node_modules/yazl": { @@ -1425,6 +2299,23 @@ "node": ">= 14" } }, + "node_modules/ajv": { + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", + "dev": true, + "license": "MIT", + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, "node_modules/ansi-colors": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", @@ -1437,6 +2328,22 @@ "node": ">=0.10.0" } }, + "node_modules/ansi-escapes": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.2.0.tgz", + "integrity": "sha512-g6LhBsl+GBPRWGWsBtutpzBYuIIdBkLEvad5C/va/74Db018+5TZiyA26cZJAr3Rft5lprVqOIPxf5Vid6tqAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/ansi-gray": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", @@ -1449,6 +2356,19 @@ "node": ">=0.10.0" } }, + "node_modules/ansi-regex": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.2.2.tgz", + "integrity": "sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, "node_modules/ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", @@ -1487,13 +2407,14 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "dev": true, + "license": "Python-2.0" }, "node_modules/arr-diff": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/arr-diff/-/arr-diff-4.0.0.tgz", "integrity": "sha1-1kYQdP6/7HHn4VI1dhoyml3HxSA= sha512-YVIQ82gZPGBebQV/a8dar4AitzCQs0jjXwMPZllpXMaGjXPYVUawSxQrRsjhjupyVxEvbHgUmIhKVlND+j02kA==", - "devOptional": true, + "optional": true, "engines": { "node": ">=0.10.0" } @@ -1502,7 +2423,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ= sha512-sKpyeERZ02v1FeCZT8lrfJq5u6goHCtpTAzPwJYe7c8SPFOboNjNg1vz2L4VTn9T4PQxEx13TbXLmYUcS6Ug7Q==", - "devOptional": true, + "optional": true, "engines": { "node": ">=0.10.0" } @@ -1511,11 +2432,21 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", "integrity": "sha1-WWZ/QfrdTyDMvCu5a41Pf3jsA2c= sha512-Q+JC7Whu8HhmTdBph/Tq59IoRtoy6KAm5zzPv00WdujX82lbAL8K7WVjne7vdCsAmbF4AYaDOPyO3k0kl8qIrw==", - "devOptional": true, + "optional": true, "engines": { "node": ">=0.10.0" } }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/async-done": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/async-done/-/async-done-1.3.2.tgz", @@ -1531,27 +2462,65 @@ "node": ">= 0.10" } }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, "node_modules/azure-devops-node-api": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-11.2.0.tgz", - "integrity": "sha512-XdiGPhrpaT5J8wdERRKs5g8E0Zy1pvOYTli7z9E8nmOn3YGp4FhtjhrOyFmX/8veWCwdI69mCHKJw6l+4J/bHA==", + "version": "12.5.0", + "resolved": "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-12.5.0.tgz", + "integrity": "sha512-R5eFskGvOm3U/GzeAuxRkUsAl0hrAwGgWn6zAd2KrZmrEhWZVqLew4OOupbQlXUuojUzpGtq62SmdhJ06N88og==", "dev": true, + "license": "MIT", "dependencies": { "tunnel": "0.0.6", "typed-rest-client": "^1.8.4" } }, + "node_modules/b4a": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.7.3.tgz", + "integrity": "sha512-5Q2mfq2WfGuFp3uS//0s6baOJLMoVduPYVeNmDYxu5OUA1/cBfvr2RIS7vi62LdNj/urk1hfmj867I3qt6uZ7Q==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } + }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, + "node_modules/bare-events": { + "version": "2.8.2", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.8.2.tgz", + "integrity": "sha512-riJjyv1/mHLIPX4RwiK+oW9/4c3TEUeORHKefKAKnZ5kyslbN+HXowtbaVEqt4IMUB7OXlfixcs6gsFeo/jhiQ==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "bare-abort-controller": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + } + } + }, "node_modules/base64-js": { "version": "1.5.1", "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", - "devOptional": true, + "dev": true, "funding": [ { "type": "github", @@ -1576,11 +2545,28 @@ "node": ">=8" } }, + "node_modules/binaryextensions": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-6.11.0.tgz", + "integrity": "sha512-sXnYK/Ij80TO3lcqZVV2YgfKN5QjUWIRk/XSm2J/4bd/lPko3lvk0O4ZppH6m+6hB2/GTu+ptNwVFe1xh+QLQw==", + "dev": true, + "license": "Artistic-2.0", + "dependencies": { + "editions": "^6.21.0" + }, + "engines": { + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, "node_modules/bl": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", - "devOptional": true, + "dev": true, + "optional": true, "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", @@ -1606,11 +2592,19 @@ "dev": true, "optional": true }, + "node_modules/boundary": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/boundary/-/boundary-2.0.0.tgz", + "integrity": "sha512-rJKn5ooC9u8q13IMCrW0RSp31pxBCHE3y9V/tp3TdWSLf8Em3p6Di4NBpfzbJge9YjjFEsD0RtFEjtvHL5VyEA==", + "dev": true, + "license": "BSD-2-Clause" + }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -1632,7 +2626,7 @@ "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", - "devOptional": true, + "dev": true, "funding": [ { "type": "github", @@ -1647,6 +2641,7 @@ "url": "https://feross.org/support" } ], + "optional": true, "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" @@ -1689,6 +2684,22 @@ "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw= sha512-T7zexNBwiiaCOGDg9xNX9PBmjrubblRkENuptryuI64URkXDFum9il/JGL8Lm8wYfAXpredVXXZz7eMHilimiQ==", "dev": true }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/byline": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/byline/-/byline-5.0.0.tgz", @@ -1698,44 +2709,88 @@ "node": ">=0.10.0" } }, + "node_modules/byte-counter": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/byte-counter/-/byte-counter-0.1.0.tgz", + "integrity": "sha512-jheRLVMeUKrDBjVw2O5+k4EvR4t9wtxHL+bo/LxfkxsVeuGMy3a5SEGgXdAFA4FSzTrU8rQXQIrsZ3oBq5a0pQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/cacheable-lookup": { - "version": "5.0.4", - "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-5.0.4.tgz", - "integrity": "sha512-2/kNscPhpcxrOigMZzbiWF7dz8ilhb/nIHU3EyZiXWXpeq/au8qJ8VhdftMkty3n7Gj6HIGalQG8oiBNB3AJgA==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", + "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", "dev": true, + "license": "MIT", "engines": { - "node": ">=10.6.0" + "node": ">=14.16" } }, "node_modules/cacheable-request": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-7.0.4.tgz", - "integrity": "sha512-v+p6ongsrp0yTGbJXjgxPow2+DL93DASP4kXCDKb8/bwRtt9OEF3whggkkDkGNzgcWy2XaF4a8nZglC7uElscg==", + "version": "13.0.18", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-13.0.18.tgz", + "integrity": "sha512-rFWadDRKJs3s2eYdXlGggnBZKG7MTblkFBB0YllFds+UYnfogDp2wcR6JN97FhRkHTvq59n2vhNoHNZn29dh/Q==", "dev": true, + "license": "MIT", "dependencies": { - "clone-response": "^1.0.2", - "get-stream": "^5.1.0", - "http-cache-semantics": "^4.0.0", - "keyv": "^4.0.0", - "lowercase-keys": "^2.0.0", - "normalize-url": "^6.0.1", - "responselike": "^2.0.0" + "@types/http-cache-semantics": "^4.0.4", + "get-stream": "^9.0.1", + "http-cache-semantics": "^4.2.0", + "keyv": "^5.5.5", + "mimic-response": "^4.0.0", + "normalize-url": "^8.1.1", + "responselike": "^4.0.2" }, "engines": { - "node": ">=8" + "node": ">=18" } }, - "node_modules/call-bind": { - "version": "1.0.7", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.7.tgz", - "integrity": "sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w==", + "node_modules/cacheable-request/node_modules/get-stream": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", + "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sec-ant/readable-stream": "^0.4.1", + "is-stream": "^4.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", "dev": true, + "license": "MIT", "dependencies": { - "es-define-property": "^1.0.0", "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "set-function-length": "^1.2.1" + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" }, "engines": { "node": ">= 0.4" @@ -1821,7 +2876,8 @@ "version": "1.1.4", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", - "devOptional": true + "dev": true, + "optional": true }, "node_modules/clone": { "version": "2.1.2", @@ -1836,31 +2892,22 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/clone-buffer/-/clone-buffer-1.0.0.tgz", "integrity": "sha1-4+JbIHrE5wGvch4staFnksrD3Fg= sha512-KLLTJWrvwIP+OPfMn0x2PheDEP20RPUcGXj/ERegTgdmPEZylALQldygiqrPPu8P45uNuPs7ckmReLY6v/iA5g==", - "devOptional": true, + "optional": true, "engines": { "node": ">= 0.10" } }, - "node_modules/clone-response": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", - "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws= sha512-yjLXh88P599UOyPTFX0POsd7WxnbsVsGohcwzHOLspIhhpalPw1BcqED8NblyZLKcGrL8dTgMlcaZxV2jAD41Q==", - "dev": true, - "dependencies": { - "mimic-response": "^1.0.0" - } - }, "node_modules/clone-stats": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/clone-stats/-/clone-stats-1.0.0.tgz", "integrity": "sha1-s3gt/4u1R04Yuba/D9/ngvh3doA= sha512-au6ydSpg6nsrigcZ4m8Bc9hxjeW+GJ8xh5G3BJCMt4WXe1H10UNaVOamqQTmrx1kjVuxAHIQSNU6hY4Nsn9/ag==", - "devOptional": true + "optional": true }, "node_modules/cloneable-readable": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/cloneable-readable/-/cloneable-readable-1.1.3.tgz", "integrity": "sha512-2EF8zTQOxYq70Y4XKtorQupqF0m49MBz2/yf5Bj+MHjvpG3Hy7sImifnqD6UA+TKYxeSV+u6qqQPawN5UvnpKQ==", - "devOptional": true, + "optional": true, "dependencies": { "inherits": "^2.0.1", "process-nextick-args": "^2.0.0", @@ -1871,7 +2918,7 @@ "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "devOptional": true, + "optional": true, "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", @@ -1882,6 +2929,16 @@ "util-deprecate": "~1.0.1" } }, + "node_modules/cockatiel": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/cockatiel/-/cockatiel-3.2.1.tgz", + "integrity": "sha512-gfrHV6ZPkquExvMh9IOkKsBzNDk6sDuZ6DdBGUBkvFnTCqCxzpuq48RySgP0AnaqQkw2zynOFj9yly6T1Q2G5Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=16" + } + }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", @@ -1906,6 +2963,29 @@ "color-support": "bin.js" } }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "license": "MIT", + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/compare-version": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/compare-version/-/compare-version-0.1.2.tgz", @@ -1971,12 +3051,13 @@ } }, "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -1991,7 +3072,8 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", - "devOptional": true, + "dev": true, + "optional": true, "dependencies": { "mimic-response": "^3.1.0" }, @@ -2006,7 +3088,8 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", - "devOptional": true, + "dev": true, + "optional": true, "engines": { "node": ">=10" }, @@ -2018,44 +3101,53 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "devOptional": true, + "dev": true, + "optional": true, "engines": { "node": ">=4.0.0" } }, - "node_modules/defer-to-connect": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-2.0.1.tgz", - "integrity": "sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==", + "node_modules/default-browser": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.4.0.tgz", + "integrity": "sha512-XDuvSq38Hr1MdN47EDvYtx3U0MTqpCEn+F6ft8z2vYDzMrvQhVp0ui9oQdqW3MvK3vqUETglt1tVGgjLuJ5izg==", "dev": true, + "license": "MIT", + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, "engines": { - "node": ">=10" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/define-data-property": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/define-data-property/-/define-data-property-1.1.4.tgz", - "integrity": "sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==", + "node_modules/default-browser-id": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", + "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", "dev": true, - "dependencies": { - "es-define-property": "^1.0.0", - "es-errors": "^1.3.0", - "gopd": "^1.0.1" - }, + "license": "MIT", "engines": { - "node": ">= 0.4" + "node": ">=18" }, "funding": { - "url": "https://github.com/sponsors/ljharb" + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", "dev": true, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/define-properties": { @@ -2071,11 +3163,22 @@ "node": ">= 0.4" } }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, "node_modules/detect-libc": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", - "devOptional": true, + "dev": true, + "optional": true, "engines": { "node": ">=8" } @@ -2152,6 +3255,21 @@ "url": "https://github.com/fb55/domutils?sponsor=1" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/duplexify": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/duplexify/-/duplexify-4.1.2.tgz", @@ -2164,6 +3282,13 @@ "stream-shift": "^1.0.0" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true, + "license": "MIT" + }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", @@ -2173,12 +3298,30 @@ "safe-buffer": "^5.0.1" } }, + "node_modules/editions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/editions/-/editions-6.22.0.tgz", + "integrity": "sha512-UgGlf8IW75je7HZjNDpJdCv4cGJWIi6yumFdZ0R7A8/CIhQiWUjyGLCxdHpd8bmyD1gnkfUNK0oeOXqUS2cpfQ==", + "dev": true, + "license": "Artistic-2.0", + "dependencies": { + "version-range": "^4.15.0" + }, + "engines": { + "ecmascript": ">= es5", + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, "node_modules/electron-osx-sign": { - "version": "0.4.16", - "resolved": "https://registry.npmjs.org/electron-osx-sign/-/electron-osx-sign-0.4.16.tgz", - "integrity": "sha512-ziMWfc3NmQlwnWLW6EaZq8nH2BWVng/atX5GWsGwhexJYpdW6hsg//MkAfRTRx1kR3Veiqkeiog1ibkbA4x0rg==", + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/electron-osx-sign/-/electron-osx-sign-0.6.0.tgz", + "integrity": "sha512-+hiIEb2Xxk6eDKJ2FFlpofCnemCbjbT5jz+BKGpVBrRNT3kWTGs4DfNX6IzGwgi33hUcXF+kFs9JW+r6Wc1LRg==", "deprecated": "Please use @electron/osx-sign moving forward. Be aware the API is slightly different", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "bluebird": "^3.5.0", "compare-version": "^0.1.2", @@ -2210,11 +3353,18 @@ "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", "dev": true }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true, + "license": "MIT" + }, "node_modules/end-of-stream": { "version": "1.4.4", "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", - "devOptional": true, + "dev": true, "dependencies": { "once": "^1.4.0" } @@ -2232,22 +3382,37 @@ } }, "node_modules/env-paths": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz", - "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-3.0.0.tgz", + "integrity": "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==", "dev": true, + "license": "MIT", "engines": { - "node": ">=6" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.4" + "license": "MIT", + "engines": { + "node": ">=18" }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -2257,6 +3422,36 @@ "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, "engines": { "node": ">= 0.4" } @@ -2269,9 +3464,9 @@ "optional": true }, "node_modules/esbuild": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz", - "integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz", + "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -2282,31 +3477,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.0", - "@esbuild/android-arm": "0.25.0", - "@esbuild/android-arm64": "0.25.0", - "@esbuild/android-x64": "0.25.0", - "@esbuild/darwin-arm64": "0.25.0", - "@esbuild/darwin-x64": "0.25.0", - "@esbuild/freebsd-arm64": "0.25.0", - "@esbuild/freebsd-x64": "0.25.0", - "@esbuild/linux-arm": "0.25.0", - "@esbuild/linux-arm64": "0.25.0", - "@esbuild/linux-ia32": "0.25.0", - "@esbuild/linux-loong64": "0.25.0", - "@esbuild/linux-mips64el": "0.25.0", - "@esbuild/linux-ppc64": "0.25.0", - "@esbuild/linux-riscv64": "0.25.0", - "@esbuild/linux-s390x": "0.25.0", - "@esbuild/linux-x64": "0.25.0", - "@esbuild/netbsd-arm64": "0.25.0", - "@esbuild/netbsd-x64": "0.25.0", - "@esbuild/openbsd-arm64": "0.25.0", - "@esbuild/openbsd-x64": "0.25.0", - "@esbuild/sunos-x64": "0.25.0", - "@esbuild/win32-arm64": "0.25.0", - "@esbuild/win32-ia32": "0.25.0", - "@esbuild/win32-x64": "0.25.0" + "@esbuild/aix-ppc64": "0.27.2", + "@esbuild/android-arm": "0.27.2", + "@esbuild/android-arm64": "0.27.2", + "@esbuild/android-x64": "0.27.2", + "@esbuild/darwin-arm64": "0.27.2", + "@esbuild/darwin-x64": "0.27.2", + "@esbuild/freebsd-arm64": "0.27.2", + "@esbuild/freebsd-x64": "0.27.2", + "@esbuild/linux-arm": "0.27.2", + "@esbuild/linux-arm64": "0.27.2", + "@esbuild/linux-ia32": "0.27.2", + "@esbuild/linux-loong64": "0.27.2", + "@esbuild/linux-mips64el": "0.27.2", + "@esbuild/linux-ppc64": "0.27.2", + "@esbuild/linux-riscv64": "0.27.2", + "@esbuild/linux-s390x": "0.27.2", + "@esbuild/linux-x64": "0.27.2", + "@esbuild/netbsd-arm64": "0.27.2", + "@esbuild/netbsd-x64": "0.27.2", + "@esbuild/openbsd-arm64": "0.27.2", + "@esbuild/openbsd-x64": "0.27.2", + "@esbuild/openharmony-arm64": "0.27.2", + "@esbuild/sunos-x64": "0.27.2", + "@esbuild/win32-arm64": "0.27.2", + "@esbuild/win32-ia32": "0.27.2", + "@esbuild/win32-x64": "0.27.2" } }, "node_modules/escape-string-regexp": { @@ -2319,19 +3515,31 @@ } }, "node_modules/events": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/events/-/events-3.2.0.tgz", - "integrity": "sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.3.0.tgz", + "integrity": "sha512-mQw+2fkQbALzQ7V0MY0IqdnXNOeTtP4r0lN9z7AAawCXgqea7bDii20AYrIBrFd/Hx0M2Ocz6S111CaFkUcb0Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.8.x" } }, + "node_modules/events-universal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", + "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.7.0" + } + }, "node_modules/expand-template": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", - "devOptional": true, + "dev": true, + "optional": true, "engines": { "node": ">=6" } @@ -2340,7 +3548,7 @@ "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz", "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg= sha512-BwY5b5Ql4+qZoefgMj2NUmx+tehVTH/Kf4k1ZEtOHNFcm2wSxMRo992l6X3TIgni2eZVTZ85xMOjF31fwZAj6Q==", - "devOptional": true, + "optional": true, "dependencies": { "assign-symbols": "^1.0.0", "is-extendable": "^1.0.1" @@ -2369,6 +3577,17 @@ "@types/yauzl": "^2.9.1" } }, + "node_modules/extract-zip/node_modules/yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + }, "node_modules/fancy-log": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", @@ -2384,40 +3603,95 @@ "node": ">= 0.10" } }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true }, - "node_modules/fast-xml-parser": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-4.5.0.tgz", - "integrity": "sha512-/PlTQCI96+fZMAOLMZK4CWG1ItCbfZ/0jx7UIJFChPNrx7tcEgerUgWbeieCM9MfHInUDyK8DWYZ+YrywDJuTg==", + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", "dev": true, "funding": [ { "type": "github", - "url": "https://github.com/sponsors/NaturalIntelligence" + "url": "https://github.com/sponsors/fastify" }, { - "type": "paypal", - "url": "https://paypal.me/naturalintelligence" + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, + "node_modules/fast-xml-parser": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/fast-xml-parser/-/fast-xml-parser-5.3.3.tgz", + "integrity": "sha512-2O3dkPAAC6JavuMm8+4+pgTk+5hoAs+CjZ+sWcQLkX9+/tHRuTkQh/Oaifr8qDmZ8iEHb771Ea6G8CdwkrgvYA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" } ], "license": "MIT", "dependencies": { - "strnum": "^1.0.5" + "strnum": "^2.1.0" }, "bin": { "fxparser": "src/cli/cli.js" } }, + "node_modules/fastq": { + "version": "1.20.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", + "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", + "dev": true, + "license": "ISC", + "dependencies": { + "reusify": "^1.0.4" + } + }, "node_modules/fd-slicer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", - "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4= sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", + "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", "dev": true, + "license": "MIT", "dependencies": { "pend": "~1.2.0" } @@ -2461,30 +3735,76 @@ "util-deprecate": "~1.0.1" } }, + "node_modules/foreground-child": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", + "dev": true, + "license": "ISC", + "dependencies": { + "cross-spawn": "^7.0.6", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/fork-stream": { "version": "0.0.4", "resolved": "https://registry.npmjs.org/fork-stream/-/fork-stream-0.0.4.tgz", "integrity": "sha512-Pqq5NnT78ehvUnAk/We/Jr22vSvanRlFTpAmQ88xBY/M1TlHe+P0ILuEyXS595ysdGfaj22634LBkGMA2GTcpA==", "dev": true }, + "node_modules/form-data": { + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", + "dev": true, + "license": "MIT", + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.12" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/form-data-encoder": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-4.1.0.tgz", + "integrity": "sha512-G6NsmEW15s0Uw9XnCg+33H3ViYRyiM0hMrMhhqQOR8NFc5GhYrI+6I3u7OTw7b91J2g8rtvMBZJDbcGb2YUniw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 18" + } + }, "node_modules/fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", - "devOptional": true + "dev": true, + "optional": true }, "node_modules/fs-extra": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-8.1.0.tgz", - "integrity": "sha512-yhlQgA6mnOJUKOsRUFsgJdQCvkKhcz8tlZG5HBQfReYZy46OwLcY+Zia0mtdHsOo9y/hP+CxMN0TU9QxoOtG4g==", + "version": "11.3.3", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.3.3.tgz", + "integrity": "sha512-VWSRii4t0AFm6ixFFmLLx1t7wS1gh+ckoa84aOeapGum0h+EZd1EhEumSB+ZdDLnEPuucsVB9oB7cxJHap6Afg==", "dev": true, + "license": "MIT", "dependencies": { "graceful-fs": "^4.2.0", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" }, "engines": { - "node": ">=6 <7 || >=8" + "node": ">=14.14" } }, "node_modules/fs.realpath": { @@ -2511,21 +3831,28 @@ "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", "dev": true, + "license": "MIT", "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "dev": true, + "license": "MIT", "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -2534,6 +3861,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-stream": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", @@ -2553,7 +3894,8 @@ "version": "0.0.0", "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4= sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", - "devOptional": true + "dev": true, + "optional": true }, "node_modules/glob": { "version": "7.2.3", @@ -2606,22 +3948,6 @@ "node": ">=10.0" } }, - "node_modules/global-agent/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "optional": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/globalthis": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/globalthis/-/globalthis-1.0.2.tgz", @@ -2638,60 +3964,155 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "node_modules/globby": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.1.0.tgz", + "integrity": "sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==", "dev": true, + "license": "MIT", "dependencies": { - "get-intrinsic": "^1.1.3" + "@sindresorhus/merge-streams": "^2.1.0", + "fast-glob": "^3.3.3", + "ignore": "^7.0.3", + "path-type": "^6.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/got": { - "version": "11.8.6", - "resolved": "https://registry.npmjs.org/got/-/got-11.8.6.tgz", - "integrity": "sha512-6tfZ91bOr7bOXnK7PRDCGBLa1H4U080YHNaAQ2KsMGlLEzRbk44nsZF2E1IeRc3vtJHPVbKCYgdFbaGO2ljd8g==", + "version": "14.6.6", + "resolved": "https://registry.npmjs.org/got/-/got-14.6.6.tgz", + "integrity": "sha512-QLV1qeYSo5l13mQzWgP/y0LbMr5Plr5fJilgAIwgnwseproEbtNym8xpLsDzeZ6MWXgNE6kdWGBjdh3zT/Qerg==", "dev": true, + "license": "MIT", "dependencies": { - "@sindresorhus/is": "^4.0.0", - "@szmarczak/http-timer": "^4.0.5", - "@types/cacheable-request": "^6.0.1", - "@types/responselike": "^1.0.0", - "cacheable-lookup": "^5.0.3", - "cacheable-request": "^7.0.2", - "decompress-response": "^6.0.0", - "http2-wrapper": "^1.0.0-beta.5.2", - "lowercase-keys": "^2.0.0", - "p-cancelable": "^2.0.0", - "responselike": "^2.0.0" + "@sindresorhus/is": "^7.0.1", + "byte-counter": "^0.1.0", + "cacheable-lookup": "^7.0.0", + "cacheable-request": "^13.0.12", + "decompress-response": "^10.0.0", + "form-data-encoder": "^4.0.2", + "http2-wrapper": "^2.2.1", + "keyv": "^5.5.3", + "lowercase-keys": "^3.0.0", + "p-cancelable": "^4.0.1", + "responselike": "^4.0.2", + "type-fest": "^4.26.1" }, "engines": { - "node": ">=10.19.0" + "node": ">=20" }, "funding": { "url": "https://github.com/sindresorhus/got?sponsor=1" } }, + "node_modules/got/node_modules/decompress-response": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-10.0.0.tgz", + "integrity": "sha512-oj7KWToJuuxlPr7VV0vabvxEIiqNMo+q0NueIiL3XhtwC6FVOX7Hr1c0C4eD0bmf7Zr+S/dSf2xvkH3Ad6sU3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^4.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/got/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/graceful-fs": { - "version": "4.2.8", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", - "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", - "devOptional": true + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "devOptional": true, + "license": "ISC" }, "node_modules/gulp-merge-json": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/gulp-merge-json/-/gulp-merge-json-2.1.1.tgz", - "integrity": "sha512-VhvAlcf+dcCb94j/2yDPWxJ3X7x4P/Xwcrv1dhjYuRgvADwFJmaQwl4zbuq+GDZvzMacbVncWtEdsETpUSkhYw==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/gulp-merge-json/-/gulp-merge-json-2.2.1.tgz", + "integrity": "sha512-w1+ka0xPu/VSEAVxvYlwEASpyZuHGVpBBtYakd4xpw+dF5QiwNh81qkNGnhGvtUINh94IInleQNs0rzy8KdyRg==", "dev": true, + "license": "MIT", "dependencies": { - "json5": "^2.1.3", + "json5": "^2.2.3", + "lodash.clonedeep": "^4.5.0", "lodash.mergewith": "^4.6.1", - "plugin-error": "^1.0.1", + "plugin-error": "^2.0.1", "through": "^2.3.8", - "vinyl": "^2.1.0" + "vinyl": "^3.0.0" + } + }, + "node_modules/gulp-merge-json/node_modules/plugin-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-2.0.1.tgz", + "integrity": "sha512-zMakqvIDyY40xHOvzXka0kUvf40nYIuwRE8dWhti2WtjQZ31xAgBZBhxsK7vK3QbRXS1Xms/LO7B5cuAsfB2Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^1.0.1" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/gulp-merge-json/node_modules/replace-ext": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-2.0.0.tgz", + "integrity": "sha512-UszKE5KVK6JvyD92nzMn9cDapSk6w/CaFZ96CnmDMUqH9oowfxF/ZjRITD25H4DnOQClLA4/j7jLGXXLVKxAug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/gulp-merge-json/node_modules/vinyl": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-3.0.1.tgz", + "integrity": "sha512-0QwqXteBNXgnLCdWdvPQBX6FXRHtIH3VhJPTd5Lwn28tJXc34YqSCWUmkOvtJHBmB3gGoPtrOKk3Ts8/kEZ9aA==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone": "^2.1.2", + "remove-trailing-separator": "^1.1.0", + "replace-ext": "^2.0.0", + "teex": "^1.0.1" + }, + "engines": { + "node": ">=10.13.0" } }, "node_modules/gulp-sort": { @@ -2737,23 +4158,12 @@ "node": ">=4" } }, - "node_modules/has-property-descriptors": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz", - "integrity": "sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==", - "dev": true, - "dependencies": { - "es-define-property": "^1.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, - "node_modules/has-proto": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.3.tgz", - "integrity": "sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q==", + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -2761,11 +4171,15 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, "engines": { "node": ">= 0.4" }, @@ -2778,6 +4192,7 @@ "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dev": true, + "license": "MIT", "dependencies": { "function-bind": "^1.1.2" }, @@ -2817,10 +4232,11 @@ } }, "node_modules/http-cache-semantics": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", - "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", - "dev": true + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "dev": true, + "license": "BSD-2-Clause" }, "node_modules/http-proxy-agent": { "version": "7.0.2", @@ -2837,13 +4253,14 @@ } }, "node_modules/http2-wrapper": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-1.0.3.tgz", - "integrity": "sha512-V+23sDMr12Wnz7iTcDeJr3O6AIxlnvT/bmaAAAP/Xda35C90p9599p0F1eHR/N1KILWSoWVAiOMFjBBXaXSMxg==", + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz", + "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==", "dev": true, + "license": "MIT", "dependencies": { "quick-lru": "^5.1.1", - "resolve-alpn": "^1.0.0" + "resolve-alpn": "^1.2.0" }, "engines": { "node": ">=10.19.0" @@ -2867,7 +4284,7 @@ "version": "1.2.1", "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", - "devOptional": true, + "dev": true, "funding": [ { "type": "github", @@ -2881,7 +4298,31 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "optional": true + }, + "node_modules/ignore": { + "version": "7.0.5", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.5.tgz", + "integrity": "sha512-Hs59xBNfUIunMFgWAbGX5cq6893IbWg4KnrjbYwX3tx0ztorVgTDA6B2sxf8ejHJ4wz8BqGUMYlnzNBer5NvGg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, + "node_modules/index-to-position": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.2.0.tgz", + "integrity": "sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/inflight": { "version": "1.0.6", @@ -2904,7 +4345,8 @@ "version": "1.3.8", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", - "devOptional": true + "dev": true, + "optional": true }, "node_modules/is-binary-path": { "version": "2.1.0", @@ -2919,15 +4361,16 @@ } }, "node_modules/is-docker": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", - "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", "dev": true, + "license": "MIT", "bin": { "is-docker": "cli.js" }, "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -2937,7 +4380,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz", "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==", - "devOptional": true, + "optional": true, "dependencies": { "is-plain-object": "^2.0.4" }, @@ -2954,6 +4397,16 @@ "node": ">=0.10.0" } }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-glob": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", @@ -2966,6 +4419,25 @@ "node": ">=0.10.0" } }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", @@ -2979,7 +4451,7 @@ "version": "2.0.4", "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "devOptional": true, + "optional": true, "dependencies": { "isobject": "^3.0.1" }, @@ -2987,6 +4459,19 @@ "node": ">=0.10.0" } }, + "node_modules/is-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-utf8": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-utf8/-/is-utf8-0.2.1.tgz", @@ -2994,15 +4479,19 @@ "optional": true }, "node_modules/is-wsl": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", - "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", "dev": true, + "license": "MIT", "dependencies": { - "is-docker": "^2.0.0" + "is-inside-container": "^1.0.0" }, "engines": { - "node": ">=8" + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/isarray": { @@ -3023,32 +4512,81 @@ "node": ">=0.6.0" } }, - "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", - "dev": true + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "dev": true + }, + "node_modules/isobject": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", + "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8= sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/istextorbinary": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-9.5.0.tgz", + "integrity": "sha512-5mbUj3SiZXCuRf9fT3ibzbSSEWiy63gFfksmGfdOzujPjW3k+z8WvIBxcJHBoQNlaZaiyB25deviif2+osLmLw==", + "dev": true, + "license": "Artistic-2.0", + "dependencies": { + "binaryextensions": "^6.11.0", + "editions": "^6.21.0", + "textextensions": "^6.11.0" + }, + "engines": { + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/jackspeak": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", + "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true, + "license": "MIT" }, - "node_modules/isobject": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", - "integrity": "sha1-TkMekrEalzFjaqH5yNHMvP2reN8= sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==", - "devOptional": true, - "engines": { - "node": ">=0.10.0" + "node_modules/js-yaml": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", + "dev": true, + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "node_modules/jsbi": { - "version": "3.1.4", - "resolved": "https://registry.npmjs.org/jsbi/-/jsbi-3.1.4.tgz", - "integrity": "sha512-52QRRFSsi9impURE8ZUbzAMCLjPm4THO7H2fcuIvaaeFTbSysvkodbQQXIVsNgq/ypDbq6dJiuGKL0vZ/i9hUg==", - "dev": true - }, - "node_modules/json-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", - "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", - "dev": true + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true, + "license": "MIT" }, "node_modules/json-stringify-safe": { "version": "5.0.1", @@ -3058,10 +4596,11 @@ "optional": true }, "node_modules/json5": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.2.tgz", - "integrity": "sha512-46Tk9JiOL2z7ytNQWFLpj99RZkVgeHf87yGQKsIkaPz1qSH9UczKH1rO7K3wgRselo0tYMUNfecYpm/p1vC7tQ==", + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", "dev": true, + "license": "MIT", "bin": { "json5": "lib/cli.js" }, @@ -3070,16 +4609,21 @@ } }, "node_modules/jsonc-parser": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.3.0.tgz", - "integrity": "sha512-b0EBt8SWFNnixVdvoR2ZtEGa9ZqLhbJnOjezn+WP+8kspFm+PFYDN8Z4Bc7pRlDjvuVcADSUkroIuTWWn/YiIA==", - "dev": true + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true, + "license": "MIT" }, "node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss= sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.0.tgz", + "integrity": "sha512-FGuPw30AdOIUTRMC2OMRtQV+jkVj2cfPqSeWXv1NEAJ1qZ5zb1X6z1mFhbfOB/iy3ssJCD+3KuZ8r8C3uVFlAg==", "dev": true, + "license": "MIT", + "dependencies": { + "universalify": "^2.0.0" + }, "optionalDependencies": { "graceful-fs": "^4.1.6" } @@ -3101,60 +4645,48 @@ } }, "node_modules/jsonwebtoken/node_modules/jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", + "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", "dev": true, + "license": "MIT", "dependencies": { - "buffer-equal-constant-time": "1.0.1", + "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "node_modules/jsonwebtoken/node_modules/jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.3.tgz", + "integrity": "sha512-byiJ0FLRdLdSVSReO/U4E7RoEyOCKnEnEPMjq3HxWtvzLsV08/i5RQKsFVNkCldrCaPr2vDNAOMsfs8T/Hze7g==", "dev": true, + "license": "MIT", "dependencies": { - "jwa": "^1.4.1", + "jwa": "^1.4.2", "safe-buffer": "^5.0.1" } }, - "node_modules/jsonwebtoken/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dev": true, - "dependencies": { - "lru-cache": "^6.0.0" - }, - "bin": { - "semver": "bin/semver.js" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/jwa": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.0.tgz", - "integrity": "sha512-jrZ2Qx916EA+fq9cEAeCROWPTfCwi1IVHqT2tapuqLEVVDKFDENFw1oL+MwrTvH6msKxsd1YTDVw6uKEcsrLEA==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", "dev": true, + "license": "MIT", "dependencies": { - "buffer-equal-constant-time": "1.0.1", + "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "node_modules/jws": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.0.tgz", - "integrity": "sha512-KDncfTmOZoOMTFG4mBlG0qUIOlc03fmzH+ru6RgYVZhPkyiy/92Owlt/8UEN+a4TXR1FQetfIpJE8ApdvdVxTg==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", "dev": true, "license": "MIT", "dependencies": { - "jwa": "^2.0.0", + "jwa": "^2.0.1", "safe-buffer": "^5.0.1" } }, @@ -3171,12 +4703,13 @@ } }, "node_modules/keyv": { - "version": "4.5.4", - "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", - "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "version": "5.5.5", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.5.5.tgz", + "integrity": "sha512-FA5LmZVF1VziNc0bIdCSA1IoSVnDCqE8HJIZZv2/W8YmoAM50+tnUgJR/gQZwEeIMleuIOnRnHA/UaZRNeV4iQ==", "dev": true, + "license": "MIT", "dependencies": { - "json-buffer": "3.0.1" + "@keyv/serialize": "^1.1.1" } }, "node_modules/leven": { @@ -3189,19 +4722,28 @@ } }, "node_modules/linkify-it": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", - "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", "dev": true, + "license": "MIT", "dependencies": { - "uc.micro": "^1.0.1" + "uc.micro": "^2.0.0" } }, "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "dev": true, + "license": "MIT" + }, + "node_modules/lodash.clonedeep": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", + "integrity": "sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", + "dev": true, + "license": "MIT" }, "node_modules/lodash.mergewith": { "version": "4.6.2", @@ -3209,20 +4751,31 @@ "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==", "dev": true }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true, + "license": "MIT" + }, "node_modules/lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", + "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", "dev": true, + "license": "MIT", "engines": { - "node": ">=8" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "devOptional": true, + "dev": true, "dependencies": { "yallist": "^4.0.0" }, @@ -3231,28 +4784,21 @@ } }, "node_modules/markdown-it": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", - "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^2.0.1", - "entities": "~2.1.0", - "linkify-it": "^3.0.1", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" }, "bin": { - "markdown-it": "bin/markdown-it.js" - } - }, - "node_modules/markdown-it/node_modules/entities": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", - "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", - "dev": true, - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" + "markdown-it": "bin/markdown-it.mjs" } }, "node_modules/matcher": { @@ -3281,11 +4827,22 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/mdurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", - "dev": true + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "dev": true, + "license": "MIT" }, "node_modules/merge-stream": { "version": "2.0.0", @@ -3293,25 +4850,80 @@ "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, "node_modules/mime": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", - "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-4.1.0.tgz", + "integrity": "sha512-X5ju04+cAzsojXKes0B/S4tcYtFAJ6tTMuSPBEn9CPGlrWr8Fiw7qYeLT0XyH80HSoAoqWCaz+MWKh22P7G1cw==", "dev": true, + "funding": [ + "https://github.com/sponsors/broofa" + ], + "license": "MIT", "bin": { - "mime": "cli.js" + "mime": "bin/cli.js" }, "engines": { - "node": ">=4" + "node": ">=16" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" } }, "node_modules/mimic-response": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", - "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", + "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", "dev": true, + "license": "MIT", "engines": { - "node": ">=4" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/minimatch": { @@ -3330,19 +4942,31 @@ "version": "1.2.6", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==", - "devOptional": true + "dev": true + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } }, "node_modules/mkdirp-classic": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", - "devOptional": true + "dev": true, + "optional": true }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" }, "node_modules/mute-stream": { "version": "0.0.8", @@ -3350,23 +4974,19 @@ "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", "dev": true }, - "node_modules/nan": { - "version": "2.19.0", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.19.0.tgz", - "integrity": "sha512-nO1xXxfh/RWNxfd/XPfbIfFk5vgLsAxUR9y5O0cHMJu/AW9U95JLXqthYHjEp+8gQ5p96K9jUp8nbVOxCdRbtw==", - "devOptional": true - }, "node_modules/napi-build-utils": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==", - "devOptional": true + "dev": true, + "optional": true }, "node_modules/node-abi": { "version": "3.30.0", "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.30.0.tgz", "integrity": "sha512-qWO5l3SCqbwQavymOmtTVuCWZE23++S+rxyoHjXqUmPyzRcaoI4lA2gO55/drddGnedAyjA7sk76SfQ5lfUMnw==", - "devOptional": true, + "dev": true, + "optional": true, "dependencies": { "semver": "^7.3.5" }, @@ -3374,33 +4994,74 @@ "node": ">=10" } }, - "node_modules/node-abi/node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", + "node_modules/node-addon-api": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", + "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==", + "dev": true, + "license": "MIT", + "optional": true + }, + "node_modules/node-gyp-build": { + "version": "4.8.4", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.8.4.tgz", + "integrity": "sha512-LA4ZjwlnUblHVgq0oBF3Jl/6h/Nvs5fzBLwdEF4nuxnFdsfajde4WfxtJr3CaiH+F6ewcIB/q4jQ4UzPyid+CQ==", "devOptional": true, + "license": "MIT", + "bin": { + "node-gyp-build": "bin.js", + "node-gyp-build-optional": "optional.js", + "node-gyp-build-test": "build-test.js" + } + }, + "node_modules/node-sarif-builder": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/node-sarif-builder/-/node-sarif-builder-3.4.0.tgz", + "integrity": "sha512-tGnJW6OKRii9u/b2WiUViTJS+h7Apxx17qsMUjsUeNDiMMX5ZFf8F8Fcz7PAQ6omvOxHZtvDTmOYKJQwmfpjeg==", + "dev": true, + "license": "MIT", "dependencies": { - "lru-cache": "^6.0.0" + "@types/sarif": "^2.1.7", + "fs-extra": "^11.1.1" }, - "bin": { - "semver": "bin/semver.js" + "engines": { + "node": ">=20" + } + }, + "node_modules/normalize-package-data": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz", + "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "hosted-git-info": "^7.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" }, "engines": { - "node": ">=10" + "node": "^16.14.0 || >=18.0.0" } }, - "node_modules/node-abort-controller": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/node-abort-controller/-/node-abort-controller-3.1.1.tgz", - "integrity": "sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==", - "dev": true + "node_modules/normalize-package-data/node_modules/hosted-git-info": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", + "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", + "dev": true, + "license": "ISC", + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } }, - "node_modules/node-addon-api": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", - "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==", + "node_modules/normalize-package-data/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", "dev": true, - "optional": true + "license": "ISC" }, "node_modules/normalize-path": { "version": "3.0.0", @@ -3412,12 +5073,13 @@ } }, "node_modules/normalize-url": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-6.1.0.tgz", - "integrity": "sha512-DlL+XwOy3NxAQ8xuC0okPgK46iuVNAK01YN7RueYBqqFeGsBjV9XmCAzAdgt+667bCl5kPh9EqKKDwnaPG1I7A==", + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.1.1.tgz", + "integrity": "sha512-JYc0DPlpGWB40kH5g07gGTrYuMqV653k3uBKY6uITPWds3M0ov3GaWGp9lbE3Bzngx8+XkfzgvASb9vk9JDFXQ==", "dev": true, + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=14.16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -3445,10 +5107,11 @@ } }, "node_modules/object-inspect": { - "version": "1.13.2", - "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.2.tgz", - "integrity": "sha512-IRZSRuzJiynemAXPYtPe5BoI/RESNYR7TYm50MC5Mqbd3Jmw5y790sErYw3V6SryFJD64b74qQQs9wn5Bg/k3g==", + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -3470,47 +5133,101 @@ "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E= sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "devOptional": true, + "dev": true, "dependencies": { "wrappy": "1" } }, "node_modules/open": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.0.tgz", - "integrity": "sha512-XgFPPM+B28FtCCgSb9I+s9szOC1vZRSwgWsRUA5ylIxRTgKozqjOCrVOqGsYABPYK5qnfqClxZTFBa8PKt2v6Q==", + "version": "10.2.0", + "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", + "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", "dev": true, + "license": "MIT", "dependencies": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" + "default-browser": "^5.2.1", + "define-lazy-prop": "^3.0.0", + "is-inside-container": "^1.0.0", + "wsl-utils": "^0.1.0" }, "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-cancelable": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-2.1.1.tgz", - "integrity": "sha512-BZOr3nRQHOntUjTrH8+Lh54smKHoHyur8We1V8DSMVrl5A2malOOwuJRnKRDjSnkoeBh4at6BwEnb5I7Jl31wg==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-4.0.1.tgz", + "integrity": "sha512-wBowNApzd45EIKdO1LaU+LrMBwAcjfPaYtVzV3lmfM3gf8Z4CHZsiIqlM8TZZ8okYvh5A1cP6gTfCRQtwUpaUg==", "dev": true, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=14.16" + } + }, + "node_modules/p-limit": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", + "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "dev": true, + "dependencies": { + "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.0.4.tgz", + "integrity": "sha512-tkAQEw8ysMzmkhgw8k+1U/iPhWNhykKnSk4Rd5zLoPJCuJaGRPo6YposrZgaxHKzDHdDWWZvE/Sk7hsL2X/CpQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/parse-json": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz", + "integrity": "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.26.2", + "index-to-position": "^1.1.0", + "type-fest": "^4.39.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "node_modules/parse-json/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", "dev": true, - "dependencies": { - "yocto-queue": "^0.1.0" - }, + "license": "(MIT OR CC0-1.0)", "engines": { - "node": ">=10" + "node": ">=16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -3586,17 +5303,66 @@ "node": ">=8" } }, + "node_modules/path-scurry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz", + "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/path-type": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-6.0.0.tgz", + "integrity": "sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", - "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA= sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", - "dev": true + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true, + "license": "MIT" + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" }, "node_modules/picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "devOptional": true, + "license": "MIT", "engines": { "node": ">=8.6" }, @@ -3631,7 +5397,7 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-1.0.1.tgz", "integrity": "sha512-L1zP0dk7vGweZME2i+EeakvUNqSrdiI3F91TwEoYiGrAfUXmVv6fJIq4g82PAXxNsWOp0J7ZqQy/3Szz0ajTxA==", - "devOptional": true, + "optional": true, "dependencies": { "ansi-colors": "^1.0.1", "arr-diff": "^4.0.0", @@ -3642,11 +5408,22 @@ "node": ">= 0.10" } }, + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } + }, "node_modules/prebuild-install": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.1.tgz", "integrity": "sha512-jAXscXWMcCK8GgCoHOfIr0ODh5ai8mj63L2nWrjuAgXE6tDyYGnx4/8o/rCgU+B4JSyZBKbeZqzhtwtC3ovxjw==", - "devOptional": true, + "dev": true, + "optional": true, "dependencies": { "detect-libc": "^2.0.0", "expand-template": "^2.0.3", @@ -3669,10 +5446,11 @@ } }, "node_modules/priorityqueuejs": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/priorityqueuejs/-/priorityqueuejs-1.0.0.tgz", - "integrity": "sha1-LuTyPCVgkT4IwHzlzN1t498sWvg= sha512-lg++21mreCEOuGWTbO5DnJKAdxfjrdN0S9ysoW9SzdSJvbkWpkaDdpG/cdsPCsEnoLUwmd9m3WcZhngW7yKA2g==", - "dev": true + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/priorityqueuejs/-/priorityqueuejs-2.0.0.tgz", + "integrity": "sha512-19BMarhgpq3x4ccvVi8k2QpJZcymo/iFUcrhPd4V96kYGovOdTsWwy7fxChYi4QY+m2EnGBWSX9Buakz+tWNQQ==", + "dev": true, + "license": "MIT" }, "node_modules/process-nextick-args": { "version": "2.0.1", @@ -3700,19 +5478,30 @@ "version": "3.0.0", "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "devOptional": true, + "dev": true, "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/qs": { - "version": "6.13.0", - "resolved": "https://registry.npmjs.org/qs/-/qs-6.13.0.tgz", - "integrity": "sha512-+38qI9SOr8tfZ4QmJNplMUxqjbe7LKvvZgWdExBOmd+egZTtjLB67Gu0HRX3u/XOq7UU2Nx6nsjvS16Z9uwfpg==", + "version": "6.14.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.14.1.tgz", + "integrity": "sha512-4EK3+xJl8Ts67nLYNwqw/dsFVnCf+qR7RgXSK9jEEm9unao3njwMDdmsdvoKBKHzxd7tCYz5e5M+SnMjdtXGQQ==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "side-channel": "^1.0.6" + "side-channel": "^1.1.0" }, "engines": { "node": ">=0.6" @@ -3721,11 +5510,33 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, "node_modules/quick-lru": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", "integrity": "sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -3737,7 +5548,8 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", - "devOptional": true, + "dev": true, + "optional": true, "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", @@ -3748,6 +5560,19 @@ "rc": "cli.js" } }, + "node_modules/rc-config-loader": { + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/rc-config-loader/-/rc-config-loader-4.1.3.tgz", + "integrity": "sha512-kD7FqML7l800i6pS6pvLyIE2ncbk9Du8Q0gp/4hMPhJU6ZxApkoLcGD8ZeqgiAlfwZ6BlETq6qqe+12DUL207w==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.3.4", + "js-yaml": "^4.1.0", + "json5": "^2.2.2", + "require-from-string": "^2.0.2" + } + }, "node_modules/read": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", @@ -3760,6 +5585,52 @@ "node": ">=0.8" } }, + "node_modules/read-pkg": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-9.0.1.tgz", + "integrity": "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/normalize-package-data": "^2.4.3", + "normalize-package-data": "^6.0.0", + "parse-json": "^8.0.0", + "type-fest": "^4.6.0", + "unicorn-magic": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg/node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg/node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/readable-stream": { "version": "3.6.0", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", @@ -3796,29 +5667,109 @@ "version": "1.0.1", "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz", "integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==", - "devOptional": true, + "optional": true, "engines": { "node": ">= 0.10" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/resolve-alpn": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/resolve-alpn/-/resolve-alpn-1.2.1.tgz", "integrity": "sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/responselike": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", - "integrity": "sha512-4gl03wn3hj1HP3yzgdI7d3lCkF95F21Pz4BPGvKHinyQzALR5CapwC8yIi0Rh58DEMQ/SguC03wFj2k0M/mHhw==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-4.0.2.tgz", + "integrity": "sha512-cGk8IbWEAnaCpdAt1BHzJ3Ahz5ewDJa0KseTsE3qIRMJ3C698W8psM7byCeWVpd/Ha7FUYzuRVzXoKoM6nRUbA==", "dev": true, + "license": "MIT", "dependencies": { - "lowercase-keys": "^2.0.0" + "lowercase-keys": "^3.0.0" + }, + "engines": { + "node": ">=20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.1.2.tgz", + "integrity": "sha512-cFCkPslJv7BAXJsYlK1dZsbP8/ZNLkCAQ0bi1hf5EKX2QHegmDFEFA6QhuYJlk7UDdc+02JjO80YSOrWPpw06g==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "glob": "^13.0.0", + "package-json-from-dist": "^1.0.1" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.0.tgz", + "integrity": "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.1.1", + "minipass": "^7.1.2", + "path-scurry": "^2.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/brace-expansion": "^5.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/roarr": { "version": "2.15.4", "resolved": "https://registry.npmjs.org/roarr/-/roarr-2.15.4.tgz", @@ -3837,6 +5788,43 @@ "node": ">=8.0" } }, + "node_modules/run-applescript": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, "node_modules/safe-buffer": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", @@ -3849,6 +5837,28 @@ "integrity": "sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==", "dev": true }, + "node_modules/secretlint": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/secretlint/-/secretlint-10.2.2.tgz", + "integrity": "sha512-xVpkeHV/aoWe4vP4TansF622nBEImzCY73y/0042DuJ29iKIaqgoJ8fGxre3rVSHHbxar4FdJobmTnLp9AU0eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@secretlint/config-creator": "^10.2.2", + "@secretlint/formatter": "^10.2.2", + "@secretlint/node": "^10.2.2", + "@secretlint/profiler": "^10.2.2", + "debug": "^4.4.1", + "globby": "^14.1.0", + "read-pkg": "^9.0.1" + }, + "bin": { + "secretlint": "bin/secretlint.js" + }, + "engines": { + "node": ">=20.0.0" + } + }, "node_modules/semaphore": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/semaphore/-/semaphore-1.1.0.tgz", @@ -3859,12 +5869,16 @@ } }, "node_modules/semver": { - "version": "6.3.1", - "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", - "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, "node_modules/semver-compare": { @@ -3890,23 +5904,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/set-function-length": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", - "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", - "dev": true, - "dependencies": { - "define-data-property": "^1.1.4", - "es-errors": "^1.3.0", - "function-bind": "^1.1.2", - "get-intrinsic": "^1.2.4", - "gopd": "^1.0.1", - "has-property-descriptors": "^1.0.2" - }, - "engines": { - "node": ">= 0.4" - } - }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -3925,19 +5922,77 @@ "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, "engines": { - "node": ">=8" + "node": ">=8" + } + }, + "node_modules/side-channel": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", + "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3", + "side-channel-list": "^1.0.0", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", + "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/side-channel": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.0.6.tgz", - "integrity": "sha512-fDW/EZ6Q9RiO8eFG8Hj+7u/oW+XrPTIChwCOM2+th2A6OblDtYYIpve9m+KvI9Z4C9qSEXlaGR6bTEYHReuglA==", + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", "dev": true, + "license": "MIT", "dependencies": { - "call-bind": "^1.0.7", + "call-bound": "^1.0.2", "es-errors": "^1.3.0", - "get-intrinsic": "^1.2.4", - "object-inspect": "^1.13.1" + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" }, "engines": { "node": ">= 0.4" @@ -3946,11 +6001,24 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/simple-concat": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", - "devOptional": true, + "dev": true, "funding": [ { "type": "github", @@ -3964,13 +6032,14 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "optional": true }, "node_modules/simple-get": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", - "devOptional": true, + "dev": true, "funding": [ { "type": "github", @@ -3985,21 +6054,126 @@ "url": "https://feross.org/support" } ], + "optional": true, "dependencies": { "decompress-response": "^6.0.0", "once": "^1.3.1", "simple-concat": "^1.0.0" } }, + "node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/slice-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/slice-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", "dev": true, + "license": "BSD-3-Clause", "engines": { - "node": ">=0.10.0" + "node": ">= 12" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true, + "license": "CC-BY-3.0" + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" } }, + "node_modules/spdx-license-ids": { + "version": "3.0.22", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.22.tgz", + "integrity": "sha512-4PRT4nh1EImPbt2jASOKHX7PB7I+e4IWNLvkKFDxNhJlfjbYlleYQh285Z/3mPTHSAK/AvdMmw5BNNuYH8ShgQ==", + "dev": true, + "license": "CC0-1.0" + }, "node_modules/sprintf-js": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.2.tgz", @@ -4007,16 +6181,6 @@ "dev": true, "optional": true }, - "node_modules/stoppable": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/stoppable/-/stoppable-1.1.0.tgz", - "integrity": "sha512-KXDYZ9dszj6bzvnEMRYvxgeTHU74QBFL54XKtP3nyMuJ81CFYtABZ3bAzL2EdFUaEwJOBOgENyFj3R7oTzDyyw==", - "dev": true, - "engines": { - "node": ">=4", - "npm": ">=6" - } - }, "node_modules/stream-exhaust": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz", @@ -4029,6 +6193,18 @@ "integrity": "sha512-AiisoFqQ0vbGcZgQPY1cdP2I76glaVA/RauYR4G4thNFgkTqr90yXTo4LYX60Jl+sIlPNHHdGSwo01AvbKUSVQ==", "dev": true }, + "node_modules/streamx": { + "version": "2.23.0", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.23.0.tgz", + "integrity": "sha512-kn+e44esVfn2Fa/O0CPFcex27fjIL6MkVae0Mm6q+E6f0hWv578YCERbv+4m02cjxvDsPKLnmxral/rR6lBMAg==", + "dev": true, + "license": "MIT", + "dependencies": { + "events-universal": "^1.0.0", + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" + } + }, "node_modules/string_decoder": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", @@ -4038,6 +6214,123 @@ "safe-buffer": "~5.1.0" } }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs": { + "name": "string-width", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "license": "MIT", + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.2.tgz", + "integrity": "sha512-gmBGslpoQJtgnMAvOVqGZpEz9dyoKTCzy2nfz/n8aIFhN/jCE/rCmcxabB6jOOHV+0WNnylOxaxBQPSvcWklhA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/strip-bom": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", @@ -4079,18 +6372,35 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo= sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", - "devOptional": true, + "dev": true, + "optional": true, "engines": { "node": ">=0.10.0" } }, "node_modules/strnum": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/strnum/-/strnum-1.0.5.tgz", - "integrity": "sha512-J8bbNyKKXl5qYcR36TIO8W3mVGVHrmmxsd5PAItGkmyzwJvybiw2IVq5nqd0i4LSNSkB/sx9VHllbfFdr9k1JA==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/strnum/-/strnum-2.1.2.tgz", + "integrity": "sha512-l63NF9y/cLROq/yqKXSLtcMeeyOfnSQlfMSlzFt/K73oIaD8DGaQWd7Z34X9GPiKqP5rbSh84Hl4bOlLcjiSrQ==", "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/NaturalIntelligence" + } + ], "license": "MIT" }, + "node_modules/structured-source": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/structured-source/-/structured-source-4.0.0.tgz", + "integrity": "sha512-qGzRFNJDjFieQkl/sVOI2dUjHKRyL9dAJi2gCPGJLbJHBIkyOHxjuocpIEfbLioX+qSJpvbYdT49/YCdMznKxA==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "boundary": "^2.0.0" + } + }, "node_modules/sumchecker": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/sumchecker/-/sumchecker-3.0.1.tgz", @@ -4115,11 +6425,93 @@ "node": ">=4" } }, + "node_modules/supports-hyperlinks": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.2.0.tgz", + "integrity": "sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=14.18" + }, + "funding": { + "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1" + } + }, + "node_modules/supports-hyperlinks/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/table": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz", + "integrity": "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/table/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/tar-fs": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", - "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", - "devOptional": true, + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", + "dev": true, + "license": "MIT", + "optional": true, "dependencies": { "chownr": "^1.1.1", "mkdirp-classic": "^0.5.2", @@ -4131,16 +6523,44 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", - "devOptional": true, + "dev": true, + "optional": true, + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/teex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz", + "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==", + "dev": true, + "license": "MIT", "dependencies": { - "bl": "^4.0.3", - "end-of-stream": "^1.4.1", - "fs-constants": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^3.1.1" + "streamx": "^2.12.5" + } + }, + "node_modules/terminal-link": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-4.0.0.tgz", + "integrity": "sha512-lk+vH+MccxNqgVqSnkMVKx4VLJfnLjDBGzH16JVZjKE2DoxP57s6/vt6JmXV5I3jBcfGrxNrYtC+mPtU7WJztA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-escapes": "^7.0.0", + "supports-hyperlinks": "^3.2.0" }, "engines": { - "node": ">=6" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/ternary-stream": { @@ -4165,6 +6585,39 @@ "readable-stream": "2 || 3" } }, + "node_modules/text-decoder": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.3.tgz", + "integrity": "sha512-3/o9z3X0X0fTupwsYvR03pJ/DjWuqqrfwBgTQzdWDiQSm9KitAyz/9WqsT2JQW7KV2m+bC2ol/zqpW37NHxLaA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.4" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT" + }, + "node_modules/textextensions": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-6.11.0.tgz", + "integrity": "sha512-tXJwSr9355kFJI3lbCkPpUH5cP8/M0GGy2xLO34aZCjMXBaK3SoPnZwr/oWmo1FdCnELcs4npdCIOFtq9W3ruQ==", + "dev": true, + "license": "Artistic-2.0", + "dependencies": { + "editions": "^6.21.0" + }, + "engines": { + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, "node_modules/through": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", @@ -4190,10 +6643,11 @@ } }, "node_modules/tmp": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", - "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "version": "0.2.5", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", + "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", "dev": true, + "license": "MIT", "engines": { "node": ">=14.14" } @@ -4211,38 +6665,101 @@ } }, "node_modules/tree-sitter": { - "version": "0.20.6", - "resolved": "https://registry.npmjs.org/tree-sitter/-/tree-sitter-0.20.6.tgz", - "integrity": "sha512-GxJodajVpfgb3UREzzIbtA1hyRnTxVbWVXrbC6sk4xTMH5ERMBJk9HJNq4c8jOJeUaIOmLcwg+t6mez/PDvGqg==", - "devOptional": true, + "version": "0.25.0", + "resolved": "https://registry.npmjs.org/tree-sitter/-/tree-sitter-0.25.0.tgz", + "integrity": "sha512-PGZZzFW63eElZJDe/b/R/LbsjDDYJa5UEjLZJB59RQsMX+fo0j54fqBPn1MGKav/QNa0JR0zBiVaikYDWCj5KQ==", + "dev": true, + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "node-addon-api": "^8.3.0", + "node-gyp-build": "^4.8.4" + } + }, + "node_modules/tree-sitter-javascript": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/tree-sitter-javascript/-/tree-sitter-javascript-0.23.1.tgz", + "integrity": "sha512-/bnhbrTD9frUYHQTiYnPcxyHORIw157ERBa6dqzaKxvR/x3PC4Yzd+D1pZIMS6zNg2v3a8BZ0oK7jHqsQo9fWA==", "hasInstallScript": true, + "license": "MIT", + "optional": true, "dependencies": { - "nan": "^2.18.0", - "prebuild-install": "^7.1.1" + "node-addon-api": "^8.2.2", + "node-gyp-build": "^4.8.2" + }, + "peerDependencies": { + "tree-sitter": "^0.21.1" + }, + "peerDependenciesMeta": { + "tree-sitter": { + "optional": true + } + } + }, + "node_modules/tree-sitter-javascript/node_modules/node-addon-api": { + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.3.1.tgz", + "integrity": "sha512-lytcDEdxKjGJPTLEfW4mYMigRezMlyJY8W4wxJK8zE533Jlb8L8dRuObJFWg2P+AuOIxoCgKF+2Oq4d4Zd0OUA==", + "license": "MIT", + "optional": true, + "engines": { + "node": "^18 || ^20 || >= 21" } }, "node_modules/tree-sitter-typescript": { - "version": "0.20.5", - "resolved": "https://registry.npmjs.org/tree-sitter-typescript/-/tree-sitter-typescript-0.20.5.tgz", - "integrity": "sha512-RzK/Pc6k4GiXvInIBlo8ZggekP6rODfW2P6KHFCTSUHENsw6ynh+iacFhfkJRa4MT8EIN2WHygFJ7076/+eHKg==", + "version": "0.23.2", + "resolved": "https://registry.npmjs.org/tree-sitter-typescript/-/tree-sitter-typescript-0.23.2.tgz", + "integrity": "sha512-e04JUUKxTT53/x3Uq1zIL45DoYKVfHH4CZqwgZhPg5qYROl5nQjV+85ruFzFGZxu+QeFVbRTPDRnqL9UbU4VeA==", "hasInstallScript": true, + "license": "MIT", "optional": true, "dependencies": { - "nan": "^2.18.0", - "tree-sitter": "^0.20.6" + "node-addon-api": "^8.2.2", + "node-gyp-build": "^4.8.2", + "tree-sitter-javascript": "^0.23.1" + }, + "peerDependencies": { + "tree-sitter": "^0.21.0" + }, + "peerDependenciesMeta": { + "tree-sitter": { + "optional": true + } + } + }, + "node_modules/tree-sitter-typescript/node_modules/node-addon-api": { + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.3.1.tgz", + "integrity": "sha512-lytcDEdxKjGJPTLEfW4mYMigRezMlyJY8W4wxJK8zE533Jlb8L8dRuObJFWg2P+AuOIxoCgKF+2Oq4d4Zd0OUA==", + "license": "MIT", + "optional": true, + "engines": { + "node": "^18 || ^20 || >= 21" + } + }, + "node_modules/tree-sitter/node_modules/node-addon-api": { + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.3.1.tgz", + "integrity": "sha512-lytcDEdxKjGJPTLEfW4mYMigRezMlyJY8W4wxJK8zE533Jlb8L8dRuObJFWg2P+AuOIxoCgKF+2Oq4d4Zd0OUA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18 || ^20 || >= 21" } }, "node_modules/tslib": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", - "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", - "dev": true + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" }, "node_modules/tunnel": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.6.11 <=0.7.0 || >=0.7.3" } @@ -4251,7 +6768,8 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", - "devOptional": true, + "dev": true, + "optional": true, "dependencies": { "safe-buffer": "^5.0.1" }, @@ -4277,6 +6795,7 @@ "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.8.11.tgz", "integrity": "sha512-5UvfMpd1oelmUPRbbaVnq+rHP7ng2cE4qoQkQeAqxRL6PklkxsM0g32/HL0yfvruK6ojQ5x8EE+HF4YV6DtuCA==", "dev": true, + "license": "MIT", "dependencies": { "qs": "^6.9.1", "tunnel": "0.0.6", @@ -4284,36 +6803,47 @@ } }, "node_modules/uc.micro": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", - "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", - "dev": true + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "dev": true, + "license": "MIT" }, "node_modules/underscore": { "version": "1.13.7", "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.7.tgz", "integrity": "sha512-GMXzWtsc57XAtguZgaQViUOzs0KTkk8ojr3/xAxXLITqf/3EMwxC0inyETfDFjH/Krbhuep0HNbbjI9i/q3F3g==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" }, - "node_modules/universal-user-agent": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", - "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==", - "dev": true + "node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", "dev": true, + "license": "MIT", "engines": { - "node": ">= 4.0.0" + "node": ">= 10.0.0" } }, "node_modules/url-join": { @@ -4337,11 +6867,35 @@ "uuid": "dist/bin/uuid" } }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/version-range": { + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/version-range/-/version-range-4.15.0.tgz", + "integrity": "sha512-Ck0EJbAGxHwprkzFO966t4/5QkRuzh+/I1RxhLgUKKwEn+Cd8NwM60mE3AqBZg5gYODoXW0EFsQvbZjRlvdqbg==", + "dev": true, + "license": "Artistic-2.0", + "engines": { + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, "node_modules/vinyl": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-2.2.1.tgz", "integrity": "sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw==", - "devOptional": true, + "optional": true, "dependencies": { "clone": "^2.1.1", "clone-buffer": "^1.0.0", @@ -4417,40 +6971,15 @@ } }, "node_modules/vscode-universal-bundler/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } }, - "node_modules/vscode-universal-bundler/node_modules/fs-extra": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.2.0.tgz", - "integrity": "sha512-PmDi3uwK5nFuXh7XDTlVnS17xJS7vW36is2+w3xcv8SVxiB4NyATf4ctkVY5bkSjX0Y4nbvZCq1/EjtEyr9ktw==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.2.0", - "jsonfile": "^6.0.1", - "universalify": "^2.0.0" - }, - "engines": { - "node": ">=14.14" - } - }, - "node_modules/vscode-universal-bundler/node_modules/jsonfile": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.1.0.tgz", - "integrity": "sha512-5dgndWOriYSm5cnYaJNhalLNDKOqFwyDB/rr1E9ZsGciGvKPs8R2xYGCacuf3z6K1YKDz182fd+fY3cn3pMqXQ==", - "dev": true, - "dependencies": { - "universalify": "^2.0.0" - }, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, "node_modules/vscode-universal-bundler/node_modules/minimatch": { "version": "9.0.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", @@ -4466,15 +6995,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/vscode-universal-bundler/node_modules/universalify": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.0.tgz", - "integrity": "sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==", - "dev": true, - "engines": { - "node": ">= 10.0.0" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -4491,16 +7011,167 @@ } }, "node_modules/workerpool": { - "version": "6.5.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.0.tgz", - "integrity": "sha512-r64Ea3glXY2RVzMeNxB+4J+0YHAVzUdV4cM5nHi4BBC2LvnO1pWFAIYKYuGcPElbg1/7eEiaPtZ/jzCjIUuGBg==", - "dev": true + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-10.0.1.tgz", + "integrity": "sha512-NAnKwZJxWlj/U1cp6ZkEtPE+GQY1S6KtOS3AlCiPfPFLxV3m64giSp7g2LsNJxzYCocDT7TSl+7T0sgrDp3KoQ==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/wrap-ansi-cjs/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi-cjs/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "6.2.3", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.3.tgz", + "integrity": "sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true, + "license": "MIT" + }, + "node_modules/wrap-ansi/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "devOptional": true + "dev": true + }, + "node_modules/wsl-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", + "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/xml2js": { "version": "0.5.0", @@ -4546,16 +7217,20 @@ "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "devOptional": true + "dev": true }, "node_modules/yauzl": { - "version": "2.10.0", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", - "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk= sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.2.0.tgz", + "integrity": "sha512-Ow9nuGZE+qp1u4JIPvg+uCiUr7xGQWdff7JQSk5VGYTAZMDe2q8lxJ10ygv10qmSj031Ty/6FNJpLO4o1Sgc+w==", "dev": true, + "license": "MIT", "dependencies": { "buffer-crc32": "~0.2.3", - "fd-slicer": "~1.1.0" + "pend": "~1.2.0" + }, + "engines": { + "node": ">=12" } }, "node_modules/yocto-queue": { diff --git a/build/package.json b/build/package.json index 3a435a8326bf..398a3074dcb8 100644 --- a/build/package.json +++ b/build/package.json @@ -3,58 +3,58 @@ "version": "1.0.0", "license": "MIT", "devDependencies": { - "@azure/core-auth": "^1.9.0", - "@azure/cosmos": "^3", - "@azure/identity": "^4.2.1", - "@azure/msal-node": "^2.16.1", - "@azure/storage-blob": "^12.25.0", - "@electron/get": "^2.0.0", - "@types/ansi-colors": "^3.2.0", - "@types/byline": "^4.2.32", - "@types/debounce": "^1.0.0", - "@types/debug": "^4.1.5", - "@types/fancy-log": "^1.3.0", - "@types/fs-extra": "^9.0.12", - "@types/glob": "^7.1.1", - "@types/gulp": "^4.0.17", - "@types/gulp-filter": "^3.0.32", - "@types/gulp-gzip": "^0.0.31", - "@types/gulp-json-editor": "^2.2.31", - "@types/gulp-rename": "^0.0.33", + "@azure/core-auth": "^1.10.1", + "@azure/cosmos": "^4", + "@azure/identity": "^4.13.0", + "@azure/msal-node": "^5.0.2", + "@azure/storage-blob": "^12.30.0", + "@electron/get": "^4.0.2", + "@types/ansi-colors": "^3.2.6", + "@types/byline": "^4.2.36", + "@types/debounce": "^1.2.4", + "@types/debug": "^4.1.12", + "@types/fancy-log": "^2.0.2", + "@types/fs-extra": "^11.0.4", + "@types/glob": "^9.0.0", + "@types/gulp": "^4.0.18", + "@types/gulp-filter": "^3.0.41", + "@types/gulp-gzip": "^0.0.36", + "@types/gulp-json-editor": "^2.2.36", + "@types/gulp-rename": "^2.0.7", "@types/gulp-sort": "^2.0.4", - "@types/gulp-sourcemaps": "^0.0.32", - "@types/jws": "^3.2.10", - "@types/mime": "0.0.29", - "@types/minimatch": "^3.0.3", - "@types/minimist": "^1.2.1", - "@types/mocha": "^9.1.1", - "@types/node": "20.x", - "@types/pump": "^1.0.1", - "@types/rimraf": "^2.0.4", - "@types/through": "^0.0.29", - "@types/through2": "^2.0.36", - "@types/workerpool": "^6.4.0", - "@types/xml2js": "0.0.33", - "@vscode/iconv-lite-umd": "0.7.0", - "@vscode/ripgrep": "^1.15.10", - "@vscode/vsce": "2.20.1", + "@types/gulp-sourcemaps": "^0.0.38", + "@types/jws": "^3.2.11", + "@types/mime": "4.0.0", + "@types/minimatch": "^6.0.0", + "@types/minimist": "^1.2.5", + "@types/mocha": "^10.0.10", + "@types/node": "25.x", + "@types/pump": "^1.1.3", + "@types/rimraf": "^4.0.5", + "@types/through": "^0.0.33", + "@types/through2": "^2.0.41", + "@types/workerpool": "^9.0.0", + "@types/xml2js": "0.4.14", + "@vscode/iconv-lite-umd": "0.7.1", + "@vscode/ripgrep": "^1.17.0", + "@vscode/vsce": "3.7.1", "byline": "^5.0.0", "debug": "^4.3.2", - "electron-osx-sign": "^0.4.16", - "esbuild": "0.25.0", + "electron-osx-sign": "^0.6.0", + "esbuild": "0.27.2", "extract-zip": "^2.0.1", - "gulp-merge-json": "^2.1.1", + "gulp-merge-json": "^2.2.1", "gulp-sort": "^2.0.0", - "jsonc-parser": "^2.3.0", - "jws": "^4.0.0", - "mime": "^1.4.1", - "source-map": "0.6.1", + "jsonc-parser": "^3.3.1", + "jws": "^4.0.1", + "mime": "^4.1.0", + "source-map": "0.7.6", "ternary-stream": "^3.0.0", "through2": "^4.0.2", - "tree-sitter": "^0.20.5", + "tree-sitter": "^0.25.0", "vscode-universal-bundler": "^0.1.3", - "workerpool": "^6.4.0", - "yauzl": "^2.10.0" + "workerpool": "^10.0.1", + "yauzl": "^3.2.0" }, "type": "commonjs", "scripts": { @@ -63,7 +63,7 @@ "npmCheckJs": "../node_modules/.bin/tsc --noEmit" }, "optionalDependencies": { - "tree-sitter-typescript": "^0.20.5", + "tree-sitter-typescript": "^0.23.2", "vscode-gulp-watch": "^5.0.3" } } diff --git a/build/win32/Cargo.lock b/build/win32/Cargo.lock index 4c169ba0f973..822d94f6cc72 100644 --- a/build/win32/Cargo.lock +++ b/build/win32/Cargo.lock @@ -3,16 +3,47 @@ version = 4 [[package]] -name = "bitflags" -version = "1.3.2" +name = "android_system_properties" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bef38d45163c2f1dde094a7dfd33ccf595c92905c8f8f4fdc18d06fb1037718a" +checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" +dependencies = [ + "libc", +] + +[[package]] +name = "anyhow" +version = "1.0.100" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a23eb6b1614318a8071c9b2521f36b424b2c83db5eb3a0fead4a6c0809af6e61" + +[[package]] +name = "autocfg" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" + +[[package]] +name = "bumpalo" +version = "3.19.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "46c5e41b57b8bba42a04676d81cb89e9ee8e859a1a66f80a5a72e1cb76b34d43" [[package]] name = "byteorder" -version = "1.4.3" +version = "1.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" + +[[package]] +name = "cc" +version = "1.2.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "14c189c53d098945499cdfa7ecc63567cf3886b3332b312a5b4585d8d3a6a610" +checksum = "90583009037521a116abf44494efecd645ba48b6622457080f080b85544e2215" +dependencies = [ + "find-msvc-tools", + "shlex", +] [[package]] name = "cfg-if" @@ -20,20 +51,39 @@ version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +[[package]] +name = "chrono" +version = "0.4.42" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "145052bdd345b87320e369255277e3fb5152762ad123a901ef5c262dd38fe8d2" +dependencies = [ + "iana-time-zone", + "js-sys", + "num-traits", + "wasm-bindgen", + "windows-link", +] + +[[package]] +name = "core-foundation-sys" +version = "0.8.7" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" + [[package]] name = "crc" -version = "3.0.1" +version = "3.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86ec7a15cbe22e59248fc7eadb1907dab5ba09372595da4d73dd805ed4417dfe" +checksum = "5eb8a2a1cd12ab0d987a5d5e825195d372001a4094a0376319d5a0ad71c1ba0d" dependencies = [ "crc-catalog", ] [[package]] name = "crc-catalog" -version = "2.2.0" +version = "2.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cace84e55f07e7301bae1c519df89cdad8cc3cd868413d3fdbdeca9ff3db484" +checksum = "19d374276b40fb8bbdee95aef7c7fa6b5316ec764510eb64b8dd0e2ed0d7e7f5" [[package]] name = "crossbeam-channel" @@ -56,53 +106,60 @@ dependencies = [ ] [[package]] -name = "dirs-next" -version = "2.0.0" +name = "erased-serde" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b98cf8ebf19c3d1b223e151f99a4f9f0690dca41414773390fc824184ac833e1" +checksum = "6c138974f9d5e7fe373eb04df7cae98833802ae4b11c24ac7039a21d5af4b26c" dependencies = [ - "cfg-if", - "dirs-sys-next", + "serde", ] [[package]] -name = "dirs-sys-next" -version = "0.1.2" +name = "find-msvc-tools" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4ebda144c4fe02d1f7ea1a7d9641b6fc6b580adcfa024ae48797ecdeb6825b4d" -dependencies = [ - "libc", - "redox_users", - "winapi", -] +checksum = "3a3076410a55c90011c298b04d0cfa770b00fa04e1e3c97d3f6c9de105a03844" [[package]] -name = "getrandom" -version = "0.2.7" +name = "hermit-abi" +version = "0.3.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4eb1a864a501629691edf6c15a593b7a51eebaa1e8468e9ddc623de7c9b58ec6" +checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" + +[[package]] +name = "iana-time-zone" +version = "0.1.64" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "33e57f83510bb73707521ebaffa789ec8caf86f9657cad665b092b581d40e9fb" dependencies = [ - "cfg-if", - "libc", - "wasi", + "android_system_properties", + "core-foundation-sys", + "iana-time-zone-haiku", + "js-sys", + "log", + "wasm-bindgen", + "windows-core", ] [[package]] -name = "hermit-abi" -version = "0.3.9" +name = "iana-time-zone-haiku" +version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d231dfb89cfffdbc30e7fc41579ed6066ad03abda9e567ccafae602b97ec5024" +checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" +dependencies = [ + "cc", +] [[package]] name = "inno_updater" -version = "0.12.1" +version = "0.13.2" dependencies = [ "byteorder", "crc", "slog", "slog-async", "slog-term", - "windows-sys 0.42.0", + "windows-sys 0.61.2", ] [[package]] @@ -122,12 +179,37 @@ version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "112c678d4050afce233f4f2852bb2eb519230b3cf12f33585275537d7e41578d" +[[package]] +name = "js-sys" +version = "0.3.83" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "464a3709c7f55f1f721e5389aa6ea4e3bc6aba669353300af094b29ffbdde1d8" +dependencies = [ + "once_cell", + "wasm-bindgen", +] + [[package]] name = "libc" version = "0.2.126" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "349d5a591cd28b49e1d1037471617a32ddcda5731b99419008085f72d5a53836" +[[package]] +name = "log" +version = "0.4.29" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" + +[[package]] +name = "num-traits" +version = "0.2.19" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" +dependencies = [ + "autocfg", +] + [[package]] name = "num_threads" version = "0.1.6" @@ -145,59 +227,80 @@ checksum = "7709cef83f0c1f58f666e746a08b21e0085f7440fa6a29cc194d68aac97a4225" [[package]] name = "proc-macro2" -version = "1.0.40" +version = "1.0.103" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dd96a1e8ed2596c337f8eae5f24924ec83f5ad5ab21ea8e455d3566c69fbcaf7" +checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8" dependencies = [ "unicode-ident", ] [[package]] name = "quote" -version = "1.0.20" +version = "1.0.42" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3bcdf212e9776fbcb2d23ab029360416bb1706b1aea2d1a5ba002727cbcab804" +checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f" dependencies = [ "proc-macro2", ] [[package]] -name = "redox_syscall" -version = "0.2.13" +name = "rustversion" +version = "1.0.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "62f25bc4c7e55e0b0b7a1d43fb893f4fa1361d0abe38b9ce4f323c2adfe6ef42" +checksum = "a0a5f7c728f5d284929a1cccb5bc19884422bfe6ef4d6c409da2c41838983fcf" + +[[package]] +name = "serde" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ - "bitflags", + "serde_core", ] [[package]] -name = "redox_users" -version = "0.4.3" +name = "serde_core" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b033d837a7cf162d7993aded9304e30a83213c648b6e389db233191f891e5c2b" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ - "getrandom", - "redox_syscall", - "thiserror", + "serde_derive", ] [[package]] -name = "rustversion" -version = "1.0.7" +name = "serde_derive" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a0a5f7c728f5d284929a1cccb5bc19884422bfe6ef4d6c409da2c41838983fcf" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "shlex" +version = "1.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "slog" -version = "2.7.0" +version = "2.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8347046d4ebd943127157b94d63abb990fcf729dc4e9978927fdf4ac3c998d06" +checksum = "9b3b8565691b22d2bdfc066426ed48f837fc0c5f2c8cad8d9718f7f99d6995c1" +dependencies = [ + "anyhow", + "erased-serde", + "rustversion", + "serde_core", +] [[package]] name = "slog-async" -version = "2.7.0" +version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "766c59b252e62a34651412870ff55d8c4e6d04df19b43eecb2703e417b097ffe" +checksum = "72c8038f898a2c79507940990f05386455b3a317d8f18d4caea7cbc3d5096b84" dependencies = [ "crossbeam-channel", "slog", @@ -207,10 +310,11 @@ dependencies = [ [[package]] name = "slog-term" -version = "2.9.1" +version = "2.9.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6e022d0b998abfe5c3782c1f03551a596269450ccd677ea51c56f8b214610e8" +checksum = "5cb1fc680b38eed6fad4c02b3871c09d2c81db8c96aa4e9c0a34904c830f09b5" dependencies = [ + "chrono", "is-terminal", "slog", "term", @@ -220,9 +324,9 @@ dependencies = [ [[package]] name = "syn" -version = "1.0.98" +version = "2.0.111" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c50aef8a904de4c23c788f104b7dddc7d6f79c647c7c8ce4cc8f73eb0ca773dd" +checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87" dependencies = [ "proc-macro2", "quote", @@ -237,33 +341,11 @@ checksum = "f764005d11ee5f36500a149ace24e00e3da98b0158b3e2d53a7495660d3f4d60" [[package]] name = "term" -version = "0.7.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c59df8ac95d96ff9bede18eb7300b0fda5e5d8d90960e76f8e14ae765eedbf1f" -dependencies = [ - "dirs-next", - "rustversion", - "winapi", -] - -[[package]] -name = "thiserror" -version = "1.0.31" +version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bd829fe32373d27f76265620b5309d0340cb8550f523c1dda251d6298069069a" +checksum = "d8c27177b12a6399ffc08b98f76f7c9a1f4fe9fc967c784c5a071fa8d93cf7e1" dependencies = [ - "thiserror-impl", -] - -[[package]] -name = "thiserror-impl" -version = "1.0.31" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0396bc89e626244658bef819e22d0cc459e795a5ebe878e6ec336d1674a8d79a" -dependencies = [ - "proc-macro2", - "quote", - "syn", + "windows-sys 0.61.2", ] [[package]] @@ -295,51 +377,112 @@ checksum = "42657b1a6f4d817cda8e7a0ace261fe0cc946cf3a80314390b22cc61ae080792" [[package]] name = "unicode-ident" -version = "1.0.1" +version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5bd2fe26506023ed7b5e1e315add59d6f584c621d037f9368fea9cfb988f368c" +checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5" [[package]] -name = "wasi" -version = "0.11.0+wasi-snapshot-preview1" +name = "wasm-bindgen" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +checksum = "0d759f433fa64a2d763d1340820e46e111a7a5ab75f993d1852d70b03dbb80fd" +dependencies = [ + "cfg-if", + "once_cell", + "rustversion", + "wasm-bindgen-macro", + "wasm-bindgen-shared", +] [[package]] -name = "winapi" -version = "0.3.9" +name = "wasm-bindgen-macro" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419" +checksum = "48cb0d2638f8baedbc542ed444afc0644a29166f1595371af4fecf8ce1e7eeb3" dependencies = [ - "winapi-i686-pc-windows-gnu", - "winapi-x86_64-pc-windows-gnu", + "quote", + "wasm-bindgen-macro-support", ] [[package]] -name = "winapi-i686-pc-windows-gnu" -version = "0.4.0" +name = "wasm-bindgen-macro-support" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" +checksum = "cefb59d5cd5f92d9dcf80e4683949f15ca4b511f4ac0a6e14d4e1ac60c6ecd40" +dependencies = [ + "bumpalo", + "proc-macro2", + "quote", + "syn", + "wasm-bindgen-shared", +] [[package]] -name = "winapi-x86_64-pc-windows-gnu" -version = "0.4.0" +name = "wasm-bindgen-shared" +version = "0.2.106" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" +checksum = "cbc538057e648b67f72a982e708d485b2efa771e1ac05fec311f9f63e5800db4" +dependencies = [ + "unicode-ident", +] [[package]] -name = "windows-sys" -version = "0.42.0" +name = "windows-core" +version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5a3e1820f08b8513f676f7ab6c1f99ff312fb97b553d30ff4dd86f9f15728aa7" +checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ - "windows_aarch64_gnullvm 0.42.2", - "windows_aarch64_msvc 0.42.2", - "windows_i686_gnu 0.42.2", - "windows_i686_msvc 0.42.2", - "windows_x86_64_gnu 0.42.2", - "windows_x86_64_gnullvm 0.42.2", - "windows_x86_64_msvc 0.42.2", + "windows-implement", + "windows-interface", + "windows-link", + "windows-result", + "windows-strings", +] + +[[package]] +name = "windows-implement" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-interface" +version = "0.59.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" +dependencies = [ + "proc-macro2", + "quote", + "syn", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-result" +version = "0.4.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" +dependencies = [ + "windows-link", +] + +[[package]] +name = "windows-strings" +version = "0.5.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" +dependencies = [ + "windows-link", ] [[package]] @@ -351,52 +494,43 @@ dependencies = [ "windows-targets", ] +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", +] + [[package]] name = "windows-targets" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", + "windows_aarch64_gnullvm", + "windows_aarch64_msvc", + "windows_i686_gnu", "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "windows_i686_msvc", + "windows_x86_64_gnu", + "windows_x86_64_gnullvm", + "windows_x86_64_msvc", ] -[[package]] -name = "windows_aarch64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "597a5118570b68bc08d8d59125332c54f1ba9d9adeedeef5b99b02ba2b0698f8" - [[package]] name = "windows_aarch64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" -[[package]] -name = "windows_aarch64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e08e8864a60f06ef0d0ff4ba04124db8b0fb3be5776a5cd47641e942e58c4d43" - [[package]] name = "windows_aarch64_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" -[[package]] -name = "windows_i686_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c61d927d8da41da96a81f029489353e68739737d3beca43145c8afec9a31a84f" - [[package]] name = "windows_i686_gnu" version = "0.52.5" @@ -409,48 +543,24 @@ version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" -[[package]] -name = "windows_i686_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44d840b6ec649f480a41c8d80f9c65108b92d89345dd94027bfe06ac444d1060" - [[package]] name = "windows_i686_msvc" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" -[[package]] -name = "windows_x86_64_gnu" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8de912b8b8feb55c064867cf047dda097f92d51efad5b491dfb98f6bbb70cb36" - [[package]] name = "windows_x86_64_gnu" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" -[[package]] -name = "windows_x86_64_gnullvm" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26d41b46a36d453748aedef1486d5c7a85db22e56aff34643984ea85514e94a3" - [[package]] name = "windows_x86_64_gnullvm" version = "0.52.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" -[[package]] -name = "windows_x86_64_msvc" -version = "0.42.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9aec5da331524158c6d1a4ac0ab1541149c0b9505fde06423b02f5ef0106b9f0" - [[package]] name = "windows_x86_64_msvc" version = "0.52.5" diff --git a/build/win32/Cargo.toml b/build/win32/Cargo.toml index e2130dd2bfae..41e838baaf94 100644 --- a/build/win32/Cargo.toml +++ b/build/win32/Cargo.toml @@ -1,18 +1,18 @@ [package] name = "inno_updater" -version = "0.12.1" +version = "0.13.2" authors = ["Microsoft "] build = "build.rs" [dependencies] -byteorder = "1.4.3" -crc = "3.0.1" -slog = "2.7.0" -slog-async = "2.7.0" -slog-term = "2.9.1" +byteorder = "1.5.0" +crc = "3.4.0" +slog = "2.8.2" +slog-async = "2.8.0" +slog-term = "2.9.2" [target.'cfg(windows)'.dependencies.windows-sys] -version = "0.42" +version = "0.61" features = [ "Win32_Foundation", "Win32_System_Shutdown", diff --git a/build/win32/inno_updater.exe b/build/win32/inno_updater.exe index ef7cb934eb63..2ca110dea1fd 100644 Binary files a/build/win32/inno_updater.exe and b/build/win32/inno_updater.exe differ diff --git a/cgmanifest.json b/cgmanifest.json index 865be1331ce0..eb5b37d39a75 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "chromium", "repositoryUrl": "https://chromium.googlesource.com/chromium/src", - "commitHash": "84f6285324151a9aeb0422e3683e4ff85b9f77c8" + "commitHash": "5c0cb964bca15fcf41718d54f4b8d70d6b9079de" } }, "licenseDetail": [ @@ -40,7 +40,7 @@ "SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ], "isOnlyProductionDependency": true, - "version": "132.0.6834.194" + "version": "132.0.6834.210" }, { "component": { @@ -516,11 +516,11 @@ "git": { "name": "nodejs", "repositoryUrl": "https://github.com/nodejs/node", - "commitHash": "70178110a8e52d28dc957b6b20da94e455922025" + "commitHash": "4819c99baa28bf2c1baf411ba100c467fec3d486" } }, "isOnlyProductionDependency": true, - "version": "20.18.1" + "version": "20.18.3" }, { "component": { @@ -528,12 +528,12 @@ "git": { "name": "electron", "repositoryUrl": "https://github.com/electron/electron", - "commitHash": "c1918f96cb88b61be2b54589f98038c01a0881de" + "commitHash": "f98501308a973e0aee2414315b426e5de2c03a60" } }, "isOnlyProductionDependency": true, "license": "MIT", - "version": "34.1.0" + "version": "34.3.2" }, { "component": { diff --git a/cli/Cargo.lock b/cli/Cargo.lock index ff45765a0c1d..b32fb98540e3 100644 --- a/cli/Cargo.lock +++ b/cli/Cargo.lock @@ -1,21 +1,12 @@ # This file is automatically @generated by Cargo. # It is not intended for manual editing. -version = 3 +version = 4 [[package]] -name = "addr2line" -version = "0.21.0" +name = "adler2" +version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8a30b2e23b9e17a9f90641c7ab1549cd9b44f296d3ccbf309d2863cfe398a0cb" -dependencies = [ - "gimli", -] - -[[package]] -name = "adler" -version = "1.0.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "f26201604c87b1e01bd3d98f8d5d9a8fcbb815e8cedb41ffccbeb4bf593a35fe" +checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "aho-corasick" @@ -26,12 +17,6 @@ dependencies = [ "memchr", ] -[[package]] -name = "android-tzdata" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e999941b234f3131b00bc13c22d06e8c5ff726d1b6318ac7eb276997bbb4fef0" - [[package]] name = "android_system_properties" version = "0.1.5" @@ -58,9 +43,9 @@ dependencies = [ [[package]] name = "anstyle" -version = "1.0.7" +version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "038dfcf04a5feb68e9c60b21c9625a54c2c0616e79b72b0fd87075a056ae1d1b" +checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" [[package]] name = "anstyle-parse" @@ -90,6 +75,15 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "c3d036a3c4ab069c7b410a2ce876bd74808d2d0888a82667669f8e783a898bf1" +dependencies = [ + "derive_arbitrary", +] + [[package]] name = "async-broadcast" version = "0.5.1" @@ -100,6 +94,18 @@ dependencies = [ "futures-core", ] +[[package]] +name = "async-broadcast" +version = "0.7.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "435a87a52755b8f27fcf321ac4f04b2802e337c8c4872923137471ec39c37532" +dependencies = [ + "event-listener 5.3.0", + "event-listener-strategy 0.5.2", + "futures-core", + "pin-project-lite", +] + [[package]] name = "async-channel" version = "2.3.1" @@ -196,7 +202,7 @@ checksum = "3b43422f69d8ff38f95f1b2bb76517c91589a924d1559a0e935d7c8ce0274c11" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.87", ] [[package]] @@ -225,13 +231,13 @@ checksum = "8b75356056920673b02621b35afd0f7dda9306d03c79a30f5c56c44cf256e3de" [[package]] name = "async-trait" -version = "0.1.80" +version = "0.1.89" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c6fa2087f2753a7da8cc1c0dbfcf89579dd57458e36769de5ac750b4671737ca" +checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.87", ] [[package]] @@ -247,25 +253,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c4b4d0bd25bd0b74681c0ad21497610ce1b7c91b1022cd21c80c6fbdd9476b0" [[package]] -name = "backtrace" -version = "0.3.71" +name = "base64" +version = "0.21.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "26b05800d2e817c8b3b4b54abd461726265fa9789ae34330622f2db9ee696f9d" -dependencies = [ - "addr2line", - "cc", - "cfg-if", - "libc", - "miniz_oxide", - "object", - "rustc-demangle", -] +checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" [[package]] name = "base64" -version = "0.21.7" +version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9d297deb1925b89f2ccc13d7635fa0714f12c87adce1c75356b39ca9b7178567" +checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bit-vec" @@ -331,9 +328,9 @@ checksum = "1fd0f2584146f6f2ef48085050886acf353beff7305ebd1ae69500e27c67f64b" [[package]] name = "bytes" -version = "1.6.0" +version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "514de17de45fdb8dc022b1a7975556c53c86f9f0aa5f534b98977b171857c2c9" +checksum = "b35204fbdc0b3f4446b89fc1ac2cf84a8a68971995d0bf2e925ec7cd960f9cb3" [[package]] name = "cc" @@ -343,28 +340,33 @@ checksum = "41c270e7540d725e65ac7f1b212ac8ce349719624d7bcff99f8e2e488e8cf03f" [[package]] name = "cfg-if" -version = "1.0.0" +version = "1.0.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" + +[[package]] +name = "cfg_aliases" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd" +checksum = "613afe47fcd5fac7ccf1db93babcb082c5994d996f20b8b159f2ad1658eb5724" [[package]] name = "chrono" -version = "0.4.38" +version = "0.4.43" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a21f936df1771bf62b77f047b726c4625ff2e8aa607c01ec06e5a05bd8463401" +checksum = "fac4744fb15ae8337dc853fee7fb3f4e48c0fbaa23d0afe49c447b4fab126118" dependencies = [ - "android-tzdata", "iana-time-zone", "num-traits", "serde", - "windows-targets 0.52.5", + "windows-link", ] [[package]] name = "clap" -version = "4.5.4" +version = "4.5.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90bc066a67923782aa8515dbaea16946c5bcc5addbd668bb80af688e53e548a0" +checksum = "c6e6ff9dcd79cff5cd969a17a545d79e84ab086e444102a591e288a8aa3ce394" dependencies = [ "clap_builder", "clap_derive", @@ -372,9 +374,9 @@ dependencies = [ [[package]] name = "clap_builder" -version = "4.5.2" +version = "4.5.54" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ae129e2e766ae0ec03484e609954119f123cc1fe650337e155d03b022f24f7b4" +checksum = "fa42cf4d2b7a41bc8f663a7cab4031ebafa1bf3875705bfaf8466dc60ab52c00" dependencies = [ "anstream", "anstyle", @@ -384,28 +386,28 @@ dependencies = [ [[package]] name = "clap_derive" -version = "4.5.4" +version = "4.5.49" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "528131438037fd55894f62d6e9f068b8f45ac57ffa77517819645d10aed04f64" +checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671" dependencies = [ "heck", "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.87", ] [[package]] name = "clap_lex" -version = "0.7.0" +version = "0.7.7" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "98cc8fbded0c607b7ba9dd60cd98df59af97e84d24e49c8557331cfc26d301ce" +checksum = "c3e64b0cc0439b12df2fa678eae89a1c56a529fd067a9115f7827f1fffd22b32" [[package]] name = "code-cli" version = "0.1.0" dependencies = [ "async-trait", - "base64", + "base64 0.22.1", "bytes", "cfg-if", "chrono", @@ -413,13 +415,13 @@ dependencies = [ "clap_lex", "console", "const_format", - "core-foundation", + "core-foundation 0.10.1", "dialoguer", - "dirs 5.0.1", + "dirs 6.0.0", "flate2", "futures", "gethostname", - "hyper", + "hyper 0.14.28", "indicatif", "keyring", "lazy_static", @@ -428,9 +430,9 @@ dependencies = [ "open", "opentelemetry", "pin-project", - "rand 0.8.5", + "rand 0.9.2", "regex", - "reqwest", + "reqwest 0.12.9", "rmp-serde", "serde", "serde_bytes", @@ -440,15 +442,15 @@ dependencies = [ "sysinfo", "tar", "tempfile", - "thiserror", + "thiserror 2.0.18", "tokio", "tokio-util", "tunnels", "url", "uuid", "winapi", - "winreg 0.50.0", - "zbus", + "winreg 0.55.0", + "zbus 5.4.0", "zip", ] @@ -469,31 +471,31 @@ dependencies = [ [[package]] name = "console" -version = "0.15.8" +version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0e1f83fc076bd6dd27517eacdf25fef6c4dfe5f1d7448bafaaf3a26f13b5e4eb" +checksum = "03e45a4a8926227e4197636ba97a9fc9b00477e9f4bd711395687c5f0734bec4" dependencies = [ "encode_unicode", - "lazy_static", "libc", + "once_cell", "unicode-width", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] name = "const_format" -version = "0.2.32" +version = "0.2.35" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e3a214c7af3d04997541b18d432afaff4c455e79e2029079647e72fc2bd27673" +checksum = "7faa7469a93a566e9ccc1c73fe783b4a65c274c5ace346038dca9c39fe0030ad" dependencies = [ "const_format_proc_macros", ] [[package]] name = "const_format_proc_macros" -version = "0.2.32" +version = "0.2.34" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c7f6ff08fd20f4f299298a28e2dfa8a8ba1036e6cd2460ac1de7b425d76f2500" +checksum = "1d57c2eccfb16dbac1f4e61e206105db5820c9d26c3c472bc17c774259ef7744" dependencies = [ "proc-macro2", "quote", @@ -510,6 +512,16 @@ dependencies = [ "libc", ] +[[package]] +name = "core-foundation" +version = "0.10.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b2a6cd9ae233e7f62ba4e9353e81a88df7fc8a5987b8d445b4d90c879bd156f6" +dependencies = [ + "core-foundation-sys", + "libc", +] + [[package]] name = "core-foundation-sys" version = "0.8.6" @@ -536,9 +548,9 @@ dependencies = [ [[package]] name = "crossbeam-channel" -version = "0.5.13" +version = "0.5.15" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "33480d6946193aa8033910124896ca395333cae7e2d1113d1fef6c3272217df2" +checksum = "82b8f8f868b36967f9606790d1903570de9ceaf870a7bf9fbbd3016d636a2cb2" dependencies = [ "crossbeam-utils", ] @@ -585,11 +597,22 @@ dependencies = [ "syn 1.0.109", ] +[[package]] +name = "derive_arbitrary" +version = "1.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e567bd82dcff979e4b03460c307b3cdc9e96fde3d73bed1496d2bc75d9dd62a" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.87", +] + [[package]] name = "dialoguer" -version = "0.10.4" +version = "0.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "59c6f2989294b9a498d3ad5491a79c6deb604617378e1cdc4bfc1c1361fe2f87" +checksum = "25f104b501bf2364e78d0d3974cbc774f738f5865306ed128e1e0d7499c0ad96" dependencies = [ "console", "shell-words", @@ -619,11 +642,11 @@ dependencies = [ [[package]] name = "dirs" -version = "5.0.1" +version = "6.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "44c45a9d03d6676652bcb5e724c7e988de1acad23a711b5217ab9cbecbec2225" +checksum = "c3e8aa94d75141228480295a7d0e7feb620b1a5ad9f12bc40be62411e38cce4e" dependencies = [ - "dirs-sys 0.4.1", + "dirs-sys 0.5.0", ] [[package]] @@ -633,20 +656,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b1d1d91c932ef41c0f2663aa8b0ca0342d444d842c06914aa0a7e352d0bada6" dependencies = [ "libc", - "redox_users", + "redox_users 0.4.5", "winapi", ] [[package]] name = "dirs-sys" -version = "0.4.1" +version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "520f05a5cbd335fae5a99ff7a6ab8627577660ee5cfd6a94a6a929b52ff0321c" +checksum = "e01a3366d27ee9890022452ee61b2b63a67e6f13f58900b651ff5665f0bb1fab" dependencies = [ "libc", "option-ext", - "redox_users", - "windows-sys 0.48.0", + "redox_users 0.5.2", + "windows-sys 0.61.2", ] [[package]] @@ -657,14 +680,14 @@ checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.87", ] [[package]] name = "encode_unicode" -version = "0.3.6" +version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a357d28ed41a50f9c765dbfe56cbc04a64e53e5fc58ba79fbc34c10ef3df831f" +checksum = "34aa73646ffb006b8f5147f3dc182bd4bcb190227ce861fc4a4844bf8e3cb2c0" [[package]] name = "encoding_rs" @@ -675,6 +698,12 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "endi" +version = "1.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "66b7e2430c6dff6a955451e2cfc438f09cea1965a9d6f87f7e3b90decc014099" + [[package]] name = "enumflags2" version = "0.7.9" @@ -693,7 +722,7 @@ checksum = "5c785274071b1b420972453b306eeca06acf4633829db4223b58a2a8c5953bc4" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.87", ] [[package]] @@ -704,12 +733,12 @@ checksum = "5443807d6dff69373d433ab9ef5378ad8df50ca6298caf15de6e52e24aaf54d5" [[package]] name = "errno" -version = "0.3.9" +version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "534c5cf6194dfab3db3242765c03bbe257cf92f22b38f6bc0c58d59108a820ba" +checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", - "windows-sys 0.52.0", + "windows-sys 0.61.2", ] [[package]] @@ -800,9 +829,9 @@ dependencies = [ [[package]] name = "flate2" -version = "1.0.30" +version = "1.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f54427cfd1c7829e2a139fcefea601bf088ebca651d2bf53ebc600eac295dae" +checksum = "b375d6465b98090a5f25b1c7703f3859783755aa9a80433b36e0379a3ec2f369" dependencies = [ "crc32fast", "libz-sys", @@ -841,9 +870,9 @@ dependencies = [ [[package]] name = "futures" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "645c6916888f6cb6350d2550b80fb63e734897a8498abe35cfb732b6487804b0" +checksum = "65bc07b1a8bc7c85c5f2e110c476c7389b4554ba72af57d8445ea63a576b0876" dependencies = [ "futures-channel", "futures-core", @@ -856,9 +885,9 @@ dependencies = [ [[package]] name = "futures-channel" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "eac8f7d7865dcb88bd4373ab671c8cf4508703796caa2b1985a9ca867b3fcb78" +checksum = "2dff15bf788c671c1934e366d07e30c1814a8ef514e1af724a602e8a2fbe1b10" dependencies = [ "futures-core", "futures-sink", @@ -866,15 +895,15 @@ dependencies = [ [[package]] name = "futures-core" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "dfc6580bb841c5a68e9ef15c77ccc837b40a7504914d52e47b8b0e9bbda25a1d" +checksum = "05f29059c0c2090612e8d742178b0580d2dc940c837851ad723096f87af6663e" [[package]] name = "futures-executor" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a576fc72ae164fca6b9db127eaa9a9dda0d61316034f33a0a0d4eda41f02b01d" +checksum = "1e28d1d997f585e54aebc3f97d39e72338912123a67330d723fdbb564d646c9f" dependencies = [ "futures-core", "futures-task", @@ -883,9 +912,9 @@ dependencies = [ [[package]] name = "futures-io" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a44623e20b9681a318efdd71c299b6b222ed6f231972bfe2f224ebad6311f0c1" +checksum = "9e5c1b78ca4aae1ac06c48a526a655760685149f0d465d21f37abfe57ce075c6" [[package]] name = "futures-lite" @@ -914,32 +943,32 @@ dependencies = [ [[package]] name = "futures-macro" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87750cf4b7a4c0625b1529e4c543c2182106e4dedc60a2a6455e00d212c489ac" +checksum = "162ee34ebcb7c64a8abebc059ce0fee27c2262618d7b60ed8faf72fef13c3650" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.87", ] [[package]] name = "futures-sink" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9fb8e00e87438d937621c1c6269e53f536c14d3fbd6a042bb24879e57d474fb5" +checksum = "e575fab7d1e0dcb8d0c7bcf9a63ee213816ab51902e6d244a95819acacf1d4f7" [[package]] name = "futures-task" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "38d84fa142264698cdce1a9f9172cf383a0c82de1bddcf3092901442c4097004" +checksum = "f90f7dce0722e95104fcb095585910c0977252f286e354b5e3bd38902cd99988" [[package]] name = "futures-util" -version = "0.3.30" +version = "0.3.31" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3d6401deb83407ab3da39eba7e33987a73c3df0c82b4bb5813ee871c19c41d48" +checksum = "9fa08315bb612088cc391249efdc3bc77536f16c91f6cf495e6fbe85b20a4a81" dependencies = [ "futures-channel", "futures-core", @@ -965,12 +994,12 @@ dependencies = [ [[package]] name = "gethostname" -version = "0.4.3" +version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "0176e0459c2e4a1fe232f984bca6890e681076abb9934f6cea7c326f3fc47818" +checksum = "1bd49230192a3797a9a4d6abe9b3eed6f7fa4c8a8a4947977c6f80025f92cbd8" dependencies = [ - "libc", - "windows-targets 0.48.5", + "rustix 1.1.3", + "windows-link", ] [[package]] @@ -996,10 +1025,16 @@ dependencies = [ ] [[package]] -name = "gimli" -version = "0.28.1" +name = "getrandom" +version = "0.3.4" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253" +checksum = "899def5c37c4fd7b2664648c28120ecec138e4d395b459e5ca34f9cce2dd77fd" +dependencies = [ + "cfg-if", + "libc", + "r-efi", + "wasip2", +] [[package]] name = "h2" @@ -1012,8 +1047,8 @@ dependencies = [ "futures-core", "futures-sink", "futures-util", - "http", - "indexmap 2.2.6", + "http 0.2.12", + "indexmap 2.13.0", "slab", "tokio", "tokio-util", @@ -1028,9 +1063,9 @@ checksum = "8a9ee70c43aaf417c914396645a0fa852624801b24ebb7ae78fe8272889ac888" [[package]] name = "hashbrown" -version = "0.14.5" +version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" +checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" [[package]] name = "heck" @@ -1076,6 +1111,16 @@ dependencies = [ "itoa", ] +[[package]] +name = "http" +version = "1.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e3ba2a386d7f85a81f119ad7498ebe444d2e22c2af0b86b069416ace48b3311a" +dependencies = [ + "bytes", + "itoa", +] + [[package]] name = "http-body" version = "0.4.6" @@ -1083,7 +1128,30 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7ceab25649e9960c0311ea418d17bee82c0dcec1bd053b5f9a66e265a693bed2" dependencies = [ "bytes", - "http", + "http 0.2.12", + "pin-project-lite", +] + +[[package]] +name = "http-body" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1efedce1fb8e6913f23e0c92de8e62cd5b772a67e7b3946df930a62566c93184" +dependencies = [ + "bytes", + "http 1.4.0", +] + +[[package]] +name = "http-body-util" +version = "0.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b021d93e26becf5dc7e1b75b1bed1fd93124b374ceb73f43d4d4eafec896a64a" +dependencies = [ + "bytes", + "futures-core", + "http 1.4.0", + "http-body 1.0.1", "pin-project-lite", ] @@ -1110,8 +1178,8 @@ dependencies = [ "futures-core", "futures-util", "h2", - "http", - "http-body", + "http 0.2.12", + "http-body 0.4.6", "httparse", "httpdate", "itoa", @@ -1123,6 +1191,25 @@ dependencies = [ "want", ] +[[package]] +name = "hyper" +version = "1.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "256fb8d4bd6413123cc9d91832d78325c48ff41677595be797d90f42969beae0" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.4.0", + "http-body 1.0.1", + "httparse", + "itoa", + "pin-project-lite", + "smallvec", + "tokio", + "want", +] + [[package]] name = "hyper-tls" version = "0.5.0" @@ -1130,10 +1217,45 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d6183ddfa99b85da61a140bea0efc93fdf56ceaa041b37d553518030827f9905" dependencies = [ "bytes", - "hyper", + "hyper 0.14.28", + "native-tls", + "tokio", + "tokio-native-tls", +] + +[[package]] +name = "hyper-tls" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "70206fc6890eaca9fde8a0bf71caa2ddfc9fe045ac9e5c70df101a7dbde866e0" +dependencies = [ + "bytes", + "http-body-util", + "hyper 1.5.2", + "hyper-util", "native-tls", "tokio", "tokio-native-tls", + "tower-service", +] + +[[package]] +name = "hyper-util" +version = "0.1.10" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df2dcfbe0677734ab2f3ffa7fa7bfd4706bfdc1ef393f2ee30184aed67e631b4" +dependencies = [ + "bytes", + "futures-channel", + "futures-util", + "http 1.4.0", + "http-body 1.0.1", + "hyper 1.5.2", + "pin-project-lite", + "socket2 0.5.7", + "tokio", + "tower-service", + "tracing", ] [[package]] @@ -1274,7 +1396,7 @@ checksum = "1ec89e9337638ecdc08744df490b221a7399bf8d164eb52a665454e60e075ad6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.87", ] [[package]] @@ -1310,25 +1432,25 @@ dependencies = [ [[package]] name = "indexmap" -version = "2.2.6" +version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "168fb715dda47215e360912c096649d23d58bf392ac62f73919e831745e40f26" +checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", - "hashbrown 0.14.5", + "hashbrown 0.16.1", ] [[package]] name = "indicatif" -version = "0.17.8" +version = "0.18.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "763a5a8f45087d6bcea4222e7b72c291a054edf80e4ef6efd2a4979878c7bea3" +checksum = "9375e112e4b463ec1b1c6c011953545c65a30164fbab5b581df32b3abf0dcb88" dependencies = [ "console", - "instant", - "number_prefix", "portable-atomic", "unicode-width", + "unit-prefix", + "web-time", ] [[package]] @@ -1423,15 +1545,15 @@ dependencies = [ [[package]] name = "lazy_static" -version = "1.4.0" +version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e2abad23fbc42b3700f2f279844dc832adb2b2eb069b2df918f455c4e18cc646" +checksum = "bbd2bcb4c963f2ddae06a2efc7e9f3591312473c50c6685e1f298068316e66fe" [[package]] name = "libc" -version = "0.2.155" +version = "0.2.180" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "97b3888a4aecf77e811145cadf6eef5901f4782c53886191b2f693f24761847c" +checksum = "bcc35a38544a891a5f7c865aca548a982ccb3b8650a5b06d0fd33a10283c56fc" [[package]] name = "libredox" @@ -1445,9 +1567,9 @@ dependencies = [ [[package]] name = "libz-sys" -version = "1.1.16" +version = "1.1.23" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5e143b5e666b2695d28f6bca6497720813f699c9602dd7f5cac91008b8ada7f9" +checksum = "15d118bbf3771060e7311cc7bb0545b01d08a8b4a7de949198dec1fa0ca1c0f7" dependencies = [ "cc", "pkg-config", @@ -1476,6 +1598,12 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "78b3ae25bc7c8c38cec158d1f2757ee79e9b3740fbc7ccf0e59e4b08d793fa89" +[[package]] +name = "linux-raw-sys" +version = "0.11.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "df1d3c3b53da64cf5760482273a98e575c651a67eec7f77df96b5b642de8f039" + [[package]] name = "litemap" version = "0.7.4" @@ -1494,9 +1622,9 @@ dependencies = [ [[package]] name = "log" -version = "0.4.21" +version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "90ed8c1e510134f979dbc4f070f87d4313098b704861a105fe34231c70a3901c" +checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "md5" @@ -1536,22 +1664,23 @@ checksum = "6877bb514081ee2a7ff5ef9de3281f14a4dd4bceac4c09388074a6b5df8a139a" [[package]] name = "miniz_oxide" -version = "0.7.3" +version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87dfd01fe195c66b572b37921ad8803d010623c0aca821bea2302239d155cdae" +checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ - "adler", + "adler2", + "simd-adler32", ] [[package]] name = "mio" -version = "0.8.11" +version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a4a650543ca06a924e8b371db273b2756685faae30f8487da1b56505a8f78b0c" +checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" dependencies = [ "libc", "wasi 0.11.0+wasi-snapshot-preview1", - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] @@ -1585,12 +1714,16 @@ dependencies = [ ] [[package]] -name = "ntapi" -version = "0.4.1" +name = "nix" +version = "0.29.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "e8a3895c6391c39d7fe7ebc444a87eb2991b2a0bc718fdabd071eec617fc68e4" +checksum = "71e2746dc3a24dd78b3cfcb7be93368c6de9963d30f43a6a73998a9cf4b17b46" dependencies = [ - "winapi", + "bitflags 2.5.0", + "cfg-if", + "cfg_aliases", + "libc", + "memoffset 0.9.1", ] [[package]] @@ -1673,31 +1806,6 @@ dependencies = [ "autocfg", ] -[[package]] -name = "num_cpus" -version = "1.16.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4161fcb6d602d4d2081af7c3a45852d875a03dd337a6bfdd6e06407b61342a43" -dependencies = [ - "hermit-abi", - "libc", -] - -[[package]] -name = "number_prefix" -version = "0.4.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "830b246a0e5f20af87141b25c173cd1b609bd7779a4617d6ec582abaf90870f3" - -[[package]] -name = "object" -version = "0.32.2" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a6a622008b6e321afc04970976f62ee297fdbaa6f95318ca343e3eebb9648441" -dependencies = [ - "memchr", -] - [[package]] name = "once_cell" version = "1.19.0" @@ -1706,9 +1814,9 @@ checksum = "3fdb12b2476b595f9358c5161aa467c2438859caa136dec86c26fdd2efe17b92" [[package]] name = "open" -version = "4.2.0" +version = "5.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "3a083c0c7e5e4a8ec4176346cf61f67ac674e8bfb059d9226e1c54a96b377c12" +checksum = "43bb73a7fa3799b198970490a51174027ba0d4ec504b03cd08caf513d40024bc" dependencies = [ "is-wsl", "libc", @@ -1717,9 +1825,9 @@ dependencies = [ [[package]] name = "openssl" -version = "0.10.70" +version = "0.10.73" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "61cfb4e166a8bb8c9b55c500bc2308550148ece889be90f609377e58140f42c6" +checksum = "8505734d46c8ab1e19a1dce3aef597ad87dcb4c37e7188231769bd6bd51cebf8" dependencies = [ "bitflags 2.5.0", "cfg-if", @@ -1738,7 +1846,7 @@ checksum = "a948666b637a0f465e8564c73e89d4dde00d72d4d473cc972f390fc3dcee7d9c" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.87", ] [[package]] @@ -1749,9 +1857,9 @@ checksum = "ff011a302c396a5197692431fc1948019154afc178baf7d8e37367442a4601cf" [[package]] name = "openssl-sys" -version = "0.9.105" +version = "0.9.109" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b22d5b84be05a8d6947c7cb71f7c849aa0f112acd4bf51c2a7c1c988ac0a9dc" +checksum = "90096e2e47630d78b7d1c20952dc621f957103f8bc2c8359ec81290d75238571" dependencies = [ "cc", "libc", @@ -1761,9 +1869,9 @@ dependencies = [ [[package]] name = "opentelemetry" -version = "0.19.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5f4b8347cc26099d3aeee044065ecc3ae11469796b4d65d065a23a584ed92a6f" +checksum = "9591d937bc0e6d2feb6f71a559540ab300ea49955229c347a517a28d27784c54" dependencies = [ "opentelemetry_api", "opentelemetry_sdk", @@ -1771,24 +1879,25 @@ dependencies = [ [[package]] name = "opentelemetry_api" -version = "0.19.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "ed41783a5bf567688eb38372f2b7a8530f5a607a4b49d38dd7573236c23ca7e2" +checksum = "8a81f725323db1b1206ca3da8bb19874bbd3f57c3bcd59471bfb04525b265b9b" dependencies = [ "futures-channel", "futures-util", "indexmap 1.9.3", + "js-sys", "once_cell", "pin-project-lite", - "thiserror", + "thiserror 1.0.69", "urlencoding", ] [[package]] name = "opentelemetry_sdk" -version = "0.19.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b3a2a91fdbfdd4d212c0dcc2ab540de2c2bcbbd90be17de7a7daf8822d010c1" +checksum = "fa8e705a0612d48139799fcbaba0d4a90f06277153e43dd2bdc16c6f0edd8026" dependencies = [ "async-trait", "crossbeam-channel", @@ -1797,9 +1906,10 @@ dependencies = [ "futures-util", "once_cell", "opentelemetry_api", + "ordered-float", "percent-encoding", "rand 0.8.5", - "thiserror", + "thiserror 1.0.69", "tokio", "tokio-stream", ] @@ -1810,6 +1920,15 @@ version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "04744f49eae99ab78e0d5c0b603ab218f515ea8cfe5a456d7629ad883a3b6e7d" +[[package]] +name = "ordered-float" +version = "3.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f1e1c390732d15f1d48471625cd92d154e66db2c56645e29a9cd26f4699f72dc" +dependencies = [ + "num-traits", +] + [[package]] name = "ordered-stream" version = "0.2.0" @@ -1856,7 +1975,7 @@ dependencies = [ "libc", "redox_syscall 0.5.1", "smallvec", - "windows-targets 0.52.5", + "windows-targets 0.52.6", ] [[package]] @@ -1879,22 +1998,22 @@ checksum = "e3148f5046208a5d56bcfc03053e3ca6334e51da8dfb19b6cdc8b306fae3283e" [[package]] name = "pin-project" -version = "1.1.5" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b6bf43b791c5b9e34c3d182969b4abb522f9343702850a2e57f460d00d09b4b3" +checksum = "677f1add503faace112b9f1373e43e9e054bfdd22ff1a63c1bc485eaec6a6a8a" dependencies = [ "pin-project-internal", ] [[package]] name = "pin-project-internal" -version = "1.1.5" +version = "1.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "2f38a4412a78282e09a2cf38d195ea5420d15ba0602cb375210efbc877243965" +checksum = "6e918e4ff8c4549eb882f14b3a4bc8c8bc93de829416eacf579f1207a8fbf861" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.87", ] [[package]] @@ -1982,7 +2101,16 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" dependencies = [ "once_cell", - "toml_edit", + "toml_edit 0.19.15", +] + +[[package]] +name = "proc-macro-crate" +version = "3.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "219cb19e96be00ab2e37d6e299658a0cfa83e52429179969b0f0121b4ac46983" +dependencies = [ + "toml_edit 0.23.10+spec-1.0.0", ] [[package]] @@ -2003,6 +2131,12 @@ dependencies = [ "proc-macro2", ] +[[package]] +name = "r-efi" +version = "5.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "69cdb34c158ceb288df11e18b4bd39de994f6657d83847bdffdbd7f346754b0f" + [[package]] name = "rand" version = "0.7.3" @@ -2027,6 +2161,16 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rand" +version = "0.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "6db2770f06117d490610c7488547d543617b21bfa07796d7a12f6f1bd53850d1" +dependencies = [ + "rand_chacha 0.9.0", + "rand_core 0.9.3", +] + [[package]] name = "rand_chacha" version = "0.2.2" @@ -2047,6 +2191,16 @@ dependencies = [ "rand_core 0.6.4", ] +[[package]] +name = "rand_chacha" +version = "0.9.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d3022b5f1df60f26e1ffddd6c66e8aa15de382ae63b3a0c1bfc0e4d3e3f325cb" +dependencies = [ + "ppv-lite86", + "rand_core 0.9.3", +] + [[package]] name = "rand_core" version = "0.5.1" @@ -2065,6 +2219,15 @@ dependencies = [ "getrandom 0.2.15", ] +[[package]] +name = "rand_core" +version = "0.9.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "99d9a13982dcf210057a8a78572b2217b667c3beacbf3a0d8b454f6f82837d38" +dependencies = [ + "getrandom 0.3.4", +] + [[package]] name = "rand_hc" version = "0.2.0" @@ -2100,14 +2263,25 @@ checksum = "bd283d9651eeda4b2a83a43c1c91b266c40fd76ecd39a50a8c630ae69dc72891" dependencies = [ "getrandom 0.2.15", "libredox", - "thiserror", + "thiserror 1.0.69", +] + +[[package]] +name = "redox_users" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a4e608c6638b9c18977b00b475ac1f28d14e84b27d8d42f70e0bf1e3dec127ac" +dependencies = [ + "getrandom 0.2.15", + "libredox", + "thiserror 2.0.18", ] [[package]] name = "regex" -version = "1.10.4" +version = "1.12.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c117dbdfde9c8308975b6a18d71f3f385c89461f7b3fb054288ecf2a2058ba4c" +checksum = "843bc0191f75f3e22651ae5f1e72939ab2f72a4bc30fa80a066bd66edefc24d4" dependencies = [ "aho-corasick", "memchr", @@ -2117,9 +2291,9 @@ dependencies = [ [[package]] name = "regex-automata" -version = "0.4.6" +version = "0.4.13" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "86b83b8b9847f9bf95ef68afb0b8e6cdb80f498442f5179a29fad448fcc1eaea" +checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c" dependencies = [ "aho-corasick", "memchr", @@ -2128,9 +2302,9 @@ dependencies = [ [[package]] name = "regex-syntax" -version = "0.8.3" +version = "0.8.8" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "adad44e29e4c806119491a7f06f03de4d1af22c3a680dd47f1e6e179439d1f56" +checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58" [[package]] name = "reqwest" @@ -2138,16 +2312,16 @@ version = "0.11.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dd67538700a17451e7cba03ac727fb961abb7607553461627b97de0b89cf4a62" dependencies = [ - "base64", + "base64 0.21.7", "bytes", "encoding_rs", "futures-core", "futures-util", "h2", - "http", - "http-body", - "hyper", - "hyper-tls", + "http 0.2.12", + "http-body 0.4.6", + "hyper 0.14.28", + "hyper-tls 0.5.0", "ipnet", "js-sys", "log", @@ -2156,42 +2330,80 @@ dependencies = [ "once_cell", "percent-encoding", "pin-project-lite", - "rustls-pemfile", + "rustls-pemfile 1.0.4", "serde", "serde_json", "serde_urlencoded", - "sync_wrapper", + "sync_wrapper 0.1.2", "system-configuration", "tokio", "tokio-native-tls", - "tokio-util", "tower-service", "url", "wasm-bindgen", "wasm-bindgen-futures", - "wasm-streams", "web-sys", "winreg 0.50.0", ] [[package]] -name = "rmp" -version = "0.8.14" +name = "reqwest" +version = "0.12.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "228ed7c16fa39782c3b3468e974aec2795e9089153cd08ee2e9aefb3613334c4" +checksum = "a77c62af46e79de0a562e1a9849205ffcb7fc1238876e9bd743357570e04046f" dependencies = [ - "byteorder", - "num-traits", - "paste", -] - -[[package]] -name = "rmp-serde" -version = "1.3.0" + "base64 0.22.1", + "bytes", + "futures-core", + "futures-util", + "http 1.4.0", + "http-body 1.0.1", + "http-body-util", + "hyper 1.5.2", + "hyper-tls 0.6.0", + "hyper-util", + "ipnet", + "js-sys", + "log", + "mime", + "native-tls", + "once_cell", + "percent-encoding", + "pin-project-lite", + "rustls-pemfile 2.2.0", + "serde", + "serde_json", + "serde_urlencoded", + "sync_wrapper 1.0.2", + "tokio", + "tokio-native-tls", + "tokio-util", + "tower-service", + "url", + "wasm-bindgen", + "wasm-bindgen-futures", + "wasm-streams", + "web-sys", + "windows-registry", +] + +[[package]] +name = "rmp" +version = "0.8.14" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "52e599a477cf9840e92f2cde9a7189e67b42c57532749bf90aea6ec10facd4db" +checksum = "228ed7c16fa39782c3b3468e974aec2795e9089153cd08ee2e9aefb3613334c4" dependencies = [ "byteorder", + "num-traits", + "paste", +] + +[[package]] +name = "rmp-serde" +version = "1.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "72f81bee8c8ef9b577d1681a70ebbc962c232461e397b22c208c43c04b67a155" +dependencies = [ "rmp", "serde", ] @@ -2220,7 +2432,7 @@ dependencies = [ "sha1", "sha2", "subtle", - "thiserror", + "thiserror 1.0.69", "tokio", "tokio-util", ] @@ -2255,18 +2467,12 @@ dependencies = [ "russh-cryptovec", "serde", "sha2", - "thiserror", + "thiserror 1.0.69", "tokio", "tokio-stream", "yasna", ] -[[package]] -name = "rustc-demangle" -version = "0.1.24" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "719b953e2095829ee67db738b3bfa9fa368c94900df327b3f07fe6e794d2fe1f" - [[package]] name = "rustix" version = "0.37.27" @@ -2294,13 +2500,44 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "rustix" +version = "1.1.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "146c9e247ccc180c1f61615433868c99f3de3ae256a30a43b49f67c2d9171f34" +dependencies = [ + "bitflags 2.5.0", + "errno", + "libc", + "linux-raw-sys 0.11.0", + "windows-sys 0.61.2", +] + [[package]] name = "rustls-pemfile" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1c74cae0a4cf6ccbbf5f359f08efdf8ee7e1dc532573bf0db71968cb56b1448c" dependencies = [ - "base64", + "base64 0.21.7", +] + +[[package]] +name = "rustls-pemfile" +version = "2.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dce314e5fee3f39953d46bb63bb8a46d40c2f8fb7cc5a3b6cab2bde9721d6e50" +dependencies = [ + "rustls-pki-types", +] + +[[package]] +name = "rustls-pki-types" +version = "1.13.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "21e6f2ab2928ca4291b86736a8bd920a277a399bba1589409d72154ff87c1282" +dependencies = [ + "zeroize", ] [[package]] @@ -2337,7 +2574,7 @@ dependencies = [ "openssl", "rand 0.8.5", "serde", - "zbus", + "zbus 3.15.2", ] [[package]] @@ -2347,7 +2584,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c627723fd09706bacdb5cf41499e95098555af3c3c29d014dc3c458ef6be11c0" dependencies = [ "bitflags 2.5.0", - "core-foundation", + "core-foundation 0.9.4", "core-foundation-sys", "libc", "security-framework-sys", @@ -2365,42 +2602,55 @@ dependencies = [ [[package]] name = "serde" -version = "1.0.202" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "226b61a0d411b2ba5ff6d7f73a476ac4f8bb900373459cd00fab8512828ba395" +checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ + "serde_core", "serde_derive", ] [[package]] name = "serde_bytes" -version = "0.11.14" +version = "0.11.19" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "8b8497c313fd43ab992087548117643f6fcd935cbf36f176ffda0aacf9591734" +checksum = "a5d440709e79d88e51ac01c4b72fc6cb7314017bb7da9eeff678aa94c10e3ea8" dependencies = [ "serde", + "serde_core", +] + +[[package]] +name = "serde_core" +version = "1.0.228" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" +dependencies = [ + "serde_derive", ] [[package]] name = "serde_derive" -version = "1.0.202" +version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6048858004bcff69094cd972ed40a32500f153bd3be9f716b2eed2e8217c4838" +checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.87", ] [[package]] name = "serde_json" -version = "1.0.117" +version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "455182ea6142b14f93f4bc5320a2b31c1f266b66a4a5c858b013302a5d8cbfc3" +checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ "itoa", - "ryu", + "memchr", "serde", + "serde_core", + "zmij", ] [[package]] @@ -2411,7 +2661,7 @@ checksum = "6c64451ba24fc7a6a2d60fc75dd9c83c90903b19028d4eff35e88fc1e86564e9" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.87", ] [[package]] @@ -2439,9 +2689,9 @@ dependencies = [ [[package]] name = "sha2" -version = "0.10.8" +version = "0.10.9" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "793db75ad2bcafc3ffa7c68b215fee268f537982cd901d132f89c6343f3a3dc8" +checksum = "a7507d819769d01a365ab707794a4084392c824f54a7a6a7862f8c3d0892b283" dependencies = [ "cfg-if", "cpufeatures", @@ -2469,6 +2719,12 @@ dependencies = [ "libc", ] +[[package]] +name = "simd-adler32" +version = "0.3.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e320a6c5ad31d271ad523dcf3ad13e2767ad8b1cb8f047f75a8aeaf8da139da2" + [[package]] name = "slab" version = "0.4.9" @@ -2504,6 +2760,16 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "socket2" +version = "0.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "17129e116933cf371d018bb80ae557e889637989d8638274fb25622827b03881" +dependencies = [ + "libc", + "windows-sys 0.60.2", +] + [[package]] name = "stable_deref_trait" version = "1.2.0" @@ -2541,9 +2807,9 @@ dependencies = [ [[package]] name = "syn" -version = "2.0.65" +version = "2.0.87" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "d2863d96a84c6439701d7a38f9de935ec562c8832cc55d1dde0f513b52fad106" +checksum = "25aa4ce346d03a6dcd68dd8b4010bcb74e54e62c90c573f394c46eae99aba32d" dependencies = [ "proc-macro2", "quote", @@ -2556,6 +2822,15 @@ version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2047c6ded9c721764247e62cd3b03c09ffc529b2ba5b10ec482ae507a4a70160" +[[package]] +name = "sync_wrapper" +version = "1.0.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" +dependencies = [ + "futures-core", +] + [[package]] name = "synstructure" version = "0.13.1" @@ -2564,21 +2839,16 @@ checksum = "c8af7666ab7b6390ab78131fb5b0fce11d6b7a6951602017c35fa82800708971" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.87", ] [[package]] name = "sysinfo" -version = "0.29.11" +version = "0.38.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "cd727fc423c2060f6c92d9534cef765c65a6ed3f428a03d7def74a8c4348e666" +checksum = "fe840c5b1afe259a5657392a4dbb74473a14c8db999c3ec2f4ae812e028a94da" dependencies = [ - "cfg-if", - "core-foundation-sys", "libc", - "ntapi", - "once_cell", - "winapi", ] [[package]] @@ -2588,7 +2858,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ba3a3adc5c275d719af8cb4272ea1c4a6d668a777f37e115f6d11ddbc1c8e0e7" dependencies = [ "bitflags 1.3.2", - "core-foundation", + "core-foundation 0.9.4", "system-configuration-sys", ] @@ -2604,9 +2874,9 @@ dependencies = [ [[package]] name = "tar" -version = "0.4.40" +version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "b16afcea1f22891c49a00c751c7b63b2233284064f11a200fc624137c51e2ddb" +checksum = "1d863878d212c87a19c1a610eb53bb01fe12951c0501cf5a0d65f724914a667a" dependencies = [ "filetime", "libc", @@ -2615,34 +2885,55 @@ dependencies = [ [[package]] name = "tempfile" -version = "3.10.1" +version = "3.12.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "85b77fafb263dd9d05cbeac119526425676db3784113aa9295c88498cbf8bff1" +checksum = "04cbcdd0c794ebb0d4cf35e88edd2f7d2c4c3e9a5a6dab322839b321c6a87a64" dependencies = [ "cfg-if", "fastrand 2.1.0", + "once_cell", "rustix 0.38.34", - "windows-sys 0.52.0", + "windows-sys 0.59.0", +] + +[[package]] +name = "thiserror" +version = "1.0.69" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b6aaf5339b578ea85b50e080feb250a3e8ae8cfcdff9a461c9ec2904bc923f52" +dependencies = [ + "thiserror-impl 1.0.69", ] [[package]] name = "thiserror" -version = "1.0.61" +version = "2.0.18" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4288b5bcbc7920c07a1149a35cf9590a2aa808e0bc1eafaade0b80947865fbc4" +dependencies = [ + "thiserror-impl 2.0.18", +] + +[[package]] +name = "thiserror-impl" +version = "1.0.69" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "c546c80d6be4bc6a00c0f01730c08df82eaa7a7a61f11d656526506112cc1709" +checksum = "4fee6c4efc90059e10f81e6d42c60a18f76588c3d74cb83a0b242a2b6c7504c1" dependencies = [ - "thiserror-impl", + "proc-macro2", + "quote", + "syn 2.0.87", ] [[package]] name = "thiserror-impl" -version = "1.0.61" +version = "2.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "46c3384250002a6d5af4d114f2845d37b57521033f30d5c3f46c4d70e1197533" +checksum = "ebc4ee7f67670e9b64d05fa4253e753e016c6c95ff35b89b7941d6b856dec1d5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.87", ] [[package]] @@ -2676,33 +2967,31 @@ dependencies = [ [[package]] name = "tokio" -version = "1.37.0" +version = "1.49.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "1adbebffeca75fcfd058afa480fb6c0b81e165a0323f9c9d39c9697e37c46787" +checksum = "72a2903cd7736441aac9df9d7688bd0ce48edccaadf181c3b90be801e81d3d86" dependencies = [ - "backtrace", "bytes", "libc", "mio", - "num_cpus", "parking_lot", "pin-project-lite", "signal-hook-registry", - "socket2 0.5.7", + "socket2 0.6.1", "tokio-macros", "tracing", - "windows-sys 0.48.0", + "windows-sys 0.61.2", ] [[package]] name = "tokio-macros" -version = "2.2.0" +version = "2.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "5b8a1e28f2deaa14e508979454cb3a223b10b938b45af148bc0986de36f1923b" +checksum = "af407857209536a95c8e56f8231ef2c2e2aff839b22e07a1ffcbc617e9db9fa5" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.87", ] [[package]] @@ -2742,9 +3031,9 @@ dependencies = [ [[package]] name = "tokio-util" -version = "0.7.11" +version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9cf6b47b3771c49ac75ad09a6162f53ad4b8088b76ac60e8ec1455b31a189fe1" +checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" dependencies = [ "bytes", "futures-core", @@ -2760,15 +3049,45 @@ version = "0.6.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4badfd56924ae69bcc9039335b2e017639ce3f9b001c393c1b2d1ef846ce2cbf" +[[package]] +name = "toml_datetime" +version = "0.7.5+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "92e1cfed4a3038bc5a127e35a2d360f145e1f4b971b551a2ba5fd7aedf7e1347" +dependencies = [ + "serde_core", +] + [[package]] name = "toml_edit" version = "0.19.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5bb770da30e5cbfde35a2d7b9b8a2c4b8ef89548a7a6aeab5c9a576e3e7421" dependencies = [ - "indexmap 2.2.6", - "toml_datetime", - "winnow", + "indexmap 2.13.0", + "toml_datetime 0.6.6", + "winnow 0.5.40", +] + +[[package]] +name = "toml_edit" +version = "0.23.10+spec-1.0.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "84c8b9f757e028cee9fa244aea147aab2a9ec09d5325a9b01e0a49730c2b5269" +dependencies = [ + "indexmap 2.13.0", + "toml_datetime 0.7.5+spec-1.1.0", + "toml_parser", + "winnow 0.7.14", +] + +[[package]] +name = "toml_parser" +version = "1.0.6+spec-1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a3198b4b0a8e11f09dd03e133c0280504d0801269e9afa46362ffde1cbeebf44" +dependencies = [ + "winnow 0.7.14", ] [[package]] @@ -2796,7 +3115,7 @@ checksum = "34704c8d6ebcbc939824180af020566b01a7c01f80641264eba0999f6c2b6be7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.87", ] [[package]] @@ -2823,13 +3142,13 @@ dependencies = [ "byteorder", "bytes", "data-encoding", - "http", + "http 0.2.12", "httparse", "log", "native-tls", "rand 0.8.5", "sha1", - "thiserror", + "thiserror 1.0.69", "url", "utf-8", ] @@ -2842,16 +3161,16 @@ dependencies = [ "async-trait", "chrono", "futures", - "hyper", + "hyper 0.14.28", "log", "os_info", "rand 0.8.5", - "reqwest", + "reqwest 0.11.27", "russh", "russh-keys", "serde", "serde_json", - "thiserror", + "thiserror 1.0.69", "tokio", "tokio-tungstenite", "tokio-util", @@ -2887,9 +3206,9 @@ checksum = "3354b9ac3fae1ff6755cb6db53683adb661634f67557942dea4facebec0fee4b" [[package]] name = "unicode-width" -version = "0.1.12" +version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "68f5e5f3158ecfd4b8ff6fe086db7c8467a2dfdac97fe420f2b7c4aa97af66d6" +checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" [[package]] name = "unicode-xid" @@ -2897,6 +3216,12 @@ version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f962df74c8c05a667b5ee8bcf162993134c104e96440b663c8daa176dc772d8c" +[[package]] +name = "unit-prefix" +version = "0.5.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "81e544489bf3d8ef66c953931f56617f423cd4b5494be343d9b9d3dda037b9a3" + [[package]] name = "url" version = "2.5.4" @@ -2940,11 +3265,11 @@ checksum = "711b9620af191e0cdc7468a8d14e709c3dcdb115b36f838e601583af800a370a" [[package]] name = "uuid" -version = "1.8.0" +version = "1.16.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "a183cf7feeba97b4dd1c0d46788634f6221d87fa961b305bed08c851829efcc0" +checksum = "458f7a779bf54acc9f347480ac654f68407d3aab21269a6e3c9f922acd9e2da9" dependencies = [ - "getrandom 0.2.15", + "getrandom 0.3.4", "serde", ] @@ -2987,6 +3312,15 @@ version = "0.11.0+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423" +[[package]] +name = "wasip2" +version = "1.0.1+wasi-0.2.4" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0562428422c63773dad2c345a1882263bbf4d65cf3f42e90921f787ef5ad58e7" +dependencies = [ + "wit-bindgen", +] + [[package]] name = "wasm-bindgen" version = "0.2.92" @@ -3008,7 +3342,7 @@ dependencies = [ "once_cell", "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.87", "wasm-bindgen-shared", ] @@ -3042,7 +3376,7 @@ checksum = "e94f17b526d0a461a191c78ea52bbce64071ed5c04c9ffe424dcb38f74171bb7" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.87", "wasm-bindgen-backend", "wasm-bindgen-shared", ] @@ -3076,6 +3410,16 @@ dependencies = [ "wasm-bindgen", ] +[[package]] +name = "web-time" +version = "1.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a6580f308b1fad9207618087a65c04e7a10bc77e02c8e84e9b00dd4b12fa0bb" +dependencies = [ + "js-sys", + "wasm-bindgen", +] + [[package]] name = "winapi" version = "0.3.9" @@ -3104,7 +3448,43 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "33ab640c8d7e35bf8ba19b884ba838ceb4fba93a4e8c65a9059d08afcfc683d9" dependencies = [ - "windows-targets 0.52.5", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-link" +version = "0.2.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" + +[[package]] +name = "windows-registry" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e400001bb720a623c1c69032f8e3e4cf09984deec740f007dd2b03ec864804b0" +dependencies = [ + "windows-result", + "windows-strings", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-result" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1d1043d8214f791817bab27572aaa8af63732e11bf84aa21a45a78d6c317ae0e" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-strings" +version = "0.1.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4cd9b125c486025df0eabcb585e62173c6c9eddcec5d117d3b6e8c30e2ee4d10" +dependencies = [ + "windows-result", + "windows-targets 0.52.6", ] [[package]] @@ -3122,7 +3502,34 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d" dependencies = [ - "windows-targets 0.52.5", + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.59.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "1e38bc4d79ed67fd075bcc251a1c39b32a1776bbe92e5bef1f0bf1f8c531853b" +dependencies = [ + "windows-targets 0.52.6", +] + +[[package]] +name = "windows-sys" +version = "0.60.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f2f500e4d28234f72040990ec9d39e3a6b950f9f22d3dba18416c35882612bcb" +dependencies = [ + "windows-targets 0.53.5", +] + +[[package]] +name = "windows-sys" +version = "0.61.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" +dependencies = [ + "windows-link", ] [[package]] @@ -3142,18 +3549,35 @@ dependencies = [ [[package]] name = "windows-targets" -version = "0.52.5" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "9b724f72796e036ab90c1021d4780d4d3d648aca59e491e6b98e725b84e99973" +dependencies = [ + "windows_aarch64_gnullvm 0.52.6", + "windows_aarch64_msvc 0.52.6", + "windows_i686_gnu 0.52.6", + "windows_i686_gnullvm 0.52.6", + "windows_i686_msvc 0.52.6", + "windows_x86_64_gnu 0.52.6", + "windows_x86_64_gnullvm 0.52.6", + "windows_x86_64_msvc 0.52.6", +] + +[[package]] +name = "windows-targets" +version = "0.53.5" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "6f0713a46559409d202e70e28227288446bf7841d3211583a4b53e3f6d96e7eb" +checksum = "4945f9f551b88e0d65f3db0bc25c33b8acea4d9e41163edf90dcd0b19f9069f3" dependencies = [ - "windows_aarch64_gnullvm 0.52.5", - "windows_aarch64_msvc 0.52.5", - "windows_i686_gnu 0.52.5", - "windows_i686_gnullvm", - "windows_i686_msvc 0.52.5", - "windows_x86_64_gnu 0.52.5", - "windows_x86_64_gnullvm 0.52.5", - "windows_x86_64_msvc 0.52.5", + "windows-link", + "windows_aarch64_gnullvm 0.53.1", + "windows_aarch64_msvc 0.53.1", + "windows_i686_gnu 0.53.1", + "windows_i686_gnullvm 0.53.1", + "windows_i686_msvc 0.53.1", + "windows_x86_64_gnu 0.53.1", + "windows_x86_64_gnullvm 0.53.1", + "windows_x86_64_msvc 0.53.1", ] [[package]] @@ -3164,9 +3588,15 @@ checksum = "2b38e32f0abccf9987a4e3079dfb67dcd799fb61361e53e2882c3cbaf0d905d8" [[package]] name = "windows_aarch64_gnullvm" -version = "0.52.5" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "32a4622180e7a0ec044bb555404c800bc9fd9ec262ec147edd5989ccd0c02cd3" + +[[package]] +name = "windows_aarch64_gnullvm" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "7088eed71e8b8dda258ecc8bac5fb1153c5cffaf2578fc8ff5d61e23578d3263" +checksum = "a9d8416fa8b42f5c947f8482c43e7d89e73a173cead56d044f6a56104a6d1b53" [[package]] name = "windows_aarch64_msvc" @@ -3176,9 +3606,15 @@ checksum = "dc35310971f3b2dbbf3f0690a219f40e2d9afcf64f9ab7cc1be722937c26b4bc" [[package]] name = "windows_aarch64_msvc" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "9985fd1504e250c615ca5f281c3f7a6da76213ebd5ccc9561496568a2752afb6" +checksum = "09ec2a7bb152e2252b53fa7803150007879548bc709c039df7627cabbd05d469" + +[[package]] +name = "windows_aarch64_msvc" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "b9d782e804c2f632e395708e99a94275910eb9100b2114651e04744e9b125006" [[package]] name = "windows_i686_gnu" @@ -3188,15 +3624,27 @@ checksum = "a75915e7def60c94dcef72200b9a8e58e5091744960da64ec734a6c6e9b3743e" [[package]] name = "windows_i686_gnu" -version = "0.52.5" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "8e9b5ad5ab802e97eb8e295ac6720e509ee4c243f69d781394014ebfe8bbfa0b" + +[[package]] +name = "windows_i686_gnu" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "960e6da069d81e09becb0ca57a65220ddff016ff2d6af6a223cf372a506593a3" + +[[package]] +name = "windows_i686_gnullvm" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "88ba073cf16d5372720ec942a8ccbf61626074c6d4dd2e745299726ce8b89670" +checksum = "0eee52d38c090b3caa76c563b86c3a4bd71ef1a819287c19d586d7334ae8ed66" [[package]] name = "windows_i686_gnullvm" -version = "0.52.5" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "87f4261229030a858f36b459e748ae97545d6f1ec60e5e0d6a3d32e0dc232ee9" +checksum = "fa7359d10048f68ab8b09fa71c3daccfb0e9b559aed648a8f95469c27057180c" [[package]] name = "windows_i686_msvc" @@ -3206,9 +3654,15 @@ checksum = "8f55c233f70c4b27f66c523580f78f1004e8b5a8b659e05a4eb49d4166cca406" [[package]] name = "windows_i686_msvc" -version = "0.52.5" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "240948bc05c5e7c6dabba28bf89d89ffce3e303022809e73deaefe4f6ec56c66" + +[[package]] +name = "windows_i686_msvc" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "db3c2bf3d13d5b658be73463284eaf12830ac9a26a90c717b7f771dfe97487bf" +checksum = "1e7ac75179f18232fe9c285163565a57ef8d3c89254a30685b57d83a38d326c2" [[package]] name = "windows_x86_64_gnu" @@ -3218,9 +3672,15 @@ checksum = "53d40abd2583d23e4718fddf1ebec84dbff8381c07cae67ff7768bbf19c6718e" [[package]] name = "windows_x86_64_gnu" -version = "0.52.5" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "147a5c80aabfbf0c7d901cb5895d1de30ef2907eb21fbbab29ca94c5b08b1a78" + +[[package]] +name = "windows_x86_64_gnu" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "4e4246f76bdeff09eb48875a0fd3e2af6aada79d409d33011886d3e1581517d9" +checksum = "9c3842cdd74a865a8066ab39c8a7a473c0778a3f29370b5fd6b4b9aa7df4a499" [[package]] name = "windows_x86_64_gnullvm" @@ -3230,9 +3690,15 @@ checksum = "0b7b52767868a23d5bab768e390dc5f5c55825b6d30b86c844ff2dc7414044cc" [[package]] name = "windows_x86_64_gnullvm" -version = "0.52.5" +version = "0.52.6" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "852298e482cd67c356ddd9570386e2862b5673c85bd5f88df9ab6802b334c596" +checksum = "24d5b23dc417412679681396f2b49f3de8c1473deb516bd34410872eff51ed0d" + +[[package]] +name = "windows_x86_64_gnullvm" +version = "0.53.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "0ffa179e2d07eee8ad8f57493436566c7cc30ac536a3379fdf008f47f6bb7ae1" [[package]] name = "windows_x86_64_msvc" @@ -3242,9 +3708,15 @@ checksum = "ed94fce61571a4006852b7389a063ab983c02eb1bb37b47f8272ce92d06d9538" [[package]] name = "windows_x86_64_msvc" -version = "0.52.5" +version = "0.52.6" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "589f6da84c646204747d1270a2a5661ea66ed1cced2631d546fdfb155959f9ec" + +[[package]] +name = "windows_x86_64_msvc" +version = "0.53.1" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "bec47e5bfd1bff0eeaf6d8b485cc1074891a197ab4225d504cb7a1ab88b02bf0" +checksum = "d6bbff5f0aada427a1e5a6da5f1f98158182f26556f345ac9e04d36d0ebed650" [[package]] name = "winnow" @@ -3255,6 +3727,15 @@ dependencies = [ "memchr", ] +[[package]] +name = "winnow" +version = "0.7.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "5a5364e9d77fcdeeaa6062ced926ee3381faa2ee02d3eb83a5c27a8825540829" +dependencies = [ + "memchr", +] + [[package]] name = "winreg" version = "0.8.0" @@ -3274,6 +3755,22 @@ dependencies = [ "windows-sys 0.48.0", ] +[[package]] +name = "winreg" +version = "0.55.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cb5a765337c50e9ec252c2069be9bf91c7df47afb103b642ba3a53bf8101be97" +dependencies = [ + "cfg-if", + "windows-sys 0.59.0", +] + +[[package]] +name = "wit-bindgen" +version = "0.46.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f17a85883d4e6d00e8a97c586de764dabcc06133f7f1d55dce5cdc070ad7fe59" + [[package]] name = "write16" version = "1.0.0" @@ -3337,7 +3834,7 @@ checksum = "2380878cad4ac9aac1e2435f3eb4020e8374b5f13c296cb75b4620ff8e229154" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.87", "synstructure", ] @@ -3347,7 +3844,7 @@ version = "3.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "675d170b632a6ad49804c8cf2105d7c31eddd3312555cffd4b740e08e97c25e6" dependencies = [ - "async-broadcast", + "async-broadcast 0.5.1", "async-process", "async-recursion", "async-trait", @@ -3359,7 +3856,7 @@ dependencies = [ "futures-sink", "futures-util", "hex", - "nix", + "nix 0.26.4", "once_cell", "ordered-stream", "rand 0.8.5", @@ -3372,9 +3869,39 @@ dependencies = [ "uds_windows", "winapi", "xdg-home", - "zbus_macros", - "zbus_names", - "zvariant", + "zbus_macros 3.15.2", + "zbus_names 2.6.1", + "zvariant 3.15.2", +] + +[[package]] +name = "zbus" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "cbddd8b6cb25d5d8ec1b23277b45299a98bfb220f1761ca11e186d5c702507f8" +dependencies = [ + "async-broadcast 0.7.2", + "async-recursion", + "async-trait", + "enumflags2", + "event-listener 5.3.0", + "futures-core", + "futures-util", + "hex", + "nix 0.29.0", + "ordered-stream", + "serde", + "serde_repr", + "static_assertions", + "tokio", + "tracing", + "uds_windows", + "windows-sys 0.59.0", + "winnow 0.7.14", + "xdg-home", + "zbus_macros 5.4.0", + "zbus_names 4.3.1", + "zvariant 5.9.2", ] [[package]] @@ -3383,12 +3910,27 @@ version = "3.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7131497b0f887e8061b430c530240063d33bf9455fa34438f388a245da69e0a5" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 1.3.1", "proc-macro2", "quote", "regex", "syn 1.0.109", - "zvariant_utils", + "zvariant_utils 1.0.1", +] + +[[package]] +name = "zbus_macros" +version = "5.4.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "dac404d48b4e9cf193c8b49589f3280ceca5ff63519e7e64f55b4cf9c47ce146" +dependencies = [ + "proc-macro-crate 3.4.0", + "proc-macro2", + "quote", + "syn 2.0.87", + "zbus_names 4.3.1", + "zvariant 5.9.2", + "zvariant_utils 3.3.0", ] [[package]] @@ -3399,7 +3941,18 @@ checksum = "437d738d3750bed6ca9b8d423ccc7a8eb284f6b1d6d4e225a0e4e6258d864c8d" dependencies = [ "serde", "static_assertions", - "zvariant", + "zvariant 3.15.2", +] + +[[package]] +name = "zbus_names" +version = "4.3.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "ffd8af6d5b78619bab301ff3c560a5bd22426150253db278f164d6cf3b72c50f" +dependencies = [ + "serde", + "winnow 0.7.14", + "zvariant 5.9.2", ] [[package]] @@ -3419,7 +3972,7 @@ checksum = "595eed982f7d355beb85837f651fa22e90b3c044842dc7f2c2842c086f295808" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.87", "synstructure", ] @@ -3448,22 +4001,32 @@ checksum = "6eafa6dfb17584ea3e2bd6e76e0cc15ad7af12b09abdd1ca55961bed9b1063c6" dependencies = [ "proc-macro2", "quote", - "syn 2.0.65", + "syn 2.0.87", ] [[package]] name = "zip" -version = "0.6.6" +version = "2.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "760394e246e4c28189f19d488c058bf16f564016aefac5d32bb1f3b51d5e9261" +checksum = "84e9a772a54b54236b9b744aaaf8d7be01b4d6e99725523cb82cb32d1c81b1d7" dependencies = [ - "byteorder", + "arbitrary", "crc32fast", "crossbeam-utils", + "displaydoc", "flate2", + "indexmap 2.13.0", + "memchr", + "thiserror 2.0.18", "time", ] +[[package]] +name = "zmij" +version = "1.0.14" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "bd8f3f50b848df28f887acb68e41201b5aea6bc8a8dacc00fb40635ff9a72fea" + [[package]] name = "zvariant" version = "3.15.2" @@ -3475,7 +4038,21 @@ dependencies = [ "libc", "serde", "static_assertions", - "zvariant_derive", + "zvariant_derive 3.15.2", +] + +[[package]] +name = "zvariant" +version = "5.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "68b64ef4f40c7951337ddc7023dd03528a57a3ce3408ee9da5e948bd29b232c4" +dependencies = [ + "endi", + "enumflags2", + "serde", + "winnow 0.7.14", + "zvariant_derive 5.9.2", + "zvariant_utils 3.3.0", ] [[package]] @@ -3484,11 +4061,24 @@ version = "3.15.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "37c24dc0bed72f5f90d1f8bb5b07228cbf63b3c6e9f82d82559d4bae666e7ed9" dependencies = [ - "proc-macro-crate", + "proc-macro-crate 1.3.1", "proc-macro2", "quote", "syn 1.0.109", - "zvariant_utils", + "zvariant_utils 1.0.1", +] + +[[package]] +name = "zvariant_derive" +version = "5.9.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "484d5d975eb7afb52cc6b929c13d3719a20ad650fea4120e6310de3fc55e415c" +dependencies = [ + "proc-macro-crate 3.4.0", + "proc-macro2", + "quote", + "syn 2.0.87", + "zvariant_utils 3.3.0", ] [[package]] @@ -3501,3 +4091,16 @@ dependencies = [ "quote", "syn 1.0.109", ] + +[[package]] +name = "zvariant_utils" +version = "3.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f75c23a64ef8f40f13a6989991e643554d9bef1d682a281160cf0c1bc389c5e9" +dependencies = [ + "proc-macro2", + "quote", + "serde", + "syn 2.0.87", + "winnow 0.7.14", +] diff --git a/cli/Cargo.toml b/cli/Cargo.toml index 2c87d662e073..c49e453e66c2 100644 --- a/cli/Cargo.toml +++ b/cli/Cargo.toml @@ -12,62 +12,62 @@ path = "src/lib.rs" name = "code" [dependencies] -futures = "0.3.28" -clap = { version = "4.3.0", features = ["derive", "env"] } -open = "4.1.0" -reqwest = { version = "0.11.22", default-features = false, features = ["json", "stream", "native-tls"] } -tokio = { version = "1.28.2", features = ["full"] } -tokio-util = { version = "0.7.8", features = ["compat", "codec"] } -flate2 = { version = "1.0.26", default-features = false, features = ["zlib"] } -zip = { version = "0.6.6", default-features = false, features = ["time", "deflate-zlib"] } -regex = "1.8.3" -lazy_static = "1.4.0" -sysinfo = { version = "0.29.0", default-features = false } +futures = "0.3.31" +clap = { version = "4.5.54", features = ["derive", "env"] } +open = "5.3.3" +reqwest = { version = "0.12.9", default-features = false, features = ["json", "stream", "native-tls"] } +tokio = { version = "1.49.0", features = ["full"] } +tokio-util = { version = "0.7.18", features = ["compat", "codec"] } +flate2 = { version = "1.1.8", default-features = false, features = ["zlib"] } +zip = { version = "2.3.0", default-features = false, features = ["time", "deflate-zlib"] } +regex = "1.12.2" +lazy_static = "1.5.0" +sysinfo = { version = "0.38.0", default-features = false } serde = { version = "1.0.163", features = ["derive"] } -serde_json = "1.0.96" -rmp-serde = "1.1.1" -uuid = { version = "1.4", features = ["serde", "v4"] } -dirs = "5.0.1" -rand = "0.8.5" -opentelemetry = { version = "0.19.0", features = ["rt-tokio"] } -serde_bytes = "0.11.9" -chrono = { version = "0.4.26", features = ["serde", "std", "clock"], default-features = false } -gethostname = "0.4.3" -libc = "0.2.144" +serde_json = "1.0.149" +rmp-serde = "1.3.1" +uuid = { version = "1.16", features = ["serde", "v4"] } +dirs = "6.0.0" +rand = "0.9.2" +opentelemetry = { version = "0.20.0", features = ["rt-tokio"] } +serde_bytes = "0.11.19" +chrono = { version = "0.4.43", features = ["serde", "std", "clock"], default-features = false } +gethostname = "1.1.0" +libc = "0.2.180" tunnels = { git = "https://github.com/microsoft/dev-tunnels", rev = "8cae9b2a24c65c6c1958f5a0e77d72b23b5c6c30", default-features = false, features = ["connections"] } keyring = { version = "2.0.3", default-features = false, features = ["linux-secret-service-rt-tokio-crypto-openssl", "platform-windows", "platform-macos", "linux-keyutils"] } -dialoguer = "0.10.4" +dialoguer = "0.12.0" hyper = { version = "0.14.26", features = ["server", "http1", "runtime"] } -indicatif = "0.17.4" -tempfile = "3.5.0" -clap_lex = "0.7.0" +indicatif = "0.18.3" +tempfile = "3.12.0" +clap_lex = "0.7.7" url = "2.5.4" -async-trait = "0.1.68" -log = "0.4.18" -const_format = "0.2.31" -sha2 = "0.10.6" -base64 = "0.21.2" +async-trait = "0.1.89" +log = "0.4.29" +const_format = "0.2.35" +sha2 = "0.10.9" +base64 = "0.22.1" shell-escape = "0.1.5" -thiserror = "1.0.40" -cfg-if = "1.0.0" -pin-project = "1.1.0" -console = "0.15.7" -bytes = "1.4.0" -tar = "0.4.38" +thiserror = "2.0.18" +cfg-if = "1.0.4" +pin-project = "1.1.10" +console = "0.16.2" +bytes = "1.11.0" +tar = "0.4.44" [build-dependencies] serde = { version="1.0.163", features = ["derive"] } -serde_json = "1.0.96" +serde_json = "1.0.149" [target.'cfg(windows)'.dependencies] -winreg = "0.50.0" +winreg = "0.55.0" winapi = "0.3.9" [target.'cfg(target_os = "macos")'.dependencies] -core-foundation = "0.9.3" +core-foundation = "0.10.1" [target.'cfg(target_os = "linux")'.dependencies] -zbus = { version = "3.13.1", default-features = false, features = ["tokio"] } +zbus = { version = "5.4.0", default-features = false, features = ["tokio"] } [patch.crates-io] russh = { git = "https://github.com/microsoft/vscode-russh", branch = "main" } diff --git a/cli/ThirdPartyNotices.txt b/cli/ThirdPartyNotices.txt index 03ac5838391f..cc255c04cfdc 100644 --- a/cli/ThirdPartyNotices.txt +++ b/cli/ThirdPartyNotices.txt @@ -684,16 +684,6 @@ Unless you explicitly state otherwise, any contribution intentionally submitted [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 @@ -738,16 +728,6 @@ Unless you explicitly state otherwise, any contribution intentionally submitted [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 @@ -962,7 +942,7 @@ chrono 0.4.38 - MIT OR 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 +Apache 2.0 License [2]. Copyright (c) 2014--2025, Kang Seonghoon and contributors. Nota Bene: This is same as the Rust Project's own license. @@ -1525,16 +1505,6 @@ Unless you explicitly state otherwise, any contribution intentionally submitted [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 @@ -1674,10 +1644,7 @@ Unless you explicitly state otherwise, any contribution intentionally submitted [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 +[msrv-1.85]: https://img.shields.io/badge/rustc-1.85.0+-blue.svg [//]: # (crates) @@ -1839,10 +1806,7 @@ Unless you explicitly state otherwise, any contribution intentionally submitted [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 +[msrv-1.85]: https://img.shields.io/badge/rustc-1.85.0+-blue.svg [//]: # (crates) @@ -2272,7 +2236,7 @@ DEALINGS IN THE SOFTWARE. flate2 1.0.30 - MIT OR Apache-2.0 https://github.com/rust-lang/flate2-rs -Copyright (c) 2014 Alex Crichton +Copyright (c) 2014-2025 Alex Crichton Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated @@ -2388,7 +2352,7 @@ SOFTWARE. form_urlencoded 1.2.1 - MIT OR Apache-2.0 https://github.com/servo/rust-url -Copyright (c) 2013-2022 The rust-url developers +Copyright (c) 2013-2025 The rust-url developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated @@ -3225,16 +3189,6 @@ Unless you explicitly state otherwise, any contribution intentionally submitted [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 @@ -3278,9 +3232,7 @@ Unless you explicitly state otherwise, any contribution intentionally submitted [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.81]: 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.85]: https://img.shields.io/badge/rustc-1.85.0+-blue.svg [//]: # (crates) @@ -3344,7 +3296,7 @@ https://github.com/hyperium/http-body The MIT License (MIT) -Copyright (c) 2019-2024 Sean McArthur & Hyper Contributors +Copyright (c) 2019-2025 Sean McArthur & Hyper Contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated @@ -3376,7 +3328,7 @@ DEALINGS IN THE SOFTWARE. httparse 1.8.0 - MIT/Apache-2.0 https://github.com/seanmonstar/httparse -Copyright (c) 2015-2024 Sean McArthur +Copyright (c) 2015-2025 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 @@ -3430,7 +3382,7 @@ https://github.com/hyperium/hyper The MIT License (MIT) -Copyright (c) 2014-2021 Sean McArthur +Copyright (c) 2014-2025 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 @@ -4076,7 +4028,7 @@ ICU 1.8.1 to ICU 57.1 © 1995-2016 International Business Machines Corporation a idna 1.0.3 - MIT OR Apache-2.0 https://github.com/servo/rust-url/ -Copyright (c) 2013-2022 The rust-url developers +Copyright (c) 2013-2025 The rust-url developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated @@ -4220,16 +4172,6 @@ Unless you explicitly state otherwise, any contribution intentionally submitted [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 @@ -4509,8 +4451,6 @@ 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 @@ -5315,7 +5255,7 @@ DEALINGS IN THE SOFTWARE. num_cpus 1.16.0 - MIT OR Apache-2.0 https://github.com/seanmonstar/num_cpus -Copyright (c) 2015 +Copyright (c) 2015-2025 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 @@ -5459,7 +5399,7 @@ OTHER DEALINGS IN THE SOFTWARE. --------------------------------------------------------- -openssl 0.10.66 - Apache-2.0 +openssl 0.10.70 - Apache-2.0 https://github.com/sfackler/rust-openssl Copyright 2011-2017 Google Inc. @@ -5538,7 +5478,7 @@ DEALINGS IN THE SOFTWARE. --------------------------------------------------------- -openssl-sys 0.9.103 - MIT +openssl-sys 0.9.105 - MIT https://github.com/sfackler/rust-openssl The MIT License (MIT) @@ -6791,7 +6731,7 @@ DEALINGS IN THE SOFTWARE. percent-encoding 2.3.1 - MIT OR Apache-2.0 https://github.com/servo/rust-url/ -Copyright (c) 2013-2022 The rust-url developers +Copyright (c) 2013-2025 The rust-url developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated @@ -7505,7 +7445,7 @@ DEALINGS IN THE SOFTWARE. reqwest 0.11.27 - MIT OR Apache-2.0 https://github.com/seanmonstar/reqwest -Copyright (c) 2016 Sean McArthur +Copyright (c) 2016-2025 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 @@ -8358,7 +8298,7 @@ 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 +Copyright (c) 2025 secret-service Developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated @@ -8644,7 +8584,7 @@ Unless you explicitly state otherwise, any contribution intentionally submitted [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.81]: https://img.shields.io/badge/rustc-1.81.0+-blue.svg +[msrv-1.85]: https://img.shields.io/badge/rustc-1.85.0+-blue.svg [//]: # (crates) @@ -8737,7 +8677,7 @@ Unless you explicitly state otherwise, any contribution intentionally submitted [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.81]: https://img.shields.io/badge/rustc-1.81.0+-blue.svg +[msrv-1.85]: https://img.shields.io/badge/rustc-1.85.0+-blue.svg [//]: # (crates) @@ -10049,7 +9989,7 @@ https://github.com/seanmonstar/try-lock The MIT License (MIT) -Copyright (c) 2018-2023 Sean McArthur +Copyright (c) 2018-2025 Sean McArthur Copyright (c) 2016 Alex Crichton Permission is hereby granted, free of charge, to any person obtaining a copy @@ -10261,7 +10201,7 @@ DEALINGS IN THE SOFTWARE. url 2.5.4 - MIT OR Apache-2.0 https://github.com/servo/rust-url -Copyright (c) 2013-2022 The rust-url developers +Copyright (c) 2013-2025 The rust-url developers Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated @@ -11829,16 +11769,6 @@ Unless you explicitly state otherwise, any contribution intentionally submitted [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 diff --git a/cli/src/auth.rs b/cli/src/auth.rs index 9116e48339f3..d4d62a8bf104 100644 --- a/cli/src/auth.rs +++ b/cli/src/auth.rs @@ -665,12 +665,12 @@ impl Auth { .into(); } - return StatusError { + StatusError { body: String::from_utf8_lossy(&body).to_string(), status_code, url: url.to_string(), } - .into(); + .into() } /// Implements the device code flow, returning the credentials upon success. async fn do_device_code_flow(&self) -> Result { diff --git a/cli/src/log.rs b/cli/src/log.rs index 538827ed1242..f58f49b21764 100644 --- a/cli/src/log.rs +++ b/cli/src/log.rs @@ -282,7 +282,7 @@ pub struct DownloadLogger<'a> { logger: &'a Logger, } -impl<'a> crate::util::io::ReportCopyProgress for DownloadLogger<'a> { +impl crate::util::io::ReportCopyProgress for DownloadLogger<'_> { fn report_progress(&mut self, bytes_so_far: u64, total_bytes: u64) { if total_bytes > 0 { self.logger.emit( diff --git a/cli/src/tunnels/singleton_server.rs b/cli/src/tunnels/singleton_server.rs index 7fbd00d04d34..f183f935ce30 100644 --- a/cli/src/tunnels/singleton_server.rs +++ b/cli/src/tunnels/singleton_server.rs @@ -142,7 +142,7 @@ pub fn make_singleton_server( } } -pub async fn start_singleton_server<'a>( +pub async fn start_singleton_server( args: SingletonServerArgs<'_>, ) -> Result { let shutdown_rx = ShutdownRequest::create_rx([ diff --git a/cli/src/util/io.rs b/cli/src/util/io.rs index c5816ae55adb..4b8118a219f8 100644 --- a/cli/src/util/io.rs +++ b/cli/src/util/io.rs @@ -235,6 +235,7 @@ mod tests { .write(true) .read(true) .create(true) + .truncate(true) .open(&file_path) .unwrap(); @@ -271,6 +272,7 @@ mod tests { .write(true) .read(true) .create(true) + .truncate(true) .open(&file_path) .unwrap(); @@ -305,6 +307,7 @@ mod tests { .write(true) .read(true) .create(true) + .truncate(true) .open(&file_path) .unwrap(); let mut rng = rand::thread_rng(); diff --git a/cli/src/util/prereqs.rs b/cli/src/util/prereqs.rs index f8eff34ec39c..44c859772e38 100644 --- a/cli/src/util/prereqs.rs +++ b/cli/src/util/prereqs.rs @@ -20,19 +20,16 @@ lazy_static! { static ref LIBSTD_CXX_VERSION_RE: BinRegex = BinRegex::new(r"GLIBCXX_([0-9]+)\.([0-9]+)(?:\.([0-9]+))?").unwrap(); static ref MIN_LDD_VERSION: SimpleSemver = SimpleSemver::new(2, 28, 0); - static ref MIN_LEGACY_LDD_VERSION: SimpleSemver = SimpleSemver::new(2, 17, 0); } #[cfg(target_arch = "arm")] lazy_static! { static ref MIN_CXX_VERSION: SimpleSemver = SimpleSemver::new(3, 4, 26); - static ref MIN_LEGACY_CXX_VERSION: SimpleSemver = SimpleSemver::new(3, 4, 22); } #[cfg(not(target_arch = "arm"))] lazy_static! { static ref MIN_CXX_VERSION: SimpleSemver = SimpleSemver::new(3, 4, 25); - static ref MIN_LEGACY_CXX_VERSION: SimpleSemver = SimpleSemver::new(3, 4, 19); } const NIXOS_TEST_PATH: &str = "/etc/NIXOS"; @@ -74,12 +71,11 @@ impl PreReqChecker { } else { println!("!!! WARNING: Skipping server pre-requisite check !!!"); println!("!!! Server stability is not guaranteed. Proceed at your own risk. !!!"); - // Use the legacy server for #210029 (Ok(true), Ok(true)) }; match (&gnu_a, &gnu_b, is_nixos) { - (Ok(false), Ok(false), _) | (_, _, true) => { + (Ok(true), Ok(true), _) | (_, _, true) => { return Ok(if cfg!(target_arch = "x86_64") { Platform::LinuxX64 } else if cfg!(target_arch = "arm") { @@ -88,15 +84,6 @@ impl PreReqChecker { Platform::LinuxARM64 }); } - (Ok(_), Ok(_), _) => { - return Ok(if cfg!(target_arch = "x86_64") { - Platform::LinuxX64Legacy - } else if cfg!(target_arch = "arm") { - Platform::LinuxARM32Legacy - } else { - Platform::LinuxARM64Legacy - }); - } _ => {} }; @@ -149,7 +136,7 @@ async fn check_musl_interpreter() -> Result<(), String> { Ok(()) } -/// Checks the glibc version, returns "true" if the legacy server is required. +/// Checks the glibc version, returns "true" if the default server is required. #[cfg(target_os = "linux")] async fn check_glibc_version() -> Result { #[cfg(target_env = "gnu")] @@ -169,8 +156,6 @@ async fn check_glibc_version() -> Result { if let Some(v) = version { return if v >= *MIN_LDD_VERSION { - Ok(false) - } else if v >= *MIN_LEGACY_LDD_VERSION { Ok(true) } else { Err(format!( @@ -192,10 +177,17 @@ async fn check_is_nixos() -> bool { /// Do not remove this check. /// Provides a way to skip the server glibc requirements check from -/// outside the install flow. A system process can create this -/// file before the server is downloaded and installed. +/// outside the install flow. +/// +/// 1) A system process can create this +/// file before the server is downloaded and installed. +/// +/// 2) An environment variable declared in host +/// that contains path to a glibc sysroot satisfying the +/// minimum requirements. #[cfg(not(windows))] pub async fn skip_requirements_check() -> bool { + std::env::var("VSCODE_SERVER_CUSTOM_GLIBC_LINKER").is_ok() || fs::metadata("/tmp/vscode-skip-server-requirements-check") .await .is_ok() @@ -206,7 +198,7 @@ pub async fn skip_requirements_check() -> bool { false } -/// Checks the glibc++ version, returns "true" if the legacy server is required. +/// Checks the glibc++ version, returns "true" if the default server is required. #[cfg(target_os = "linux")] async fn check_glibcxx_version() -> Result { let mut libstdc_path: Option = None; @@ -250,10 +242,6 @@ fn check_for_sufficient_glibcxx_versions(contents: Vec) -> Result= &*MIN_CXX_VERSION { - return Ok(false); - } - - if max_version >= &*MIN_LEGACY_CXX_VERSION { return Ok(true); } } diff --git a/eslint.config.js b/eslint.config.js index 7263f3813130..2b005721dee1 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -224,15 +224,12 @@ export default tseslint.config( 'src/vs/platform/registry/test/common/platform.test.ts', 'src/vs/platform/workspace/test/common/workspace.test.ts', 'src/vs/platform/workspaces/test/electron-main/workspaces.test.ts', - 'src/vs/workbench/api/test/browser/mainThreadConfiguration.test.ts', - 'src/vs/workbench/api/test/node/extHostTunnelService.test.ts', 'src/vs/workbench/contrib/bulkEdit/test/browser/bulkCellEdits.test.ts', 'src/vs/workbench/contrib/chat/test/common/chatWordCounter.test.ts', 'src/vs/workbench/contrib/extensions/test/common/extensionQuery.test.ts', 'src/vs/workbench/contrib/notebook/test/browser/notebookExecutionService.test.ts', 'src/vs/workbench/contrib/notebook/test/browser/notebookExecutionStateService.test.ts', 'src/vs/workbench/contrib/tasks/test/common/problemMatcher.test.ts', - 'src/vs/workbench/contrib/tasks/test/common/taskConfiguration.test.ts', 'src/vs/workbench/services/commands/test/common/commandService.test.ts', 'src/vs/workbench/services/userActivity/test/browser/domActivityTracker.test.ts', 'src/vs/workbench/test/browser/quickAccess.test.ts' @@ -1000,6 +997,7 @@ export default tseslint.config( { 'target': 'src/vs/workbench/api/~', 'restrictions': [ + '@c4312/eventsource-umd', 'vscode', 'vs/base/~', 'vs/base/parts/*/~', diff --git a/extensions/configuration-editing/package-lock.json b/extensions/configuration-editing/package-lock.json index cba3a0fece64..6619002d1209 100644 --- a/extensions/configuration-editing/package-lock.json +++ b/extensions/configuration-editing/package-lock.json @@ -9,190 +9,191 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@octokit/rest": "^21.1.1", - "jsonc-parser": "^3.2.0", + "@octokit/rest": "^22.0.1", + "jsonc-parser": "^3.3.1", "tunnel": "^0.0.6" }, "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "engines": { "vscode": "^1.0.0" } }, "node_modules/@octokit/auth-token": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-5.1.2.tgz", - "integrity": "sha512-JcQDsBdg49Yky2w2ld20IHAlwr8d/d8N6NiOXbtuoPCqzbsiJgF633mVUw3x4mo0H5ypataQIX7SFu3yy44Mpw==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-6.0.0.tgz", + "integrity": "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==", "license": "MIT", "engines": { - "node": ">= 18" + "node": ">= 20" } }, "node_modules/@octokit/core": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-6.1.4.tgz", - "integrity": "sha512-lAS9k7d6I0MPN+gb9bKDt7X8SdxknYqAMh44S5L+lNqIN2NuV8nvv3g8rPp7MuRxcOpxpUIATWprO0C34a8Qmg==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-7.0.6.tgz", + "integrity": "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q==", "license": "MIT", "dependencies": { - "@octokit/auth-token": "^5.0.0", - "@octokit/graphql": "^8.1.2", - "@octokit/request": "^9.2.1", - "@octokit/request-error": "^6.1.7", - "@octokit/types": "^13.6.2", - "before-after-hook": "^3.0.2", + "@octokit/auth-token": "^6.0.0", + "@octokit/graphql": "^9.0.3", + "@octokit/request": "^10.0.6", + "@octokit/request-error": "^7.0.2", + "@octokit/types": "^16.0.0", + "before-after-hook": "^4.0.0", "universal-user-agent": "^7.0.0" }, "engines": { - "node": ">= 18" + "node": ">= 20" } }, "node_modules/@octokit/endpoint": { - "version": "10.1.3", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.3.tgz", - "integrity": "sha512-nBRBMpKPhQUxCsQQeW+rCJ/OPSMcj3g0nfHn01zGYZXuNDvvXudF/TYY6APj5THlurerpFN4a/dQAIAaM6BYhA==", + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-11.0.2.tgz", + "integrity": "sha512-4zCpzP1fWc7QlqunZ5bSEjxc6yLAlRTnDwKtgXfcI/FxxGoqedDG8V2+xJ60bV2kODqcGB+nATdtap/XYq2NZQ==", "license": "MIT", "dependencies": { - "@octokit/types": "^13.6.2", + "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.2" }, "engines": { - "node": ">= 18" + "node": ">= 20" } }, "node_modules/@octokit/graphql": { - "version": "8.2.1", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-8.2.1.tgz", - "integrity": "sha512-n57hXtOoHrhwTWdvhVkdJHdhTv0JstjDbDRhJfwIRNfFqmSo1DaK/mD2syoNUoLCyqSjBpGAKOG0BuwF392slw==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-9.0.3.tgz", + "integrity": "sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA==", "license": "MIT", "dependencies": { - "@octokit/request": "^9.2.2", - "@octokit/types": "^13.8.0", + "@octokit/request": "^10.0.6", + "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.0" }, "engines": { - "node": ">= 18" + "node": ">= 20" } }, "node_modules/@octokit/openapi-types": { - "version": "23.0.1", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-23.0.1.tgz", - "integrity": "sha512-izFjMJ1sir0jn0ldEKhZ7xegCTj/ObmEDlEfpFrx4k/JyZSMRHbO3/rBwgE7f3m2DHt+RrNGIVw4wSmwnm3t/g==", + "version": "27.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-27.0.0.tgz", + "integrity": "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA==", "license": "MIT" }, "node_modules/@octokit/plugin-paginate-rest": { - "version": "11.4.2", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-11.4.2.tgz", - "integrity": "sha512-BXJ7XPCTDXFF+wxcg/zscfgw2O/iDPtNSkwwR1W1W5c4Mb3zav/M2XvxQ23nVmKj7jpweB4g8viMeCQdm7LMVA==", + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-14.0.0.tgz", + "integrity": "sha512-fNVRE7ufJiAA3XUrha2omTA39M6IXIc6GIZLvlbsm8QOQCYvpq/LkMNGyFlB1d8hTDzsAXa3OKtybdMAYsV/fw==", "license": "MIT", "dependencies": { - "@octokit/types": "^13.7.0" + "@octokit/types": "^16.0.0" }, "engines": { - "node": ">= 18" + "node": ">= 20" }, "peerDependencies": { "@octokit/core": ">=6" } }, "node_modules/@octokit/plugin-request-log": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-5.3.1.tgz", - "integrity": "sha512-n/lNeCtq+9ofhC15xzmJCNKP2BWTv8Ih2TTy+jatNCCq/gQP/V7rK3fjIfuz0pDWDALO/o/4QY4hyOF6TQQFUw==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-6.0.0.tgz", + "integrity": "sha512-UkOzeEN3W91/eBq9sPZNQ7sUBvYCqYbrrD8gTbBuGtHEuycE4/awMXcYvx6sVYo7LypPhmQwwpUe4Yyu4QZN5Q==", "license": "MIT", "engines": { - "node": ">= 18" + "node": ">= 20" }, "peerDependencies": { "@octokit/core": ">=6" } }, "node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "13.3.1", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-13.3.1.tgz", - "integrity": "sha512-o8uOBdsyR+WR8MK9Cco8dCgvG13H1RlM1nWnK/W7TEACQBFux/vPREgKucxUfuDQ5yi1T3hGf4C5ZmZXAERgwQ==", + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-17.0.0.tgz", + "integrity": "sha512-B5yCyIlOJFPqUUeiD0cnBJwWJO8lkJs5d8+ze9QDP6SvfiXSz1BF+91+0MeI1d2yxgOhU/O+CvtiZ9jSkHhFAw==", "license": "MIT", "dependencies": { - "@octokit/types": "^13.8.0" + "@octokit/types": "^16.0.0" }, "engines": { - "node": ">= 18" + "node": ">= 20" }, "peerDependencies": { "@octokit/core": ">=6" } }, "node_modules/@octokit/request": { - "version": "9.2.2", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.2.2.tgz", - "integrity": "sha512-dZl0ZHx6gOQGcffgm1/Sf6JfEpmh34v3Af2Uci02vzUYz6qEN6zepoRtmybWXIGXFIK8K9ylE3b+duCWqhArtg==", + "version": "10.0.7", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-10.0.7.tgz", + "integrity": "sha512-v93h0i1yu4idj8qFPZwjehoJx4j3Ntn+JhXsdJrG9pYaX6j/XRz2RmasMUHtNgQD39nrv/VwTWSqK0RNXR8upA==", "license": "MIT", "dependencies": { - "@octokit/endpoint": "^10.1.3", - "@octokit/request-error": "^6.1.7", - "@octokit/types": "^13.6.2", - "fast-content-type-parse": "^2.0.0", + "@octokit/endpoint": "^11.0.2", + "@octokit/request-error": "^7.0.2", + "@octokit/types": "^16.0.0", + "fast-content-type-parse": "^3.0.0", "universal-user-agent": "^7.0.2" }, "engines": { - "node": ">= 18" + "node": ">= 20" } }, "node_modules/@octokit/request-error": { - "version": "6.1.7", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.7.tgz", - "integrity": "sha512-69NIppAwaauwZv6aOzb+VVLwt+0havz9GT5YplkeJv7fG7a40qpLt/yZKyiDxAhgz0EtgNdNcb96Z0u+Zyuy2g==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-7.1.0.tgz", + "integrity": "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw==", "license": "MIT", "dependencies": { - "@octokit/types": "^13.6.2" + "@octokit/types": "^16.0.0" }, "engines": { - "node": ">= 18" + "node": ">= 20" } }, "node_modules/@octokit/rest": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-21.1.1.tgz", - "integrity": "sha512-sTQV7va0IUVZcntzy1q3QqPm/r8rWtDCqpRAmb8eXXnKkjoQEtFe3Nt5GTVsHft+R6jJoHeSiVLcgcvhtue/rg==", + "version": "22.0.1", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-22.0.1.tgz", + "integrity": "sha512-Jzbhzl3CEexhnivb1iQ0KJ7s5vvjMWcmRtq5aUsKmKDrRW6z3r84ngmiFKFvpZjpiU/9/S6ITPFRpn5s/3uQJw==", "license": "MIT", "dependencies": { - "@octokit/core": "^6.1.4", - "@octokit/plugin-paginate-rest": "^11.4.2", - "@octokit/plugin-request-log": "^5.3.1", - "@octokit/plugin-rest-endpoint-methods": "^13.3.0" + "@octokit/core": "^7.0.6", + "@octokit/plugin-paginate-rest": "^14.0.0", + "@octokit/plugin-request-log": "^6.0.0", + "@octokit/plugin-rest-endpoint-methods": "^17.0.0" }, "engines": { - "node": ">= 18" + "node": ">= 20" } }, "node_modules/@octokit/types": { - "version": "13.8.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.8.0.tgz", - "integrity": "sha512-x7DjTIbEpEWXK99DMd01QfWy0hd5h4EN+Q7shkdKds3otGQP+oWE/y0A76i1OvH9fygo4ddvNf7ZvF0t78P98A==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-16.0.0.tgz", + "integrity": "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg==", "license": "MIT", "dependencies": { - "@octokit/openapi-types": "^23.0.1" + "@octokit/openapi-types": "^27.0.0" } }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/before-after-hook": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-3.0.2.tgz", - "integrity": "sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-4.0.0.tgz", + "integrity": "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==", "license": "Apache-2.0" }, "node_modules/fast-content-type-parse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-2.0.1.tgz", - "integrity": "sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-3.0.0.tgz", + "integrity": "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==", "funding": [ { "type": "github", @@ -206,9 +207,10 @@ "license": "MIT" }, "node_modules/jsonc-parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", - "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==" + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "license": "MIT" }, "node_modules/tunnel": { "version": "0.0.6", @@ -219,15 +221,16 @@ } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" }, "node_modules/universal-user-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.2.tgz", - "integrity": "sha512-0JCqzSKnStlRRQfCdowvqy3cy0Dvtlb8xecj/H8JFZuCze4rwjPZQOgvFvn0Ws/usCHQFGpyr+pB9adaGwXn4Q==", + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz", + "integrity": "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==", "license": "ISC" } } diff --git a/extensions/configuration-editing/package.json b/extensions/configuration-editing/package.json index 224ea33c02ad..66e18bcab14b 100644 --- a/extensions/configuration-editing/package.json +++ b/extensions/configuration-editing/package.json @@ -25,8 +25,8 @@ "watch": "gulp watch-extension:configuration-editing" }, "dependencies": { - "@octokit/rest": "^21.1.1", - "jsonc-parser": "^3.2.0", + "@octokit/rest": "^22.0.1", + "jsonc-parser": "^3.3.1", "tunnel": "^0.0.6" }, "capabilities": { @@ -49,6 +49,7 @@ "settings.json", "launch.json", "tasks.json", + "mcp.json", "keybindings.json", "extensions.json", "argv.json", @@ -117,6 +118,10 @@ "fileMatch": "/.vscode/tasks.json", "url": "vscode://schemas/tasks" }, + { + "fileMatch": "/.vscode/mcp.json", + "url": "vscode://schemas/mcp" + }, { "fileMatch": "%APP_SETTINGS_HOME%/tasks.json", "url": "vscode://schemas/tasks" @@ -164,7 +169,7 @@ ] }, "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "repository": { "type": "git", diff --git a/extensions/configuration-editing/src/settingsDocumentHelper.ts b/extensions/configuration-editing/src/settingsDocumentHelper.ts index 6135df5315a5..12b50f31759d 100644 --- a/extensions/configuration-editing/src/settingsDocumentHelper.ts +++ b/extensions/configuration-editing/src/settingsDocumentHelper.ts @@ -127,7 +127,7 @@ export class SettingsDocument { completions.push(this.newSimpleCompletionItem(getText('separator'), range, vscode.l10n.t("a conditional separator (' - ') that only shows when surrounded by variables with values"))); completions.push(this.newSimpleCompletionItem(getText('activeRepositoryName'), range, vscode.l10n.t("the name of the active repository (e.g. vscode)"))); completions.push(this.newSimpleCompletionItem(getText('activeRepositoryBranchName'), range, vscode.l10n.t("the name of the active branch in the active repository (e.g. main)"))); - + completions.push(this.newSimpleCompletionItem(getText('activeEditorState'), range, vscode.l10n.t("the state of the active editor (e.g. modified)."))); return completions; } diff --git a/extensions/csharp/cgmanifest.json b/extensions/csharp/cgmanifest.json index fefe63e47868..58ae5ece50ae 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": "62026a70f9fcc42d9222eccfec34ed5ee0784f3d" + "commitHash": "1381bedfb087c18aca67af8278050d11bc9d9349" } }, "license": "MIT", diff --git a/extensions/csharp/language-configuration.json b/extensions/csharp/language-configuration.json index 60814ae02f4a..7db6640f4c59 100644 --- a/extensions/csharp/language-configuration.json +++ b/extensions/csharp/language-configuration.json @@ -84,9 +84,10 @@ }, "onEnterRules": [ // Add // when pressing enter from inside line comment + // We do not want to match /// (a documentation comment) { "beforeText": { - "pattern": "\/\/.*" + "pattern": "[^\/]\/\/[^\/].*" }, "afterText": { "pattern": "^(?!\\s*$).+" @@ -96,5 +97,16 @@ "appendText": "// " } }, + // Add /// when pressing enter from anywhere inside a documentation comment. + // Documentation comments are not valid after non-whitespace. + { + "beforeText": { + "pattern": "^\\s*\/\/\/" + }, + "action": { + "indent": "none", + "appendText": "/// " + } + }, ] } diff --git a/extensions/csharp/syntaxes/csharp.tmLanguage.json b/extensions/csharp/syntaxes/csharp.tmLanguage.json index c4d9a2519dc3..1afcc3053b60 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/62026a70f9fcc42d9222eccfec34ed5ee0784f3d", + "version": "https://github.com/dotnet/csharp-tmLanguage/commit/1381bedfb087c18aca67af8278050d11bc9d9349", "name": "C#", "scopeName": "source.cs", "patterns": [ @@ -714,11 +714,11 @@ }, "enum-declaration": { "begin": "(?=\\benum\\b)", - "end": "(?<=\\})", + "end": "(?<=\\})|(?=;)", "patterns": [ { "begin": "(?=enum)", - "end": "(?=\\{)", + "end": "(?=\\{)|(?=;)", "patterns": [ { "include": "#comment" @@ -805,7 +805,7 @@ }, "interface-declaration": { "begin": "(?=\\binterface\\b)", - "end": "(?<=\\})", + "end": "(?<=\\})|(?=;)", "patterns": [ { "begin": "(?x)\n(interface)\\b\\s+\n(@?[_[:alpha:]][_[:alnum:]]*)", @@ -817,7 +817,7 @@ "name": "entity.name.type.interface.cs" } }, - "end": "(?=\\{)", + "end": "(?=\\{)|(?=;)", "patterns": [ { "include": "#comment" diff --git a/extensions/css-language-features/package-lock.json b/extensions/css-language-features/package-lock.json index a645c2ab11af..7ef1b266912f 100644 --- a/extensions/css-language-features/package-lock.json +++ b/extensions/css-language-features/package-lock.json @@ -9,70 +9,67 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "vscode-languageclient": "^10.0.0-next.13", - "vscode-uri": "^3.0.8" + "vscode-languageclient": "^10.0.0-next.19", + "vscode-uri": "^3.1.0" }, "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "engines": { "vscode": "^1.77.0" } }, - "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", - "dev": true, - "dependencies": { - "undici-types": "~5.26.4" + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "license": "MIT", + "engines": { + "node": "20 || >=22" } }, - "node_modules/balanced-match": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" - }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0" + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" } }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "node_modules/@types/node": { + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", + "dev": true, + "license": "MIT", "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" + "undici-types": "~7.16.0" } }, "node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^2.0.1" + "@isaacs/brace-expansion": "^5.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dependencies": { - "lru-cache": "^6.0.0" - }, + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -81,56 +78,56 @@ } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" }, "node_modules/vscode-jsonrpc": { - "version": "9.0.0-next.6", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.6.tgz", - "integrity": "sha512-KCSvUNsFiVciG9iqjJKBZOd66CN3ZKohDlYRmoOi+pd8l15MFLZ8wRG4c+wuzePGba/8WcCG2TM+C/GVlvuaeA==", + "version": "9.0.0-next.11", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.11.tgz", + "integrity": "sha512-u6LElQNbSiE9OugEEmrUKwH6+8BpPz2S5MDHvQUqHL//I4Q8GPikKLOUf856UnbLkZdhxaPrExac1lA3XwpIPA==", + "license": "MIT", "engines": { "node": ">=14.0.0" } }, "node_modules/vscode-languageclient": { - "version": "10.0.0-next.13", - "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-10.0.0-next.13.tgz", - "integrity": "sha512-KLsOMJoYpkk36PIgcOjyZ4AekOfzp4kdWdRRbVKeVvSIrwrn/4RSZr0NlD6EvUBBJSsJW4WDrYY7Y3znkqa6+w==", + "version": "10.0.0-next.19", + "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-10.0.0-next.19.tgz", + "integrity": "sha512-sJtO8y0Dxs4ue/DK0QgO/ATBfZwQdee3TqvCsoqUej/GZrBA01DTf4pbfswRxHsTxN2yH0haImUnMafWHtE4CQ==", "license": "MIT", "dependencies": { - "minimatch": "^9.0.3", - "semver": "^7.6.0", - "vscode-languageserver-protocol": "3.17.6-next.11" + "minimatch": "^10.0.3", + "semver": "^7.7.1", + "vscode-languageserver-protocol": "3.17.6-next.16" }, "engines": { "vscode": "^1.91.0" } }, "node_modules/vscode-languageserver-protocol": { - "version": "3.17.6-next.11", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.11.tgz", - "integrity": "sha512-GeJxEp1TiLsp79f8WG5n10wLViXfgFKb99hU9K8m7KDWM95/QFEqWkm79f9LVm54tUK74I91a9EeiQLCS/FABQ==", + "version": "3.17.6-next.16", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.16.tgz", + "integrity": "sha512-kQTjXEuyxMbdmmZ3U+Lib3oUl12xEKNc73RtWxPSDS3TFtjVwt98Q1CUzfDA9EUpsA24M46Bl6q3sLe9AUOKyw==", + "license": "MIT", "dependencies": { - "vscode-jsonrpc": "9.0.0-next.6", - "vscode-languageserver-types": "3.17.6-next.5" + "vscode-jsonrpc": "9.0.0-next.11", + "vscode-languageserver-types": "3.17.6-next.6" } }, "node_modules/vscode-languageserver-types": { - "version": "3.17.6-next.5", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.5.tgz", - "integrity": "sha512-QFmf3Yl1tCgUQfA77N9Me/LXldJXkIVypQbty2rJ1DNHQkC+iwvm4Z2tXg9czSwlhvv0pD4pbF5mT7WhAglolw==" + "version": "3.17.6-next.6", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.6.tgz", + "integrity": "sha512-aiJY5/yW+xzw7KPNlwi3gQtddq/3EIn5z8X8nCgJfaiAij2R1APKePngv+MUdLdYJBVTLu+Qa0ODsT+pHgYguQ==", + "license": "MIT" }, "node_modules/vscode-uri": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", - "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==" - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "license": "MIT" } } } diff --git a/extensions/css-language-features/package.json b/extensions/css-language-features/package.json index 0533881380c1..0779d640a7fa 100644 --- a/extensions/css-language-features/package.json +++ b/extensions/css-language-features/package.json @@ -994,11 +994,11 @@ ] }, "dependencies": { - "vscode-languageclient": "^10.0.0-next.13", - "vscode-uri": "^3.0.8" + "vscode-languageclient": "^10.0.0-next.19", + "vscode-uri": "^3.1.0" }, "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "repository": { "type": "git", diff --git a/extensions/css-language-features/server/package-lock.json b/extensions/css-language-features/server/package-lock.json index b9c936d84bf8..29a57f1d6361 100644 --- a/extensions/css-language-features/server/package-lock.json +++ b/extensions/css-language-features/server/package-lock.json @@ -10,31 +10,33 @@ "license": "MIT", "dependencies": { "@vscode/l10n": "^0.0.18", - "vscode-css-languageservice": "^6.3.2", - "vscode-languageserver": "^10.0.0-next.11", - "vscode-uri": "^3.0.8" + "vscode-css-languageservice": "^6.3.9", + "vscode-languageserver": "^10.0.0-next.16", + "vscode-uri": "^3.1.0" }, "devDependencies": { - "@types/mocha": "^9.1.1", - "@types/node": "20.x" + "@types/mocha": "^10.0.10", + "@types/node": "25.x" }, "engines": { "node": "*" } }, "node_modules/@types/mocha": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", - "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==", - "dev": true + "version": "10.0.10", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz", + "integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==", + "dev": true, + "license": "MIT" }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/@vscode/l10n": { @@ -43,56 +45,60 @@ "integrity": "sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ==" }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" }, "node_modules/vscode-css-languageservice": { - "version": "6.3.2", - "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-6.3.2.tgz", - "integrity": "sha512-GEpPxrUTAeXWdZWHev1OJU9lz2Q2/PPBxQ2TIRmLGvQiH3WZbqaNoute0n0ewxlgtjzTW3AKZT+NHySk5Rf4Eg==", + "version": "6.3.9", + "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-6.3.9.tgz", + "integrity": "sha512-1tLWfp+TDM5ZuVWht3jmaY5y7O6aZmpeXLoHl5bv1QtRsRKt4xYGRMmdJa5Pqx/FTkgRbsna9R+Gn2xE+evVuA==", "license": "MIT", "dependencies": { "@vscode/l10n": "^0.0.18", "vscode-languageserver-textdocument": "^1.0.12", "vscode-languageserver-types": "3.17.5", - "vscode-uri": "^3.0.8" + "vscode-uri": "^3.1.0" } }, "node_modules/vscode-jsonrpc": { - "version": "9.0.0-next.6", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.6.tgz", - "integrity": "sha512-KCSvUNsFiVciG9iqjJKBZOd66CN3ZKohDlYRmoOi+pd8l15MFLZ8wRG4c+wuzePGba/8WcCG2TM+C/GVlvuaeA==", + "version": "9.0.0-next.11", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.11.tgz", + "integrity": "sha512-u6LElQNbSiE9OugEEmrUKwH6+8BpPz2S5MDHvQUqHL//I4Q8GPikKLOUf856UnbLkZdhxaPrExac1lA3XwpIPA==", + "license": "MIT", "engines": { "node": ">=14.0.0" } }, "node_modules/vscode-languageserver": { - "version": "10.0.0-next.11", - "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-10.0.0-next.11.tgz", - "integrity": "sha512-cmobSrVDYhlh/t02vz/bV8nNpds8mus5HnILULae2iAvOjoaJPnTAp0jJWoYdUqTpIVzT9JV6JMKqLEvdqpeqg==", + "version": "10.0.0-next.16", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-10.0.0-next.16.tgz", + "integrity": "sha512-RbsYDOhddv1NtBCAR7+oVxxCmOpQUHhrtgUE0xz6J+BJGSCkfOqBCyLUIwSjKk2rK9llxUj/pR5aL8QCsXrxow==", "license": "MIT", "dependencies": { - "vscode-languageserver-protocol": "3.17.6-next.11" + "vscode-languageserver-protocol": "3.17.6-next.16" }, "bin": { "installServerIntoExtension": "bin/installServerIntoExtension" } }, "node_modules/vscode-languageserver-protocol": { - "version": "3.17.6-next.11", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.11.tgz", - "integrity": "sha512-GeJxEp1TiLsp79f8WG5n10wLViXfgFKb99hU9K8m7KDWM95/QFEqWkm79f9LVm54tUK74I91a9EeiQLCS/FABQ==", + "version": "3.17.6-next.16", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.16.tgz", + "integrity": "sha512-kQTjXEuyxMbdmmZ3U+Lib3oUl12xEKNc73RtWxPSDS3TFtjVwt98Q1CUzfDA9EUpsA24M46Bl6q3sLe9AUOKyw==", + "license": "MIT", "dependencies": { - "vscode-jsonrpc": "9.0.0-next.6", - "vscode-languageserver-types": "3.17.6-next.5" + "vscode-jsonrpc": "9.0.0-next.11", + "vscode-languageserver-types": "3.17.6-next.6" } }, "node_modules/vscode-languageserver-protocol/node_modules/vscode-languageserver-types": { - "version": "3.17.6-next.5", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.5.tgz", - "integrity": "sha512-QFmf3Yl1tCgUQfA77N9Me/LXldJXkIVypQbty2rJ1DNHQkC+iwvm4Z2tXg9czSwlhvv0pD4pbF5mT7WhAglolw==" + "version": "3.17.6-next.6", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.6.tgz", + "integrity": "sha512-aiJY5/yW+xzw7KPNlwi3gQtddq/3EIn5z8X8nCgJfaiAij2R1APKePngv+MUdLdYJBVTLu+Qa0ODsT+pHgYguQ==", + "license": "MIT" }, "node_modules/vscode-languageserver-textdocument": { "version": "1.0.12", @@ -105,9 +111,10 @@ "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==" }, "node_modules/vscode-uri": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", - "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "license": "MIT" } } } diff --git a/extensions/css-language-features/server/package.json b/extensions/css-language-features/server/package.json index 9b6a8b47d6b8..1e6bc0311b8e 100644 --- a/extensions/css-language-features/server/package.json +++ b/extensions/css-language-features/server/package.json @@ -11,13 +11,13 @@ "browser": "./dist/browser/cssServerMain", "dependencies": { "@vscode/l10n": "^0.0.18", - "vscode-css-languageservice": "^6.3.2", - "vscode-languageserver": "^10.0.0-next.11", - "vscode-uri": "^3.0.8" + "vscode-css-languageservice": "^6.3.9", + "vscode-languageserver": "^10.0.0-next.16", + "vscode-uri": "^3.1.0" }, "devDependencies": { - "@types/mocha": "^9.1.1", - "@types/node": "20.x" + "@types/mocha": "^10.0.10", + "@types/node": "25.x" }, "scripts": { "compile": "gulp compile-extension:css-language-features-server", diff --git a/extensions/debug-auto-launch/package-lock.json b/extensions/debug-auto-launch/package-lock.json index 84a1daab83bb..19ebcad6e8d7 100644 --- a/extensions/debug-auto-launch/package-lock.json +++ b/extensions/debug-auto-launch/package-lock.json @@ -9,26 +9,28 @@ "version": "1.0.0", "license": "MIT", "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "engines": { "vscode": "^1.5.0" } }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" } } } diff --git a/extensions/debug-auto-launch/package.json b/extensions/debug-auto-launch/package.json index 4a5d3361f95e..23514f6e87a5 100644 --- a/extensions/debug-auto-launch/package.json +++ b/extensions/debug-auto-launch/package.json @@ -33,7 +33,7 @@ ] }, "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "prettier": { "printWidth": 100, diff --git a/extensions/debug-server-ready/package-lock.json b/extensions/debug-server-ready/package-lock.json index 29a149e0e16a..c4d14ed743f2 100644 --- a/extensions/debug-server-ready/package-lock.json +++ b/extensions/debug-server-ready/package-lock.json @@ -9,26 +9,28 @@ "version": "1.0.0", "license": "MIT", "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "engines": { "vscode": "^1.32.0" } }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" } } } diff --git a/extensions/debug-server-ready/package.json b/extensions/debug-server-ready/package.json index 2afe977a9fc5..d2a061e06d18 100644 --- a/extensions/debug-server-ready/package.json +++ b/extensions/debug-server-ready/package.json @@ -212,7 +212,7 @@ ] }, "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "repository": { "type": "git", diff --git a/extensions/debug-server-ready/src/extension.ts b/extensions/debug-server-ready/src/extension.ts index 22a8ff836d34..40155f306bc8 100644 --- a/extensions/debug-server-ready/src/extension.ts +++ b/extensions/debug-server-ready/src/extension.ts @@ -23,7 +23,14 @@ interface ServerReadyAction { } // From src/vs/base/common/strings.ts -const CSI_SEQUENCE = /(?:(?:\x1b\[|\x9B)[=?>!]?[\d;:]*["$#'* ]?[a-zA-Z@^`{}|~])|(:?\x1b\].*?\x07)/g; +const CSI_SEQUENCE = /(?:\x1b\[|\x9b)[=?>!]?[\d;:]*["$#'* ]?[a-zA-Z@^`{}|~]/; +const OSC_SEQUENCE = /(?:\x1b\]|\x9d).*?(?:\x1b\\|\x07|\x9c)/; +const ESC_SEQUENCE = /\x1b(?:[ #%\(\)\*\+\-\.\/]?[a-zA-Z0-9\|}~@])/; +const CONTROL_SEQUENCES = new RegExp('(?:' + [ + CSI_SEQUENCE.source, + OSC_SEQUENCE.source, + ESC_SEQUENCE.source, +].join('|') + ')', 'g'); /** * Froms vs/base/common/strings.ts in core @@ -31,7 +38,7 @@ const CSI_SEQUENCE = /(?:(?:\x1b\[|\x9B)[=?>!]?[\d;:]*["$#'* ]?[a-zA-Z@^`{}|~])| */ function removeAnsiEscapeCodes(str: string): string { if (str) { - str = str.replace(CSI_SEQUENCE, ''); + str = str.replace(CONTROL_SEQUENCES, ''); } return str; diff --git a/extensions/emmet/package-lock.json b/extensions/emmet/package-lock.json index cadbf5387a8f..abac59d730ee 100644 --- a/extensions/emmet/package-lock.json +++ b/extensions/emmet/package-lock.json @@ -10,14 +10,14 @@ "license": "MIT", "dependencies": { "@emmetio/css-parser": "ramya-rao-a/css-parser#vscode", - "@emmetio/html-matcher": "^0.3.3", + "@emmetio/html-matcher": "^1.3.0", "@emmetio/math-expression": "^1.0.5", "@vscode/emmet-helper": "^2.8.8", - "image-size": "~1.0.0", + "image-size": "~2.0.2", "vscode-languageserver-textdocument": "^1.0.1" }, "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "engines": { "vscode": "^1.13.0" @@ -48,13 +48,12 @@ } }, "node_modules/@emmetio/html-matcher": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@emmetio/html-matcher/-/html-matcher-0.3.3.tgz", - "integrity": "sha512-+aeGmFXoR36nk2qzqPhBnWjnB38BV+dreTh/tsfbWP9kHv7fqRa9XuG1BSQFbPtKzsjUsBvGXkgGU3G8MkMw6A==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@emmetio/html-matcher/-/html-matcher-1.3.0.tgz", + "integrity": "sha512-NTbsvppE5eVyBMuyGfVu2CRrLvo7J4YHb6t9sBFLyY03WYhXET37qA4zOYUjBWFCRHO7pS1B9khERtY0f5JXPQ==", "license": "ISC", "dependencies": { - "@emmetio/stream-reader": "^2.0.0", - "@emmetio/stream-reader-utils": "^0.1.0" + "@emmetio/scanner": "^1.0.0" } }, "node_modules/@emmetio/math-expression": { @@ -81,12 +80,13 @@ "integrity": "sha1-JEywLHfsLnT3ipvTGCGKvJxQCmE= sha512-ZsZ2I9Vzso3Ho/pjZFsmmZ++FWeEd/txqybHTm4OgaZzdS8V9V/YYWQwg5TC38Z7uLWUV1vavpLLbjJtKubR1A==" }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/@vscode/emmet-helper": { @@ -119,42 +119,28 @@ } }, "node_modules/image-size": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/image-size/-/image-size-1.0.0.tgz", - "integrity": "sha512-JLJ6OwBfO1KcA+TvJT+v8gbE6iWbj24LyDNFgFEN0lzegn6cC6a/p3NIDaepMsJjQjlUWqIC7wJv8lBFxPNjcw==", - "dependencies": { - "queue": "6.0.2" - }, + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/image-size/-/image-size-2.0.2.tgz", + "integrity": "sha512-IRqXKlaXwgSMAMtpNzZa1ZAe8m+Sa1770Dhk8VkSsP9LS+iHD62Zd8FQKs8fbPiagBE7BzoFX23cxFnwshpV6w==", + "license": "MIT", "bin": { "image-size": "bin/image-size.js" }, "engines": { - "node": ">=12.0.0" + "node": ">=16.x" } }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" - }, "node_modules/jsonc-parser": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-2.3.1.tgz", "integrity": "sha512-H8jvkz1O50L3dMZCsLqiuB2tA7muqbSg1AtGEkN0leAqGjsUzDJir3Zwr02BhqdcITPg3ei3mZ+HjMocAknhhg==" }, - "node_modules/queue": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/queue/-/queue-6.0.2.tgz", - "integrity": "sha512-iHZWu+q3IdFZFX36ro/lKBkSvfkztY5Y7HMiPlOUjhupPcG2JMfst2KKEpu5XndviX/3UhFbRngUPNKtgvtZiA==", - "dependencies": { - "inherits": "~2.0.3" - } - }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" }, "node_modules/vscode-languageserver-textdocument": { "version": "1.0.12", diff --git a/extensions/emmet/package.json b/extensions/emmet/package.json index a390a86fc2ec..b4c19e159f87 100644 --- a/extensions/emmet/package.json +++ b/extensions/emmet/package.json @@ -479,14 +479,14 @@ "deps": "npm install @vscode/emmet-helper" }, "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "dependencies": { "@emmetio/css-parser": "ramya-rao-a/css-parser#vscode", - "@emmetio/html-matcher": "^0.3.3", + "@emmetio/html-matcher": "^1.3.0", "@emmetio/math-expression": "^1.0.5", "@vscode/emmet-helper": "^2.8.8", - "image-size": "~1.0.0", + "image-size": "~2.0.2", "vscode-languageserver-textdocument": "^1.0.1" }, "capabilities": { diff --git a/extensions/extension-editing/package-lock.json b/extensions/extension-editing/package-lock.json index 3fa0c35e2d06..c4b1f5e99162 100644 --- a/extensions/extension-editing/package-lock.json +++ b/extensions/extension-editing/package-lock.json @@ -9,31 +9,51 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "jsonc-parser": "^3.2.0", - "markdown-it": "^12.3.2", - "parse5": "^3.0.2" + "jsonc-parser": "^3.3.1", + "markdown-it": "^14.1.0", + "parse5": "^8.0.0" }, "devDependencies": { - "@types/markdown-it": "0.0.2", - "@types/node": "20.x" + "@types/markdown-it": "14.1.2", + "@types/node": "25.x" }, "engines": { "vscode": "^1.4.0" } }, + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/markdown-it": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-0.0.2.tgz", - "integrity": "sha1-XZrRnm5lCM3S8llt+G/Qqt5ZhmA= sha512-A2seE+zJYSjGHy7L/v0EN/xRfgv2A60TuXOwI8tt5aZxF4UeoYIkM2jERnNH8w4VFr7oFEm0lElGOao7fZgygQ==", - "dev": true + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, + "node_modules/@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "dev": true, + "license": "MIT" }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/argparse": { @@ -42,69 +62,100 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==" }, "node_modules/entities": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", - "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, "funding": { "url": "https://github.com/fb55/entities?sponsor=1" } }, "node_modules/jsonc-parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", - "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==" + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "license": "MIT" }, "node_modules/linkify-it": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", - "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "license": "MIT", "dependencies": { - "uc.micro": "^1.0.1" + "uc.micro": "^2.0.0" } }, "node_modules/markdown-it": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", - "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "license": "MIT", "dependencies": { "argparse": "^2.0.1", - "entities": "~2.1.0", - "linkify-it": "^3.0.1", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" }, "bin": { - "markdown-it": "bin/markdown-it.js" + "markdown-it": "bin/markdown-it.mjs" } }, "node_modules/mdurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha1-/oWy7HWlkDfyrf7BAP1sYBdhFS4= sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "license": "MIT" }, "node_modules/parse5": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/parse5/-/parse5-3.0.2.tgz", - "integrity": "sha1-Be/1fw70V3+xRKefi5qWemzERRA= sha512-yQW05f47bKFJa0WdnyzP7vh7+B+w8jhVsFBBiaEbIfNDSSt8GADBhcQgsdYxatQ7rVs1nU9cmsYXURGWBH3Siw==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz", + "integrity": "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==", + "license": "MIT", "dependencies": { - "@types/node": "^6.0.46" + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/parse5/node_modules/@types/node": { - "version": "6.0.78", - "resolved": "https://registry.npmjs.org/@types/node/-/node-6.0.78.tgz", - "integrity": "sha512-+vD6E8ixntRzzZukoF3uP1iV+ZjVN3koTcaeK+BEoc/kSfGbLDIGC7RmCaUgVpUfN6cWvfczFRERCyKM9mkvXg==" + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "license": "MIT", + "engines": { + "node": ">=6" + } }, "node_modules/uc.micro": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", - "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "license": "MIT" }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" } } } diff --git a/extensions/extension-editing/package.json b/extensions/extension-editing/package.json index 184d28e8df0c..03f7f6398836 100644 --- a/extensions/extension-editing/package.json +++ b/extensions/extension-editing/package.json @@ -26,9 +26,9 @@ "watch": "gulp watch-extension:extension-editing" }, "dependencies": { - "jsonc-parser": "^3.2.0", - "markdown-it": "^12.3.2", - "parse5": "^3.0.2" + "jsonc-parser": "^3.3.1", + "markdown-it": "^14.1.0", + "parse5": "^8.0.0" }, "contributes": { "jsonValidation": [ @@ -66,8 +66,8 @@ ] }, "devDependencies": { - "@types/markdown-it": "0.0.2", - "@types/node": "20.x" + "@types/markdown-it": "14.1.2", + "@types/node": "25.x" }, "repository": { "type": "git", diff --git a/extensions/git-base/package-lock.json b/extensions/git-base/package-lock.json index f4b29739ca63..05a194b412e9 100644 --- a/extensions/git-base/package-lock.json +++ b/extensions/git-base/package-lock.json @@ -9,26 +9,28 @@ "version": "1.0.0", "license": "MIT", "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "engines": { "vscode": "0.10.x" } }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" } } } diff --git a/extensions/git-base/package.json b/extensions/git-base/package.json index 3c9b07a13e8f..139b8d064fd7 100644 --- a/extensions/git-base/package.json +++ b/extensions/git-base/package.json @@ -104,7 +104,7 @@ ] }, "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "repository": { "type": "git", diff --git a/extensions/git/package-lock.json b/extensions/git/package-lock.json index bc150555c70c..f12ed5a6f84f 100644 --- a/extensions/git/package-lock.json +++ b/extensions/git/package-lock.json @@ -10,26 +10,36 @@ "license": "MIT", "dependencies": { "@joaomoreno/unique-names-generator": "^5.2.0", - "@vscode/extension-telemetry": "^0.9.8", - "@vscode/iconv-lite-umd": "0.7.0", + "@vscode/extension-telemetry": "^1.4.0", + "@vscode/iconv-lite-umd": "0.7.1", "byline": "^5.0.0", - "file-type": "16.5.4", + "file-type": "21.3.0", "jschardet": "3.1.4", - "picomatch": "2.3.1", - "vscode-uri": "^2.0.0", - "which": "4.0.0" + "picomatch": "4.0.3", + "vscode-uri": "^3.1.0", + "which": "6.0.0" }, "devDependencies": { - "@types/byline": "4.2.31", - "@types/mocha": "^9.1.1", - "@types/node": "20.x", - "@types/picomatch": "2.3.0", - "@types/which": "3.0.0" + "@types/byline": "4.2.36", + "@types/mocha": "^10.0.10", + "@types/node": "25.x", + "@types/picomatch": "4.0.2", + "@types/which": "3.0.4" }, "engines": { "vscode": "^1.5.0" } }, + "node_modules/@borewit/text-codec": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@borewit/text-codec/-/text-codec-0.1.1.tgz", + "integrity": "sha512-5L/uBxmjaCIX5h8Z+uu+kA9BQLkc/Wl06UGR5ajNRxu+/XjonB5i8JpgFMrPj3LXTCPA0pv8yxUvbUi+QthGGA==", + "license": "MIT", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/@joaomoreno/unique-names-generator": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/@joaomoreno/unique-names-generator/-/unique-names-generator-5.2.0.tgz", @@ -40,73 +50,73 @@ } }, "node_modules/@microsoft/1ds-core-js": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.4.tgz", - "integrity": "sha512-3gbDUQgAO8EoyQTNcAEkxpuPnioC0May13P1l1l0NKZ128L9Ts/sj8QsfwCRTjHz0HThlA+4FptcAJXNYUy3rg==", + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.10.tgz", + "integrity": "sha512-5fSZmkGwWkH+mrIA5M1GYPZdPM+SjXwCCl2Am7VhFoVwOBJNhRnwvIpAdzw6sFjiebN/rz+/YH0NdxztGZSa9Q==", "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" } }, "node_modules/@microsoft/1ds-post-js": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.4.tgz", - "integrity": "sha512-nlKjWricDj0Tn68Dt0P8lX9a+X7LYrqJ6/iSfQwMfDhRIGLqW+wxx8gxS+iGWC/oc8zMQAeiZaemUpCwQcwpRQ==", + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.10.tgz", + "integrity": "sha512-VSLjc9cT+Y+eTiSfYltJHJCejn8oYr0E6Pq2BMhOEO7F6IyLGYIxzKKvo78ze9x+iHX7KPTATcZ+PFgjGXuNqg==", "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "4.3.4", + "@microsoft/1ds-core-js": "4.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" } }, "node_modules/@microsoft/applicationinsights-channel-js": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.4.tgz", - "integrity": "sha512-Z4nrxYwGKP9iyrYtm7iPQXVOFy4FsEsX0nDKkAi96Qpgw+vEh6NH4ORxMMuES0EollBQ3faJyvYCwckuCVIj0g==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.10.tgz", + "integrity": "sha512-iolFLz1ocWAzIQqHIEjjov3gNTPkgFQ4ArHnBcJEYoffOGWlJt6copaevS5YPI5rHzmbySsengZ8cLJJBBrXzQ==", "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-common": "3.3.4", - "@microsoft/applicationinsights-core-js": "3.3.4", + "@microsoft/applicationinsights-common": "3.3.10", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-common": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.4.tgz", - "integrity": "sha512-4ms16MlIvcP4WiUPqopifNxcWCcrXQJ2ADAK/75uok2mNQe6ZNRsqb/P+pvhUxc8A5HRlvoXPP1ptDSN5Girgw==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.10.tgz", + "integrity": "sha512-RVIenPIvNgZCbjJdALvLM4rNHgAFuHI7faFzHCgnI6S2WCUNGHeXlQTs9EUUrL+n2TPp9/cd0KKMILU5VVyYiA==", "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-core-js": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.4.tgz", - "integrity": "sha512-MummANF0mgKIkdvVvfmHQTBliK114IZLRhTL0X0Ep+zjDwWMHqYZgew0nlFKAl6ggu42abPZFK5afpE7qjtYJA==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.10.tgz", + "integrity": "sha512-5yKeyassZTq2l+SAO4npu6LPnbS++UD+M+Ghjm9uRzoBwD8tumFx0/F8AkSVqbniSREd+ztH/2q2foewa2RZyg==", "license": "MIT", "dependencies": { "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { "tslib": ">= 1.0.0" @@ -122,18 +132,18 @@ } }, "node_modules/@microsoft/applicationinsights-web-basic": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.4.tgz", - "integrity": "sha512-OpEPXr8vU/t/M8T9jvWJzJx/pCyygIiR1nGM/2PTde0wn7anl71Gxl5fWol7K/WwFEORNjkL3CEyWOyDc+28AA==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.10.tgz", + "integrity": "sha512-AZib5DAT3NU0VT0nLWEwXrnoMDDgZ/5S4dso01CNU5ELNxLdg+1fvchstlVdMy4FrAnxzs8Wf/GIQNFYOVgpAw==", "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-channel-js": "3.3.4", - "@microsoft/applicationinsights-common": "3.3.4", - "@microsoft/applicationinsights-core-js": "3.3.4", + "@microsoft/applicationinsights-channel-js": "3.3.10", + "@microsoft/applicationinsights-common": "3.3.10", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { "tslib": ">= 1.0.0" @@ -158,70 +168,94 @@ } }, "node_modules/@nevware21/ts-utils": { - "version": "0.11.6", - "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.11.6.tgz", - "integrity": "sha512-OUUJTh3fnaUSzg9DEHgv3d7jC+DnPL65mIO7RaR+jWve7+MmcgIvF79gY97DPQ4frH+IpNR78YAYd/dW4gK3kg==", + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.12.5.tgz", + "integrity": "sha512-JPQZWPKQJjj7kAftdEZL0XDFfbMgXCGiUAZe0d7EhLC3QlXTlZdSckGqqRIQ2QNl0VTEZyZUvRBw6Ednw089Fw==", "license": "MIT" }, + "node_modules/@tokenizer/inflate": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@tokenizer/inflate/-/inflate-0.4.1.tgz", + "integrity": "sha512-2mAv+8pkG6GIZiF1kNg1jAjh27IDxEPKwdGul3snfztFerfPGI1LjDezZp3i7BElXompqEtPmoPx6c2wgtWsOA==", + "license": "MIT", + "dependencies": { + "debug": "^4.4.3", + "token-types": "^6.1.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/Borewit" + } + }, "node_modules/@tokenizer/token": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/@tokenizer/token/-/token-0.3.0.tgz", - "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==" + "integrity": "sha512-OvjF+z51L3ov0OyAU0duzsYuvO01PH7x4t6DJx+guahgTnBHkhJdG7soQeTSFLWN3efnHyibZ4Z8l2EuWwJN3A==", + "license": "MIT" }, "node_modules/@types/byline": { - "version": "4.2.31", - "resolved": "https://registry.npmjs.org/@types/byline/-/byline-4.2.31.tgz", - "integrity": "sha1-DmH8ucA+BH0hxEllVMcRYperYM0= sha512-TC6Ljn7tALesQMQyTNoMWoM44SNvWtCLkJDrA/TxcwE5ILkWt4zi5wbEokqiDk42S75eykAY1onPImWDybOkmQ==", + "version": "4.2.36", + "resolved": "https://registry.npmjs.org/@types/byline/-/byline-4.2.36.tgz", + "integrity": "sha512-dO55KDSaOSE+3T8TwP66mzn0u/PM/aSedVMr1tby7WBNjfLIuS6IbYXi1mlau49sVSVB+gXKJscWE0JO3tlXDw==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } }, "node_modules/@types/mocha": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", - "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==", - "dev": true + "version": "10.0.10", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz", + "integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==", + "dev": true, + "license": "MIT" }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/@types/picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@types/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-O397rnSS9iQI4OirieAtsDqvCj4+3eY1J+EPdNTKuHuRWIfUoGyzX294o8C4KJYaLqgSrd2o60c5EqCU8Zv02g==", - "dev": true + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-qHHxQ+P9PysNEGbALT8f8YOSHW0KJu6l2xU8DYY0fu/EmGxXdVnuTLvFUvBgPJMSqXq29SYHveejeAha+4AYgA==", + "dev": true, + "license": "MIT" }, "node_modules/@types/which": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/which/-/which-3.0.0.tgz", - "integrity": "sha512-ASCxdbsrwNfSMXALlC3Decif9rwDMu+80KGp5zI2RLRotfMsTv7fHL8W8VDp24wymzDyIFudhUeSCugrgRFfHQ==", - "dev": true + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/which/-/which-3.0.4.tgz", + "integrity": "sha512-liyfuo/106JdlgSchJzXEQCVArk0CvevqPote8F8HgWgJ3dRCcTHgJIsLDuee0kxk/mhbInzIZk3QWSZJ8R+2w==", + "dev": true, + "license": "MIT" }, "node_modules/@vscode/extension-telemetry": { - "version": "0.9.8", - "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.8.tgz", - "integrity": "sha512-7YcKoUvmHlIB8QYCE4FNzt3ErHi9gQPhdCM3ZWtpw1bxPT0I+lMdx52KHlzTNoJzQ2NvMX7HyzyDwBEiMgTrWQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-1.4.0.tgz", + "integrity": "sha512-mqypuqIDn+vQnaQ8tMdvCRn9BprZTH2j6nlFFv9hE6hB25n32F8rOZinos8OgblRi87X1qdBpjpb0Em1LGSG9w==", "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "^4.3.4", - "@microsoft/1ds-post-js": "^4.3.4", - "@microsoft/applicationinsights-web-basic": "^3.3.4" + "@microsoft/1ds-core-js": "^4.3.10", + "@microsoft/1ds-post-js": "^4.3.10", + "@microsoft/applicationinsights-web-basic": "^3.3.10" }, "engines": { "vscode": "^1.75.0" } }, "node_modules/@vscode/iconv-lite-umd": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@vscode/iconv-lite-umd/-/iconv-lite-umd-0.7.0.tgz", - "integrity": "sha512-bRRFxLfg5dtAyl5XyiVWz/ZBPahpOpPrNYnnHpOpUZvam4tKH35wdhP4Kj6PbM0+KdliOsPzbGWpkxcdpNB/sg==" + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@vscode/iconv-lite-umd/-/iconv-lite-umd-0.7.1.tgz", + "integrity": "sha512-tK6k0DXFHW7q5+GGuGZO+phpAqpxO4WXl+BLc/8/uOk3RsM2ssAL3CQUQDb1TGfwltjsauhN6S4ghYZzs4sPFw==", + "license": "MIT" }, "node_modules/byline": { "version": "5.0.0", @@ -231,17 +265,36 @@ "node": ">=0.10.0" } }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, "node_modules/file-type": { - "version": "16.5.4", - "resolved": "https://registry.npmjs.org/file-type/-/file-type-16.5.4.tgz", - "integrity": "sha512-/yFHK0aGjFEgDJjEKP0pWCplsPFPhwyfwevf/pVxiN0tmE4L9LmwWxWukdJSHdoCli4VgQLehjJtwQBnqmsKcw==", + "version": "21.3.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-21.3.0.tgz", + "integrity": "sha512-8kPJMIGz1Yt/aPEwOsrR97ZyZaD1Iqm8PClb1nYFclUCkBi0Ma5IsYNQzvSFS9ib51lWyIw5mIT9rWzI/xjpzA==", + "license": "MIT", "dependencies": { - "readable-web-to-node-stream": "^3.0.0", - "strtok3": "^6.2.4", - "token-types": "^4.1.1" + "@tokenizer/inflate": "^0.4.1", + "strtok3": "^10.3.4", + "token-types": "^6.1.1", + "uint8array-extras": "^1.4.0" }, "engines": { - "node": ">=10" + "node": ">=20" }, "funding": { "url": "https://github.com/sindresorhus/file-type?sponsor=1" @@ -264,12 +317,8 @@ "type": "consulting", "url": "https://feross.org/support" } - ] - }, - "node_modules/inherits": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + ], + "license": "BSD-3-Clause" }, "node_modules/isexe": { "version": "3.1.1", @@ -288,136 +337,88 @@ "node": ">=0.1.90" } }, - "node_modules/peek-readable": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/peek-readable/-/peek-readable-4.1.0.tgz", - "integrity": "sha512-ZI3LnwUv5nOGbQzD9c2iDG6toheuXSZP5esSHBjopsXH4dg19soufvpUGA3uohi5anFtGb2lhAVdHzH6R/Evvg==", - "engines": { - "node": ">=8" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" - } + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "license": "MIT" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "license": "MIT", "engines": { - "node": ">=8.6" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", - "dependencies": { - "inherits": "^2.0.3", - "string_decoder": "^1.1.1", - "util-deprecate": "^1.0.1" - }, - "engines": { - "node": ">= 6" - } - }, - "node_modules/readable-web-to-node-stream": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/readable-web-to-node-stream/-/readable-web-to-node-stream-3.0.2.tgz", - "integrity": "sha512-ePeK6cc1EcKLEhJFt/AebMCLL+GgSKhuygrZ/GLaKZYEecIgIECf4UaUuaByiGtzckwR4ain9VzUh95T1exYGw==", + "node_modules/strtok3": { + "version": "10.3.4", + "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-10.3.4.tgz", + "integrity": "sha512-KIy5nylvC5le1OdaaoCJ07L+8iQzJHGH6pWDuzS+d07Cu7n1MZ2x26P8ZKIWfbK02+XIL8Mp4RkWeqdUCrDMfg==", + "license": "MIT", "dependencies": { - "readable-stream": "^3.6.0" + "@tokenizer/token": "^0.3.0" }, "engines": { - "node": ">=8" + "node": ">=18" }, "funding": { "type": "github", "url": "https://github.com/sponsors/Borewit" } }, - "node_modules/safe-buffer": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", - "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/feross" - }, - { - "type": "patreon", - "url": "https://www.patreon.com/feross" - }, - { - "type": "consulting", - "url": "https://feross.org/support" - } - ] - }, - "node_modules/string_decoder": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", - "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", - "dependencies": { - "safe-buffer": "~5.2.0" - } - }, - "node_modules/strtok3": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/strtok3/-/strtok3-6.3.0.tgz", - "integrity": "sha512-fZtbhtvI9I48xDSywd/somNqgUHl2L2cstmXCCif0itOf96jeW18MBSyrLuNicYQVkvpOxkZtkzujiTJ9LW5Jw==", + "node_modules/token-types": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/token-types/-/token-types-6.1.1.tgz", + "integrity": "sha512-kh9LVIWH5CnL63Ipf0jhlBIy0UsrMj/NJDfpsy1SqOXlLKEVyXXYrnFxFT1yOOYVGBSApeVnjPw/sBz5BfEjAQ==", + "license": "MIT", "dependencies": { + "@borewit/text-codec": "^0.1.0", "@tokenizer/token": "^0.3.0", - "peek-readable": "^4.1.0" + "ieee754": "^1.2.1" }, "engines": { - "node": ">=10" + "node": ">=14.16" }, "funding": { "type": "github", "url": "https://github.com/sponsors/Borewit" } }, - "node_modules/token-types": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/token-types/-/token-types-4.2.0.tgz", - "integrity": "sha512-P0rrp4wUpefLncNamWIef62J0v0kQR/GfDVji9WKY7GDCWy5YbVSrKUTam07iWPZQGy0zWNOfstYTykMmPNR7w==", - "dependencies": { - "@tokenizer/token": "^0.3.0", - "ieee754": "^1.2.1" - }, + "node_modules/uint8array-extras": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/uint8array-extras/-/uint8array-extras-1.5.0.tgz", + "integrity": "sha512-rvKSBiC5zqCCiDZ9kAOszZcDvdAHwwIKJG33Ykj43OKcWsnmcBRL09YTU4nOeHZ8Y2a7l1MgTd08SBe9A8Qj6A==", + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true - }, - "node_modules/util-deprecate": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" }, "node_modules/vscode-uri": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-2.0.0.tgz", - "integrity": "sha512-lWXWofDSYD8r/TIyu64MdwB4FaSirQ608PP/TzUyslyOeHGwQ0eTHUZeJrK1ILOmwUHaJtV693m2JoUYroUDpw==" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "license": "MIT" }, "node_modules/which": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", - "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.0.tgz", + "integrity": "sha512-f+gEpIKMR9faW/JgAgPK1D7mekkFoqbmiwvNzuhsHetni20QSgzg9Vhn0g2JSJkkfehQnqdUAx7/e15qS1lPxg==", + "license": "ISC", "dependencies": { "isexe": "^3.1.1" }, @@ -425,7 +426,7 @@ "node-which": "bin/which.js" }, "engines": { - "node": "^16.13.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } } } diff --git a/extensions/git/package.json b/extensions/git/package.json index f5195eee8fb7..1815af8783d6 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -21,7 +21,6 @@ "contribSourceControlInputBoxMenu", "contribSourceControlTitleMenu", "contribViewsWelcome", - "diffCommand", "editSessionIdentityProvider", "quickDiffProvider", "quickInputButtonLocation", @@ -3322,10 +3321,15 @@ "markdownDescription": "%config.diagnosticsCommitHook.Sources%", "scope": "resource" }, - "git.untrackedChangesSoftDelete": { + "git.discardUntrackedChangesToTrash": { "type": "boolean", "default": true, - "markdownDescription": "%config.untrackedChangesSoftDelete%" + "markdownDescription": "%config.discardUntrackedChangesToTrash%" + }, + "git.showReferenceDetails": { + "type": "boolean", + "default": false, + "markdownDescription": "%config.showReferenceDetails%" } } }, @@ -3434,10 +3438,10 @@ "id": "git.blame.editorDecorationForeground", "description": "%colors.blameEditorDecoration%", "defaults": { - "dark": "editorCodeLens.foreground", - "light": "editorCodeLens.foreground", - "highContrast": "editorCodeLens.foreground", - "highContrastLight": "editorCodeLens.foreground" + "dark": "editorInlayHint.foreground", + "light": "editorInlayHint.foreground", + "highContrast": "editorInlayHint.foreground", + "highContrastLight": "editorInlayHint.foreground" } } ], @@ -3562,21 +3566,21 @@ }, "dependencies": { "@joaomoreno/unique-names-generator": "^5.2.0", - "@vscode/extension-telemetry": "^0.9.8", - "@vscode/iconv-lite-umd": "0.7.0", + "@vscode/extension-telemetry": "^1.4.0", + "@vscode/iconv-lite-umd": "0.7.1", "byline": "^5.0.0", - "file-type": "16.5.4", + "file-type": "21.3.0", "jschardet": "3.1.4", - "picomatch": "2.3.1", - "vscode-uri": "^2.0.0", - "which": "4.0.0" + "picomatch": "4.0.3", + "vscode-uri": "^3.1.0", + "which": "6.0.0" }, "devDependencies": { - "@types/byline": "4.2.31", - "@types/mocha": "^9.1.1", - "@types/node": "20.x", - "@types/picomatch": "2.3.0", - "@types/which": "3.0.0" + "@types/byline": "4.2.36", + "@types/mocha": "^10.0.10", + "@types/node": "25.x", + "@types/picomatch": "4.0.2", + "@types/which": "3.0.4" }, "repository": { "type": "git", diff --git a/extensions/git/package.nls.json b/extensions/git/package.nls.json index 08b0cc49ed57..52ba38198170 100644 --- a/extensions/git/package.nls.json +++ b/extensions/git/package.nls.json @@ -288,7 +288,8 @@ "config.commitShortHashLength": "Controls the length of the commit short hash.", "config.diagnosticsCommitHook.Enabled": "Controls whether to check for unresolved diagnostics before committing.", "config.diagnosticsCommitHook.Sources": "Controls the list of sources (**Item**) and the minimum severity (**Value**) to be considered before committing. **Note:** To ignore diagnostics from a particular source, add the source to the list and set the minimum severity to `none`.", - "config.untrackedChangesSoftDelete": "Controls whether discarding untracked changes moves the file(s) to the Recycle Bin (Windows), Trash (macOS, Linux) instead of deleting them. **Note:** This setting has no effect when connected to a remote.", + "config.discardUntrackedChangesToTrash": "Controls whether discarding untracked changes moves the file(s) to the Recycle Bin (Windows), Trash (macOS, Linux) instead of deleting them permanently. **Note:** This setting has no effect when connected to a remote or when running in Linux as a snap package.", + "config.showReferenceDetails": "Controls whether to show the details of the last commit for Git refs in the checkout, branch, and tag pickers.", "submenu.explorer": "Git", "submenu.commit": "Commit", "submenu.commit.amend": "Amend", diff --git a/extensions/git/src/api/git.d.ts b/extensions/git/src/api/git.d.ts index 5bbb4b03a9c6..9562106f9c6d 100644 --- a/extensions/git/src/api/git.d.ts +++ b/extensions/git/src/api/git.d.ts @@ -30,6 +30,7 @@ export interface Ref { readonly type: RefType; readonly name?: string; readonly commit?: string; + readonly commitDetails?: Commit; readonly remote?: string; } @@ -185,6 +186,7 @@ export interface RefQuery { readonly contains?: string; readonly count?: number; readonly pattern?: string | string[]; + readonly includeCommitDetails?: boolean; readonly sort?: 'alphabetically' | 'committerdate'; } diff --git a/extensions/git/src/blame.ts b/extensions/git/src/blame.ts index f29212c51a84..12182f1de8d8 100644 --- a/extensions/git/src/blame.ts +++ b/extensions/git/src/blame.ts @@ -69,6 +69,41 @@ function isResourceSchemeSupported(uri: Uri): boolean { return uri.scheme === 'file' || isGitUri(uri); } +function isResourceBlameInformationEqual(a: ResourceBlameInformation | undefined, b: ResourceBlameInformation | undefined): boolean { + if (a === b) { + return true; + } + + if (!a || !b || + a.resource.toString() !== b.resource.toString() || + a.blameInformation.length !== b.blameInformation.length) { + return false; + } + + for (let index = 0; index < a.blameInformation.length; index++) { + if (a.blameInformation[index].lineNumber !== b.blameInformation[index].lineNumber) { + return false; + } + + const aBlameInformation = a.blameInformation[index].blameInformation; + const bBlameInformation = b.blameInformation[index].blameInformation; + + if (typeof aBlameInformation === 'string' && typeof bBlameInformation === 'string') { + if (aBlameInformation !== bBlameInformation) { + return false; + } + } else if (typeof aBlameInformation !== 'string' && typeof bBlameInformation !== 'string') { + if (aBlameInformation.hash !== bBlameInformation.hash) { + return false; + } + } else { + return false; + } + } + + return true; +} + type BlameInformationTemplateTokens = { readonly hash: string; readonly hashShort: string; @@ -79,6 +114,11 @@ type BlameInformationTemplateTokens = { readonly authorDateAgo: string; }; +interface ResourceBlameInformation { + readonly resource: Uri; + readonly blameInformation: readonly LineBlameInformation[]; +} + interface LineBlameInformation { readonly lineNumber: number; readonly blameInformation: BlameInformation | string; @@ -116,11 +156,15 @@ export class GitBlameController { private readonly _onDidChangeBlameInformation = new EventEmitter(); public readonly onDidChangeBlameInformation = this._onDidChangeBlameInformation.event; - private _textEditorBlameInformation: LineBlameInformation[] | undefined; - get textEditorBlameInformation(): readonly LineBlameInformation[] | undefined { + private _textEditorBlameInformation: ResourceBlameInformation | undefined; + get textEditorBlameInformation(): ResourceBlameInformation | undefined { return this._textEditorBlameInformation; } - private set textEditorBlameInformation(blameInformation: LineBlameInformation[] | undefined) { + private set textEditorBlameInformation(blameInformation: ResourceBlameInformation | undefined) { + if (isResourceBlameInformationEqual(this._textEditorBlameInformation, blameInformation)) { + return; + } + this._textEditorBlameInformation = blameInformation; this._onDidChangeBlameInformation.fire(); } @@ -318,7 +362,7 @@ export class GitBlameController { } window.onDidChangeActiveTextEditor(e => this._updateTextEditorBlameInformation(e), this, this._enablementDisposables); - window.onDidChangeTextEditorSelection(e => this._updateTextEditorBlameInformation(e.textEditor, true), this, this._enablementDisposables); + window.onDidChangeTextEditorSelection(e => this._updateTextEditorBlameInformation(e.textEditor, 'selection'), this, this._enablementDisposables); window.onDidChangeTextEditorDiffInformation(e => this._updateTextEditorBlameInformation(e.textEditor), this, this._enablementDisposables); } } else { @@ -377,7 +421,7 @@ export class GitBlameController { } @throttle - private async _updateTextEditorBlameInformation(textEditor: TextEditor | undefined, showBlameInformationForPositionZero = false): Promise { + private async _updateTextEditorBlameInformation(textEditor: TextEditor | undefined, reason?: 'selection'): Promise { if (textEditor) { if (!textEditor.diffInformation || textEditor !== window.activeTextEditor) { return; @@ -401,7 +445,7 @@ export class GitBlameController { // Do not show blame information when there is a single selection and it is at the beginning // of the file [0, 0, 0, 0] unless the user explicitly navigates the cursor there. We do this // to avoid showing blame information when the editor is not focused. - if (!showBlameInformationForPositionZero && textEditor.selections.length === 1 && + if (reason !== 'selection' && textEditor.selections.length === 1 && textEditor.selections[0].start.line === 0 && textEditor.selections[0].start.character === 0 && textEditor.selections[0].end.line === 0 && textEditor.selections[0].end.character === 0) { this.textEditorBlameInformation = undefined; @@ -426,8 +470,11 @@ export class GitBlameController { // Resource on the right-hand side of the diff editor when viewing a resource from the index. const diffInformationWorkingTreeAndIndex = getWorkingTreeAndIndexDiffInformation(textEditor); - // Working tree + index diff information is present and it is stale + // Working tree + index diff information is present and it is stale. Diff information + // may be stale when the selection changes because of a content change and the diff + // information is not yet updated. if (diffInformationWorkingTreeAndIndex && diffInformationWorkingTreeAndIndex.isStale) { + this.textEditorBlameInformation = undefined; return; } @@ -440,16 +487,22 @@ export class GitBlameController { // Working tree diff information. Diff Editor (Working Tree) -> Text Editor const diffInformationWorkingTree = getWorkingTreeDiffInformation(textEditor); - // Working tree diff information is not present or it is stale + // Working tree diff information is not present or it is stale. Diff information + // may be stale when the selection changes because of a content change and the diff + // information is not yet updated. if (!diffInformationWorkingTree || diffInformationWorkingTree.isStale) { + this.textEditorBlameInformation = undefined; return; } // Working tree + index diff information const diffInformationWorkingTreeAndIndex = getWorkingTreeAndIndexDiffInformation(textEditor); - // Working tree + index diff information is present and it is stale + // Working tree + index diff information is present and it is stale. Diff information + // may be stale when the selection changes because of a content change and the diff + // information is not yet updated. if (diffInformationWorkingTreeAndIndex && diffInformationWorkingTreeAndIndex.isStale) { + this.textEditorBlameInformation = undefined; return; } @@ -482,7 +535,10 @@ export class GitBlameController { for (const lineNumber of new Set(textEditor.selections.map(s => s.active.line))) { // Check if the line is contained in the working tree diff information if (lineRangesContainLine(workingTreeChanges, lineNumber + 1)) { - lineBlameInformation.push({ lineNumber, blameInformation: l10n.t('Not Committed Yet') }); + if (reason === 'selection') { + // Only show the `Not Committed Yet` message upon selection change due to navigation + lineBlameInformation.push({ lineNumber, blameInformation: l10n.t('Not Committed Yet') }); + } continue; } @@ -505,7 +561,10 @@ export class GitBlameController { } } - this.textEditorBlameInformation = lineBlameInformation; + this.textEditorBlameInformation = { + resource: textEditor.document.uri, + blameInformation: lineBlameInformation + }; } dispose() { @@ -555,7 +614,7 @@ class GitBlameEditorDecoration implements HoverProvider { } // Get blame information - const blameInformation = this._controller.textEditorBlameInformation; + const blameInformation = this._controller.textEditorBlameInformation?.blameInformation; const lineBlameInformation = blameInformation?.find(blame => blame.lineNumber === position.line); if (!lineBlameInformation || typeof lineBlameInformation.blameInformation === 'string') { @@ -601,8 +660,8 @@ class GitBlameEditorDecoration implements HoverProvider { } // Get blame information - const blameInformation = this._controller.textEditorBlameInformation; - if (!blameInformation) { + const blameInformation = this._controller.textEditorBlameInformation?.blameInformation; + if (!blameInformation || blameInformation.length === 0) { textEditor.setDecorations(this._decoration, []); return; } @@ -680,7 +739,7 @@ class GitBlameStatusBarItem { return; } - const blameInformation = this._controller.textEditorBlameInformation; + const blameInformation = this._controller.textEditorBlameInformation?.blameInformation; if (!blameInformation || blameInformation.length === 0) { this._statusBarItem.hide(); return; diff --git a/extensions/git/src/commands.ts b/extensions/git/src/commands.ts index 94bcb143c58d..1094a438b07f 100644 --- a/extensions/git/src/commands.ts +++ b/extensions/git/src/commands.ts @@ -5,16 +5,16 @@ import * as os from 'os'; import * as path from 'path'; -import { Command, commands, Disposable, LineChange, MessageOptions, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder, TimelineItem, env, Selection, TextDocumentContentProvider, InputBoxValidationSeverity, TabInputText, TabInputTextMerge, QuickPickItemKind, TextDocument, LogOutputChannel, l10n, Memento, UIKind, QuickInputButton, ThemeIcon, SourceControlHistoryItem, SourceControl, InputBoxValidationMessage, Tab, TabInputNotebook, QuickInputButtonLocation, languages } from 'vscode'; +import { Command, commands, Disposable, MessageOptions, Position, ProgressLocation, QuickPickItem, Range, SourceControlResourceState, TextDocumentShowOptions, TextEditor, Uri, ViewColumn, window, workspace, WorkspaceEdit, WorkspaceFolder, TimelineItem, env, Selection, TextDocumentContentProvider, InputBoxValidationSeverity, TabInputText, TabInputTextMerge, QuickPickItemKind, TextDocument, LogOutputChannel, l10n, Memento, UIKind, QuickInputButton, ThemeIcon, SourceControlHistoryItem, SourceControl, InputBoxValidationMessage, Tab, TabInputNotebook, QuickInputButtonLocation, languages } from 'vscode'; import TelemetryReporter from '@vscode/extension-telemetry'; import { uniqueNamesGenerator, adjectives, animals, colors, NumberDictionary } from '@joaomoreno/unique-names-generator'; import { ForcePushMode, GitErrorCodes, Ref, RefType, Status, CommitOptions, RemoteSourcePublisher, Remote } from './api/git'; import { Git, Stash } from './git'; import { Model } from './model'; import { GitResourceGroup, Repository, Resource, ResourceGroupType } from './repository'; -import { DiffEditorSelectionHunkToolbarContext, applyLineChanges, getIndexDiffInformation, getModifiedRange, getWorkingTreeDiffInformation, intersectDiffWithRange, invertLineChange, toLineChanges, toLineRanges } from './staging'; +import { DiffEditorSelectionHunkToolbarContext, LineChange, applyLineChanges, getIndexDiffInformation, getModifiedRange, getWorkingTreeDiffInformation, intersectDiffWithRange, invertLineChange, toLineChanges, toLineRanges } from './staging'; import { fromGitUri, toGitUri, isGitUri, toMergeUris, toMultiFileDiffEditorUris } from './uri'; -import { DiagnosticSeverityConfig, dispose, getCommitShortHash, grep, isDefined, isDescendant, isRemote, isWindows, pathEquals, relativePath, toDiagnosticSeverity, truncate } from './util'; +import { DiagnosticSeverityConfig, dispose, fromNow, getCommitShortHash, grep, isDefined, isDescendant, isLinuxSnap, isRemote, isWindows, pathEquals, relativePath, toDiagnosticSeverity, truncate } from './util'; import { GitTimelineItem } from './timelineProvider'; import { ApiRepository } from './api/api1'; import { getRemoteSourceActions, pickRemoteSource } from './remoteSource'; @@ -73,6 +73,10 @@ class RefItem implements QuickPickItem { } get description(): string { + if (this.ref.commitDetails?.authorDate) { + return fromNow(this.ref.commitDetails.authorDate, true, true); + } + switch (this.ref.type) { case RefType.Head: return this.shortCommit; @@ -85,6 +89,14 @@ class RefItem implements QuickPickItem { } } + get detail(): string | undefined { + if (this.ref.commitDetails?.authorName && this.ref.commitDetails?.message) { + return `${this.ref.commitDetails?.authorName} | ${this.ref.commitDetails?.message}`; + } + + return undefined; + } + get refName(): string | undefined { return this.ref.name; } get refRemote(): string | undefined { return this.ref.remote; } get shortCommit(): string { return (this.ref.commit || '').substr(0, 8); } @@ -275,7 +287,6 @@ class StashItem implements QuickPickItem { interface ScmCommandOptions { repository?: boolean; - diff?: boolean; } interface ScmCommand { @@ -327,6 +338,8 @@ async function categorizeResourceByResolution(resources: Resource[]): Promise<{ async function createCheckoutItems(repository: Repository, detached = false): Promise { const config = workspace.getConfiguration('git'); const checkoutTypeConfig = config.get('checkoutType'); + const showRefDetails = config.get('showReferenceDetails') === true; + let checkoutTypes: string[]; if (checkoutTypeConfig === 'all' || !checkoutTypeConfig || checkoutTypeConfig.length === 0) { @@ -342,7 +355,7 @@ async function createCheckoutItems(repository: Repository, detached = false): Pr checkoutTypes = checkoutTypes.filter(t => t !== 'tags'); } - const refs = await repository.getRefs(); + const refs = await repository.getRefs({ includeCommitDetails: showRefDetails }); const refProcessors = checkoutTypes.map(type => getCheckoutRefProcessor(repository, type)) .filter(p => !!p) as RefProcessor[]; @@ -712,12 +725,7 @@ export class CommandCenter { ) { this.disposables = Commands.map(({ commandId, key, method, options }) => { const command = this.createCommand(commandId, key, method, options); - - if (options.diff) { - return commands.registerDiffInformationCommand(commandId, command); - } else { - return commands.registerCommand(commandId, command); - } + return commands.registerCommand(commandId, command); }); this.disposables.push(workspace.registerTextDocumentContentProvider('git-output', this.commandErrors)); @@ -1643,8 +1651,8 @@ export class CommandCenter { await this.runByRepository(modifiedUri, async (repository, resource) => await repository.stage(resource, result)); } - @command('git.stageSelectedRanges', { diff: true }) - async stageSelectedChanges(changes: LineChange[]): Promise { + @command('git.stageSelectedRanges') + async stageSelectedChanges(): Promise { const textEditor = window.activeTextEditor; if (!textEditor) { @@ -1658,7 +1666,6 @@ export class CommandCenter { const workingTreeLineChanges = toLineChanges(workingTreeDiffInformation); - this.logger.trace(`[CommandCenter][stageSelectedChanges] changes: ${JSON.stringify(changes)}`); this.logger.trace(`[CommandCenter][stageSelectedChanges] diffInformation: ${JSON.stringify(workingTreeDiffInformation)}`); this.logger.trace(`[CommandCenter][stageSelectedChanges] diffInformation changes: ${JSON.stringify(workingTreeLineChanges)}`); @@ -1838,8 +1845,8 @@ export class CommandCenter { textEditor.selections = [new Selection(firstStagedLine, 0, firstStagedLine, 0)]; } - @command('git.revertSelectedRanges', { diff: true }) - async revertSelectedRanges(changes: LineChange[]): Promise { + @command('git.revertSelectedRanges') + async revertSelectedRanges(): Promise { const textEditor = window.activeTextEditor; if (!textEditor) { @@ -1853,7 +1860,6 @@ export class CommandCenter { const workingTreeLineChanges = toLineChanges(workingTreeDiffInformation); - this.logger.trace(`[CommandCenter][revertSelectedRanges] changes: ${JSON.stringify(changes)}`); this.logger.trace(`[CommandCenter][revertSelectedRanges] diffInformation: ${JSON.stringify(workingTreeDiffInformation)}`); this.logger.trace(`[CommandCenter][revertSelectedRanges] diffInformation changes: ${JSON.stringify(workingTreeLineChanges)}`); @@ -1928,8 +1934,8 @@ export class CommandCenter { await repository.revert([]); } - @command('git.unstageSelectedRanges', { diff: true }) - async unstageSelectedRanges(changes: LineChange[]): Promise { + @command('git.unstageSelectedRanges') + async unstageSelectedRanges(): Promise { const textEditor = window.activeTextEditor; if (!textEditor) { @@ -1967,7 +1973,6 @@ export class CommandCenter { const indexLineChanges = toLineChanges(indexDiffInformation); - this.logger.trace(`[CommandCenter][unstageSelectedRanges] changes: ${JSON.stringify(changes)}`); this.logger.trace(`[CommandCenter][unstageSelectedRanges] diffInformation: ${JSON.stringify(indexDiffInformation)}`); this.logger.trace(`[CommandCenter][unstageSelectedRanges] diffInformation changes: ${JSON.stringify(indexLineChanges)}`); @@ -2164,9 +2169,9 @@ export class CommandCenter { private getDiscardUntrackedChangesDialogDetails(resources: Resource[]): [string, string, string] { const config = workspace.getConfiguration('git'); - const untrackedChangesSoftDelete = config.get('untrackedChangesSoftDelete', true) && !isRemote; + const discardUntrackedChangesToTrash = config.get('discardUntrackedChangesToTrash', true) && !isRemote && !isLinuxSnap; - const messageWarning = !untrackedChangesSoftDelete + const messageWarning = !discardUntrackedChangesToTrash ? resources.length === 1 ? '\n\nThis is IRREVERSIBLE!\nThis file will be FOREVER LOST if you proceed.' : '\n\nThis is IRREVERSIBLE!\nThese files will be FOREVER LOST if you proceed.' @@ -2176,7 +2181,7 @@ export class CommandCenter { ? l10n.t('Are you sure you want to DELETE the following untracked file: \'{0}\'?{1}', path.basename(resources[0].resourceUri.fsPath), messageWarning) : l10n.t('Are you sure you want to DELETE the {0} untracked files?{1}', resources.length, messageWarning); - const messageDetail = untrackedChangesSoftDelete + const messageDetail = discardUntrackedChangesToTrash ? isWindows ? resources.length === 1 ? 'You can restore this file from the Recycle Bin.' @@ -2186,7 +2191,7 @@ export class CommandCenter { : 'You can restore these files from the Trash.' : ''; - const primaryAction = untrackedChangesSoftDelete + const primaryAction = discardUntrackedChangesToTrash ? isWindows ? l10n.t('Move to Recycle Bin') : l10n.t('Move to Trash') @@ -2705,6 +2710,7 @@ export class CommandCenter { const quickPick = window.createQuickPick(); quickPick.busy = true; quickPick.sortByLabel = false; + quickPick.matchOnDetail = true; quickPick.placeholder = opts?.detached ? l10n.t('Select a branch to checkout in detached mode') : l10n.t('Select a branch or tag to checkout'); @@ -2925,9 +2931,12 @@ export class CommandCenter { private async _branch(repository: Repository, defaultName?: string, from = false, target?: string): Promise { target = target ?? 'HEAD'; + const config = workspace.getConfiguration('git'); + const showRefDetails = config.get('showReferenceDetails') === true; + if (from) { const getRefPicks = async () => { - const refs = await repository.getRefs(); + const refs = await repository.getRefs({ includeCommitDetails: showRefDetails }); const refProcessors = new RefItemsProcessor([ new RefProcessor(RefType.Head), new RefProcessor(RefType.RemoteHead), @@ -3030,6 +3039,9 @@ export class CommandCenter { private async _deleteBranch(repository: Repository, remote: string | undefined, name: string | undefined, options: { remote: boolean; force?: boolean }): Promise { let run: (force?: boolean) => Promise; + const config = workspace.getConfiguration('git'); + const showRefDetails = config.get('showReferenceDetails') === true; + if (!options.remote && typeof name === 'string') { // Local branch run = force => repository.deleteBranch(name!, force); @@ -3039,7 +3051,7 @@ export class CommandCenter { } else { const getBranchPicks = async () => { const pattern = options.remote ? 'refs/remotes' : 'refs/heads'; - const refs = await repository.getRefs({ pattern }); + const refs = await repository.getRefs({ pattern, includeCommitDetails: showRefDetails }); const refsToExclude: string[] = []; if (options.remote) { @@ -3117,8 +3129,11 @@ export class CommandCenter { @command('git.merge', { repository: true }) async merge(repository: Repository): Promise { + const config = workspace.getConfiguration('git'); + const showRefDetails = config.get('showReferenceDetails') === true; + const getQuickPickItems = async (): Promise => { - const refs = await repository.getRefs(); + const refs = await repository.getRefs({ includeCommitDetails: showRefDetails }); const itemsProcessor = new RefItemsProcessor([ new RefProcessor(RefType.Head, MergeItem), new RefProcessor(RefType.RemoteHead, MergeItem), @@ -3143,8 +3158,11 @@ export class CommandCenter { @command('git.rebase', { repository: true }) async rebase(repository: Repository): Promise { + const config = workspace.getConfiguration('git'); + const showRefDetails = config.get('showReferenceDetails') === true; + const getQuickPickItems = async (): Promise => { - const refs = await repository.getRefs(); + const refs = await repository.getRefs({ includeCommitDetails: showRefDetails }); const itemsProcessor = new RebaseItemsProcessors(repository); return itemsProcessor.processRefs(refs); @@ -3182,8 +3200,11 @@ export class CommandCenter { @command('git.deleteTag', { repository: true }) async deleteTag(repository: Repository): Promise { + const config = workspace.getConfiguration('git'); + const showRefDetails = config.get('showReferenceDetails') === true; + const tagPicks = async (): Promise => { - const remoteTags = await repository.getRefs({ pattern: 'refs/tags' }); + const remoteTags = await repository.getRefs({ pattern: 'refs/tags', includeCommitDetails: showRefDetails }); return remoteTags.length === 0 ? [{ label: l10n.t('$(info) This repository has no tags.') }] : remoteTags.map(ref => new TagDeleteItem(ref)); }; diff --git a/extensions/git/src/decorators.ts b/extensions/git/src/decorators.ts index b1a25d4fd919..f89ff2327e95 100644 --- a/extensions/git/src/decorators.ts +++ b/extensions/git/src/decorators.ts @@ -98,4 +98,4 @@ export function debounce(delay: number): Function { this[timerKey] = setTimeout(() => fn.apply(this, args), delay); }; }); -} \ No newline at end of file +} diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 2f0d11bd4884..4f64607aa6a1 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -12,10 +12,10 @@ import which from 'which'; import { EventEmitter } from 'events'; import * as iconv from '@vscode/iconv-lite-umd'; import * as filetype from 'file-type'; -import { assign, groupBy, IDisposable, toDisposable, dispose, mkdirp, readBytes, detectUnicodeEncoding, Encoding, onceEvent, splitInChunks, Limiter, Versions, isWindows, pathEquals, isMacintosh, isDescendant } from './util'; +import { assign, groupBy, IDisposable, toDisposable, dispose, mkdirp, readBytes, detectUnicodeEncoding, Encoding, onceEvent, splitInChunks, Limiter, Versions, isWindows, pathEquals, isMacintosh, isDescendant, relativePath } from './util'; import { CancellationError, CancellationToken, ConfigurationChangeEvent, LogOutputChannel, Progress, Uri, workspace } from 'vscode'; import { detectEncoding } from './encoding'; -import { Ref, RefType, Branch, Remote, ForcePushMode, GitErrorCodes, LogOptions, Change, Status, CommitOptions, RefQuery, InitOptions } from './api/git'; +import { Commit as ApiCommit, Ref, RefType, Branch, Remote, ForcePushMode, GitErrorCodes, LogOptions, Change, Status, CommitOptions, RefQuery, InitOptions } from './api/git'; import * as byline from 'byline'; import { StringDecoder } from 'string_decoder'; @@ -355,6 +355,10 @@ function sanitizePath(path: string): string { return path.replace(/^([a-z]):\\/i, (_, letter) => `${letter.toUpperCase()}:\\`); } +function sanitizeRelativePath(from: string, to: string): string { + return path.isAbsolute(to) ? relativePath(from, to).replace(/\\/g, '/') : to; +} + const COMMIT_FORMAT = '%H%n%aN%n%aE%n%at%n%ct%n%P%n%D%n%B'; const STASH_FORMAT = '%H%n%P%n%gd%n%gs'; @@ -1126,6 +1130,70 @@ function parseGitBlame(data: string): BlameInformation[] { return Array.from(blameInformation.values()); } +const REFS_FORMAT = '%(refname)%00%(objectname)%00%(*objectname)'; +const REFS_WITH_DETAILS_FORMAT = `${REFS_FORMAT}%00%(parent)%00%(*parent)%00%(authorname)%00%(*authorname)%00%(authordate:unix)%00%(*authordate:unix)%00%(subject)%00%(*subject)`; + +const headRegex = /^refs\/heads\/([^ ]+)$/; +const remoteHeadRegex = /^refs\/remotes\/([^/]+)\/([^ ]+)$/; +const tagRegex = /^refs\/tags\/([^ ]+)$/; + +function parseRefs(data: string, includeCommitDetails: boolean): Ref[] { + const refs: Ref[] = []; + const refRegex = !includeCommitDetails + ? /^(.*)\0([0-9a-f]{40})\0([0-9a-f]{40})?$/gm + : /^(.*)\0([0-9a-f]{40})\0([0-9a-f]{40})?\0(.*)\0(.*)\0(.*)\0(.*)\0(.*)\0(.*)\0(.*)\0(.*)$/gm; + + let ref: string | undefined; + let commitHash: string | undefined; + let tagCommitHash: string | undefined; + let commitParents: string | undefined; + let tagCommitParents: string | undefined; + let commitSubject: string | undefined; + let tagCommitSubject: string | undefined; + let authorName: string | undefined; + let tagAuthorName: string | undefined; + let authorDate: string | undefined; + let tagAuthorDate: string | undefined; + + let match: RegExpExecArray | null; + let refMatch: RegExpExecArray | null; + + do { + match = refRegex.exec(data); + if (match === null) { + break; + } + + let commitDetails: ApiCommit | undefined = undefined; + [, ref, commitHash, tagCommitHash, commitParents, tagCommitParents, authorName, tagAuthorName, authorDate, tagAuthorDate, commitSubject, tagCommitSubject] = match; + + if (includeCommitDetails) { + const parents = tagCommitParents || commitParents; + const subject = tagCommitSubject || commitSubject; + const author = tagAuthorName || authorName; + const date = tagAuthorDate || authorDate; + + commitDetails = { + hash: commitHash, + message: subject, + parents: parents ? parents.split(' ') : [], + authorName: author, + authorDate: date ? new Date(Number(date) * 1000) : undefined + } satisfies ApiCommit; + } + + if (refMatch = headRegex.exec(ref)) { + refs.push({ name: refMatch[1], commit: commitHash, commitDetails, type: RefType.Head }); + } else if (refMatch = remoteHeadRegex.exec(ref)) { + refs.push({ name: `${refMatch[1]}/${refMatch[2]}`, remote: refMatch[1], commit: commitHash, commitDetails, type: RefType.RemoteHead }); + } else if (refMatch = tagRegex.exec(ref)) { + refs.push({ name: refMatch[1], commit: tagCommitHash ?? commitHash, commitDetails, type: RefType.Tag }); + } + } while (true); + + return refs; +} + export interface PullOptions { readonly unshallow?: boolean; readonly tags?: boolean; @@ -1330,8 +1398,8 @@ export class Repository { .filter(entry => !!entry); } - async bufferString(object: string, encoding: string = 'utf8', autoGuessEncoding = false, candidateGuessEncodings: string[] = []): Promise { - const stdout = await this.buffer(object); + async bufferString(ref: string, filePath: string, encoding: string = 'utf8', autoGuessEncoding = false, candidateGuessEncodings: string[] = []): Promise { + const stdout = await this.buffer(ref, filePath); if (autoGuessEncoding) { encoding = detectEncoding(stdout, candidateGuessEncodings) || encoding; @@ -1342,8 +1410,9 @@ export class Repository { return iconv.decode(stdout, encoding); } - async buffer(object: string): Promise { - const child = this.stream(['show', '--textconv', object]); + async buffer(ref: string, filePath: string): Promise { + const relativePath = sanitizeRelativePath(this.repositoryRoot, filePath); + const child = this.stream(['show', '--textconv', `${ref}:${relativePath}`]); if (!child.stdout) { return Promise.reject('Can\'t open file from git'); @@ -1395,10 +1464,17 @@ export class Repository { return { mode, object, size: parseInt(size) || 0 }; } - async lstree(treeish: string, path?: string): Promise { - const args = ['ls-tree', '-l', treeish]; + async lstree(treeish: string, path?: string, options?: { recursive?: boolean }): Promise { + const args = ['ls-tree', '-l']; + + if (options?.recursive) { + args.push('-r'); + } + + args.push(treeish); + if (path) { - args.push('--', sanitizePath(path)); + args.push('--', sanitizeRelativePath(this.repositoryRoot, path)); } const { stdout } = await this.exec(args); @@ -1406,15 +1482,24 @@ export class Repository { } async lsfiles(path: string): Promise { - const { stdout } = await this.exec(['ls-files', '--stage', '--', sanitizePath(path)]); + const args = ['ls-files', '--stage']; + const relativePath = sanitizeRelativePath(this.repositoryRoot, path); + + if (relativePath) { + args.push('--', relativePath); + } + + const { stdout } = await this.exec(args); return parseLsFiles(stdout); } - async getGitRelativePath(ref: string, relativePath: string): Promise { - const relativePathLowercase = relativePath.toLowerCase(); - const dirname = path.posix.dirname(relativePath) + '/'; - const elements: { file: string }[] = ref ? await this.lstree(ref, dirname) : await this.lsfiles(dirname); - const element = elements.filter(file => file.file.toLowerCase() === relativePathLowercase)[0]; + async getGitFilePath(ref: string, filePath: string): Promise { + const elements: { file: string }[] = ref + ? await this.lstree(ref, undefined, { recursive: true }) + : await this.lsfiles(this.repositoryRoot); + + const relativePathLowercase = sanitizeRelativePath(this.repositoryRoot, filePath).toLowerCase(); + const element = elements.find(file => file.file.toLowerCase() === relativePathLowercase); if (!element) { throw new GitError({ @@ -1422,7 +1507,7 @@ export class Repository { }); } - return element.file; + return path.join(this.repositoryRoot, element.file); } async detectObjectType(object: string): Promise<{ mimetype: string; encoding?: string }> { @@ -1502,7 +1587,7 @@ export class Repository { return await this.diffFiles(false); } - const args = ['diff', '--', sanitizePath(path)]; + const args = ['diff', '--', sanitizeRelativePath(this.repositoryRoot, path)]; const result = await this.exec(args); return result.stdout; } @@ -1515,7 +1600,7 @@ export class Repository { return await this.diffFiles(false, ref); } - const args = ['diff', ref, '--', sanitizePath(path)]; + const args = ['diff', ref, '--', sanitizeRelativePath(this.repositoryRoot, path)]; const result = await this.exec(args); return result.stdout; } @@ -1528,7 +1613,7 @@ export class Repository { return await this.diffFiles(true); } - const args = ['diff', '--cached', '--', sanitizePath(path)]; + const args = ['diff', '--cached', '--', sanitizeRelativePath(this.repositoryRoot, path)]; const result = await this.exec(args); return result.stdout; } @@ -1541,7 +1626,7 @@ export class Repository { return await this.diffFiles(true, ref); } - const args = ['diff', '--cached', ref, '--', sanitizePath(path)]; + const args = ['diff', '--cached', ref, '--', sanitizeRelativePath(this.repositoryRoot, path)]; const result = await this.exec(args); return result.stdout; } @@ -1561,7 +1646,7 @@ export class Repository { return await this.diffFiles(false, range); } - const args = ['diff', range, '--', sanitizePath(path)]; + const args = ['diff', range, '--', sanitizeRelativePath(this.repositoryRoot, path)]; const result = await this.exec(args); return result.stdout.trim(); @@ -1653,7 +1738,7 @@ export class Repository { } if (paths && paths.length) { - for (const chunk of splitInChunks(paths.map(sanitizePath), MAX_CLI_LENGTH)) { + for (const chunk of splitInChunks(paths.map(p => sanitizeRelativePath(this.repositoryRoot, p)), MAX_CLI_LENGTH)) { await this.exec([...args, '--', ...chunk]); } } else { @@ -1668,13 +1753,14 @@ export class Repository { return; } - args.push(...paths.map(sanitizePath)); + args.push(...paths.map(p => sanitizeRelativePath(this.repositoryRoot, p))); await this.exec(args); } async stage(path: string, data: string, encoding: string): Promise { - const child = this.stream(['hash-object', '--stdin', '-w', '--path', sanitizePath(path)], { stdio: [null, null, null] }); + const relativePath = sanitizeRelativePath(this.repositoryRoot, path); + const child = this.stream(['hash-object', '--stdin', '-w', '--path', relativePath], { stdio: [null, null, null] }); child.stdin!.end(iconv.encode(data, encoding)); const { exitCode, stdout } = await exec(child); @@ -1703,7 +1789,7 @@ export class Repository { add = '--add'; } - await this.exec(['update-index', add, '--cacheinfo', mode, hash, path]); + await this.exec(['update-index', add, '--cacheinfo', mode, hash, relativePath]); } async checkout(treeish: string, paths: string[], opts: { track?: boolean; detached?: boolean } = Object.create(null)): Promise { @@ -1723,7 +1809,7 @@ export class Repository { try { if (paths && paths.length > 0) { - for (const chunk of splitInChunks(paths.map(sanitizePath), MAX_CLI_LENGTH)) { + for (const chunk of splitInChunks(paths.map(p => sanitizeRelativePath(this.repositoryRoot, p)), MAX_CLI_LENGTH)) { await this.exec([...args, '--', ...chunk]); } } else { @@ -1937,7 +2023,7 @@ export class Repository { const args = ['clean', '-f', '-q']; for (const paths of groups) { - for (const chunk of splitInChunks(paths.map(sanitizePath), MAX_CLI_LENGTH)) { + for (const chunk of splitInChunks(paths.map(p => sanitizeRelativePath(this.repositoryRoot, p)), MAX_CLI_LENGTH)) { promises.push(limiter.queue(() => this.exec([...args, '--', ...chunk]))); } } @@ -1977,7 +2063,7 @@ export class Repository { try { if (paths && paths.length > 0) { - for (const chunk of splitInChunks(paths.map(sanitizePath), MAX_CLI_LENGTH)) { + for (const chunk of splitInChunks(paths.map(p => sanitizeRelativePath(this.repositoryRoot, p)), MAX_CLI_LENGTH)) { await this.exec([...args, '--', ...chunk]); } } else { @@ -2222,7 +2308,7 @@ export class Repository { async blame(path: string): Promise { try { - const args = ['blame', sanitizePath(path)]; + const args = ['blame', '--', sanitizeRelativePath(this.repositoryRoot, path)]; const result = await this.exec(args); return result.stdout.trim(); } catch (err) { @@ -2242,7 +2328,7 @@ export class Repository { args.push(ref); } - args.push('--', sanitizePath(path)); + args.push('--', sanitizeRelativePath(this.repositoryRoot, path)); const result = await this.exec(args); @@ -2563,7 +2649,7 @@ export class Repository { args.push('--sort', `-${query.sort}`); } - args.push('--format', '%(refname) %(objectname) %(*objectname)'); + args.push('--format', query.includeCommitDetails ? REFS_WITH_DETAILS_FORMAT : REFS_FORMAT); if (query.pattern) { const patterns = Array.isArray(query.pattern) ? query.pattern : [query.pattern]; @@ -2577,25 +2663,7 @@ export class Repository { } const result = await this.exec(args, { cancellationToken }); - - const fn = (line: string): Ref | null => { - let match: RegExpExecArray | null; - - if (match = /^refs\/heads\/([^ ]+) ([0-9a-f]{40}) ([0-9a-f]{40})?$/.exec(line)) { - return { name: match[1], commit: match[2], type: RefType.Head }; - } else if (match = /^refs\/remotes\/([^/]+)\/([^ ]+) ([0-9a-f]{40}) ([0-9a-f]{40})?$/.exec(line)) { - return { name: `${match[1]}/${match[2]}`, commit: match[3], type: RefType.RemoteHead, remote: match[1] }; - } else if (match = /^refs\/tags\/([^ ]+) ([0-9a-f]{40}) ([0-9a-f]{40})?$/.exec(line)) { - return { name: match[1], commit: match[3] ?? match[2], type: RefType.Tag }; - } - - return null; - }; - - return result.stdout.split('\n') - .filter(line => !!line) - .map(fn) - .filter(ref => !!ref) as Ref[]; + return parseRefs(result.stdout, query.includeCommitDetails === true); } async getRemoteRefs(remote: string, opts?: { heads?: boolean; tags?: boolean; cancellationToken?: CancellationToken }): Promise { @@ -2853,7 +2921,7 @@ export class Repository { } async getCommit(ref: string): Promise { - const result = await this.exec(['show', '-s', '--decorate=full', '--shortstat', `--format=${COMMIT_FORMAT}`, '-z', ref]); + const result = await this.exec(['show', '-s', '--decorate=full', '--shortstat', `--format=${COMMIT_FORMAT}`, '-z', ref, '--']); const commits = parseGitCommits(result.stdout); if (commits.length === 0) { return Promise.reject('bad commit format'); @@ -2892,7 +2960,7 @@ export class Repository { async updateSubmodules(paths: string[]): Promise { const args = ['submodule', 'update']; - for (const chunk of splitInChunks(paths.map(sanitizePath), MAX_CLI_LENGTH)) { + for (const chunk of splitInChunks(paths.map(p => sanitizeRelativePath(this.repositoryRoot, p)), MAX_CLI_LENGTH)) { await this.exec([...args, '--', ...chunk]); } } diff --git a/extensions/git/src/gitEditor.ts b/extensions/git/src/gitEditor.ts index 6291e5152a72..38001136956e 100644 --- a/extensions/git/src/gitEditor.ts +++ b/extensions/git/src/gitEditor.ts @@ -67,7 +67,7 @@ export class GitEditor implements IIPCHandler, ITerminalEnvironmentProvider { } export class GitEditorDocumentLinkProvider implements DocumentLinkProvider { - private readonly _regex = /^#\s+(modified|new file|deleted|renamed|copied|type change):\s+(?.*?)(?:\s+->\s+(?.*))*$/gm; + constructor(private readonly _model: Model) { } diff --git a/extensions/git/src/historyProvider.ts b/extensions/git/src/historyProvider.ts index db316c81d0b3..8e5a8f9d2f14 100644 --- a/extensions/git/src/historyProvider.ts +++ b/extensions/git/src/historyProvider.ts @@ -163,7 +163,7 @@ export class GitHistoryProvider implements SourceControlHistoryProvider, FileDec // Compute base if the branch has changed const mergeBase = await this.resolveHEADMergeBase(); - this._currentHistoryItemBaseRef = mergeBase && + this._currentHistoryItemBaseRef = mergeBase && mergeBase.name && mergeBase.remote && (mergeBase.remote !== this.repository.HEAD.upstream?.remote || mergeBase.name !== this.repository.HEAD.upstream?.name) ? { id: `refs/remotes/${mergeBase.remote}/${mergeBase.name}`, diff --git a/extensions/git/src/repository.ts b/extensions/git/src/repository.ts index ca8974f2fc56..f32d915d7151 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -23,7 +23,7 @@ import { IPushErrorHandlerRegistry } from './pushError'; import { IRemoteSourcePublisherRegistry } from './remotePublisher'; import { StatusBarCommands } from './statusbar'; import { toGitUri } from './uri'; -import { anyEvent, combinedDisposable, debounceEvent, dispose, EmptyDisposable, eventToPromise, filterEvent, find, getCommitShortHash, IDisposable, isDescendant, isRemote, Limiter, onceEvent, pathEquals, relativePath } from './util'; +import { anyEvent, combinedDisposable, debounceEvent, dispose, EmptyDisposable, eventToPromise, filterEvent, find, getCommitShortHash, IDisposable, isDescendant, isLinuxSnap, isRemote, Limiter, onceEvent, pathEquals, relativePath } from './util'; import { IFileWatcher, watch } from './watch'; import { detectEncoding } from './encoding'; import { ISourceControlHistoryItemDetailsProviderRegistry } from './historyItemDetailsProvider'; @@ -1223,7 +1223,6 @@ export class Repository implements Disposable { } async stage(resource: Uri, contents: string): Promise { - const path = relativePath(this.repository.root, resource.fsPath).replace(/\\/g, '/'); await this.run(Operation.Stage, async () => { const configFiles = workspace.getConfiguration('files', Uri.file(resource.fsPath)); let encoding = configFiles.get('encoding') ?? 'utf8'; @@ -1235,7 +1234,7 @@ export class Repository implements Disposable { } encoding = iconv.encodingExists(encoding) ? encoding : 'utf8'; - await this.repository.stage(path, contents, encoding); + await this.repository.stage(resource.fsPath, contents, encoding); this._onDidChangeOriginalResource.fire(resource); this.closeDiffEditors([], [...resource.fsPath]); @@ -1361,7 +1360,7 @@ export class Repository implements Disposable { async clean(resources: Uri[]): Promise { const config = workspace.getConfiguration('git'); - const untrackedChangesSoftDelete = config.get('untrackedChangesSoftDelete', true) && !isRemote; + const discardUntrackedChangesToTrash = config.get('discardUntrackedChangesToTrash', true) && !isRemote && !isLinuxSnap; await this.run( Operation.Clean(!this.optimisticUpdateEnabled()), @@ -1400,7 +1399,7 @@ export class Repository implements Disposable { } }); - if (untrackedChangesSoftDelete) { + if (discardUntrackedChangesToTrash) { const limiter = new Limiter(5); await Promise.all(toClean.map(fsPath => limiter.queue( async () => await workspace.fs.delete(Uri.file(fsPath), { useTrash: true })))); @@ -1533,7 +1532,11 @@ export class Repository implements Disposable { try { const mergeBase = await this.getConfig(mergeBaseConfigKey); const branchFromConfig = mergeBase !== '' ? await this.getBranch(mergeBase) : undefined; - if (branchFromConfig) { + + // There was a brief period of time when we would consider local branches as a valid + // merge base. Since then we have fixed the issue and only remote branches can be used + // as a merge base so we are adding an additional check. + if (branchFromConfig && branchFromConfig.remote) { return branchFromConfig; } } catch (err) { } @@ -1852,18 +1855,12 @@ export class Repository implements Disposable { await this.run(Operation.Push, () => this._push(remote, undefined, false, false, forcePushMode, true)); } - async blame(filePath: string): Promise { - return await this.run(Operation.Blame(true), () => { - const path = relativePath(this.repository.root, filePath).replace(/\\/g, '/'); - return this.repository.blame(path); - }); + async blame(path: string): Promise { + return await this.run(Operation.Blame(true), () => this.repository.blame(path)); } - async blame2(filePath: string, ref?: string): Promise { - return await this.run(Operation.Blame(false), () => { - const path = relativePath(this.repository.root, filePath).replace(/\\/g, '/'); - return this.repository.blame2(path, ref); - }); + async blame2(path: string, ref?: string): Promise { + return await this.run(Operation.Blame(false), () => this.repository.blame2(path, ref)); } @throttle @@ -1979,18 +1976,17 @@ export class Repository implements Disposable { async show(ref: string, filePath: string): Promise { return await this.run(Operation.Show, async () => { - const path = relativePath(this.repository.root, filePath).replace(/\\/g, '/'); const configFiles = workspace.getConfiguration('files', Uri.file(filePath)); const defaultEncoding = configFiles.get('encoding'); const autoGuessEncoding = configFiles.get('autoGuessEncoding'); const candidateGuessEncodings = configFiles.get('candidateGuessEncodings'); try { - return await this.repository.bufferString(`${ref}:${path}`, defaultEncoding, autoGuessEncoding, candidateGuessEncodings); + return await this.repository.bufferString(ref, filePath, defaultEncoding, autoGuessEncoding, candidateGuessEncodings); } catch (err) { if (err.gitErrorCode === GitErrorCodes.WrongCase) { - const gitRelativePath = await this.repository.getGitRelativePath(ref, path); - return await this.repository.bufferString(`${ref}:${gitRelativePath}`, defaultEncoding, autoGuessEncoding, candidateGuessEncodings); + const gitFilePath = await this.repository.getGitFilePath(ref, filePath); + return await this.repository.bufferString(ref, gitFilePath, defaultEncoding, autoGuessEncoding, candidateGuessEncodings); } throw err; @@ -1999,21 +1995,15 @@ export class Repository implements Disposable { } async buffer(ref: string, filePath: string): Promise { - return this.run(Operation.Show, () => { - const path = relativePath(this.repository.root, filePath).replace(/\\/g, '/'); - return this.repository.buffer(`${ref}:${path}`); - }); + return this.run(Operation.Show, () => this.repository.buffer(ref, filePath)); } getObjectFiles(ref: string): Promise { return this.run(Operation.GetObjectFiles, () => this.repository.lstree(ref)); } - getObjectDetails(ref: string, filePath: string): Promise<{ mode: string; object: string; size: number }> { - return this.run(Operation.GetObjectDetails, () => { - const path = relativePath(this.repository.root, filePath).replace(/\\/g, '/'); - return this.repository.getObjectDetails(ref, path); - }); + getObjectDetails(ref: string, path: string): Promise<{ mode: string; object: string; size: number }> { + return this.run(Operation.GetObjectDetails, () => this.repository.getObjectDetails(ref, path)); } detectObjectType(object: string): Promise<{ mimetype: string; encoding?: string }> { diff --git a/extensions/git/src/staging.ts b/extensions/git/src/staging.ts index 14e4e379e478..ec7232bec44a 100644 --- a/extensions/git/src/staging.ts +++ b/extensions/git/src/staging.ts @@ -3,9 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { TextDocument, Range, LineChange, Selection, Uri, TextEditor, TextEditorDiffInformation } from 'vscode'; +import { TextDocument, Range, Selection, Uri, TextEditor, TextEditorDiffInformation } from 'vscode'; import { fromGitUri, isGitUri } from './uri'; +export interface LineChange { + readonly originalStartLineNumber: number; + readonly originalEndLineNumber: number; + readonly modifiedStartLineNumber: number; + readonly modifiedEndLineNumber: number; +} + export function applyLineChanges(original: TextDocument, modified: TextDocument, diffs: LineChange[]): string { const result: string[] = []; let currentLine = 0; diff --git a/extensions/git/src/test/smoke.test.ts b/extensions/git/src/test/smoke.test.ts index b9a3ddfd0638..c0e15b03ebeb 100644 --- a/extensions/git/src/test/smoke.test.ts +++ b/extensions/git/src/test/smoke.test.ts @@ -140,8 +140,8 @@ suite('git smoke test', function () { assert.strictEqual(repository.state.indexChanges.length, 0); - await repository.commit('third commit', { all: true }); - + await commands.executeCommand('git.stageAll'); + await repository.commit('third commit'); assert.strictEqual(repository.state.workingTreeChanges.length, 0); assert.strictEqual(repository.state.indexChanges.length, 0); }); diff --git a/extensions/git/src/util.ts b/extensions/git/src/util.ts index 9565b7e5171d..7dd1cbafbdf0 100644 --- a/extensions/git/src/util.ts +++ b/extensions/git/src/util.ts @@ -12,6 +12,8 @@ import byline from 'byline'; export const isMacintosh = process.platform === 'darwin'; export const isWindows = process.platform === 'win32'; export const isRemote = env.remoteName !== undefined; +export const isLinux = process.platform === 'linux'; +export const isLinuxSnap = isLinux && !!process.env['SNAP'] && !!process.env['SNAP_REVISION']; export function log(...args: any[]): void { console.log.apply(console, ['git:', ...args]); diff --git a/extensions/git/tsconfig.json b/extensions/git/tsconfig.json index 42218d8decb0..c79be520be97 100644 --- a/extensions/git/tsconfig.json +++ b/extensions/git/tsconfig.json @@ -11,7 +11,6 @@ "src/**/*", "../../src/vscode-dts/vscode.d.ts", "../../src/vscode-dts/vscode.proposed.canonicalUriProvider.d.ts", - "../../src/vscode-dts/vscode.proposed.diffCommand.d.ts", "../../src/vscode-dts/vscode.proposed.editSessionIdentityProvider.d.ts", "../../src/vscode-dts/vscode.proposed.quickDiffProvider.d.ts", "../../src/vscode-dts/vscode.proposed.quickInputButtonLocation.d.ts", diff --git a/extensions/github-authentication/package-lock.json b/extensions/github-authentication/package-lock.json index cbc9e16b75f3..d70713a24729 100644 --- a/extensions/github-authentication/package-lock.json +++ b/extensions/github-authentication/package-lock.json @@ -9,87 +9,87 @@ "version": "0.0.2", "license": "MIT", "dependencies": { - "@vscode/extension-telemetry": "^0.9.8", - "node-fetch": "2.6.7", + "@vscode/extension-telemetry": "^1.4.0", + "node-fetch": "3.3.2", "vscode-tas-client": "^0.1.84" }, "devDependencies": { - "@types/mocha": "^9.1.1", - "@types/node": "20.x", - "@types/node-fetch": "^2.5.7" + "@types/mocha": "^10.0.10", + "@types/node": "25.x", + "@types/node-fetch": "^2.6.13" }, "engines": { "vscode": "^1.41.0" } }, "node_modules/@microsoft/1ds-core-js": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.4.tgz", - "integrity": "sha512-3gbDUQgAO8EoyQTNcAEkxpuPnioC0May13P1l1l0NKZ128L9Ts/sj8QsfwCRTjHz0HThlA+4FptcAJXNYUy3rg==", + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.10.tgz", + "integrity": "sha512-5fSZmkGwWkH+mrIA5M1GYPZdPM+SjXwCCl2Am7VhFoVwOBJNhRnwvIpAdzw6sFjiebN/rz+/YH0NdxztGZSa9Q==", "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" } }, "node_modules/@microsoft/1ds-post-js": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.4.tgz", - "integrity": "sha512-nlKjWricDj0Tn68Dt0P8lX9a+X7LYrqJ6/iSfQwMfDhRIGLqW+wxx8gxS+iGWC/oc8zMQAeiZaemUpCwQcwpRQ==", + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.10.tgz", + "integrity": "sha512-VSLjc9cT+Y+eTiSfYltJHJCejn8oYr0E6Pq2BMhOEO7F6IyLGYIxzKKvo78ze9x+iHX7KPTATcZ+PFgjGXuNqg==", "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "4.3.4", + "@microsoft/1ds-core-js": "4.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" } }, "node_modules/@microsoft/applicationinsights-channel-js": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.4.tgz", - "integrity": "sha512-Z4nrxYwGKP9iyrYtm7iPQXVOFy4FsEsX0nDKkAi96Qpgw+vEh6NH4ORxMMuES0EollBQ3faJyvYCwckuCVIj0g==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.10.tgz", + "integrity": "sha512-iolFLz1ocWAzIQqHIEjjov3gNTPkgFQ4ArHnBcJEYoffOGWlJt6copaevS5YPI5rHzmbySsengZ8cLJJBBrXzQ==", "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-common": "3.3.4", - "@microsoft/applicationinsights-core-js": "3.3.4", + "@microsoft/applicationinsights-common": "3.3.10", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-common": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.4.tgz", - "integrity": "sha512-4ms16MlIvcP4WiUPqopifNxcWCcrXQJ2ADAK/75uok2mNQe6ZNRsqb/P+pvhUxc8A5HRlvoXPP1ptDSN5Girgw==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.10.tgz", + "integrity": "sha512-RVIenPIvNgZCbjJdALvLM4rNHgAFuHI7faFzHCgnI6S2WCUNGHeXlQTs9EUUrL+n2TPp9/cd0KKMILU5VVyYiA==", "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-core-js": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.4.tgz", - "integrity": "sha512-MummANF0mgKIkdvVvfmHQTBliK114IZLRhTL0X0Ep+zjDwWMHqYZgew0nlFKAl6ggu42abPZFK5afpE7qjtYJA==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.10.tgz", + "integrity": "sha512-5yKeyassZTq2l+SAO4npu6LPnbS++UD+M+Ghjm9uRzoBwD8tumFx0/F8AkSVqbniSREd+ztH/2q2foewa2RZyg==", "license": "MIT", "dependencies": { "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { "tslib": ">= 1.0.0" @@ -105,18 +105,18 @@ } }, "node_modules/@microsoft/applicationinsights-web-basic": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.4.tgz", - "integrity": "sha512-OpEPXr8vU/t/M8T9jvWJzJx/pCyygIiR1nGM/2PTde0wn7anl71Gxl5fWol7K/WwFEORNjkL3CEyWOyDc+28AA==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.10.tgz", + "integrity": "sha512-AZib5DAT3NU0VT0nLWEwXrnoMDDgZ/5S4dso01CNU5ELNxLdg+1fvchstlVdMy4FrAnxzs8Wf/GIQNFYOVgpAw==", "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-channel-js": "3.3.4", - "@microsoft/applicationinsights-common": "3.3.4", - "@microsoft/applicationinsights-core-js": "3.3.4", + "@microsoft/applicationinsights-channel-js": "3.3.10", + "@microsoft/applicationinsights-common": "3.3.10", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { "tslib": ">= 1.0.0" @@ -141,45 +141,48 @@ } }, "node_modules/@nevware21/ts-utils": { - "version": "0.11.6", - "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.11.6.tgz", - "integrity": "sha512-OUUJTh3fnaUSzg9DEHgv3d7jC+DnPL65mIO7RaR+jWve7+MmcgIvF79gY97DPQ4frH+IpNR78YAYd/dW4gK3kg==", + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.12.5.tgz", + "integrity": "sha512-JPQZWPKQJjj7kAftdEZL0XDFfbMgXCGiUAZe0d7EhLC3QlXTlZdSckGqqRIQ2QNl0VTEZyZUvRBw6Ednw089Fw==", "license": "MIT" }, "node_modules/@types/mocha": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", - "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==", - "dev": true + "version": "10.0.10", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz", + "integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==", + "dev": true, + "license": "MIT" }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/@types/node-fetch": { - "version": "2.5.7", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.7.tgz", - "integrity": "sha512-o2WVNf5UhWRkxlf6eq+jMZDu7kjgpgJfl4xVNlvryc95O/6F2ld8ztKX+qu+Rjyet93WAWm5LjeX9H5FGkODvw==", + "version": "2.6.13", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.13.tgz", + "integrity": "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*", - "form-data": "^3.0.0" + "form-data": "^4.0.4" } }, "node_modules/@vscode/extension-telemetry": { - "version": "0.9.8", - "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.8.tgz", - "integrity": "sha512-7YcKoUvmHlIB8QYCE4FNzt3ErHi9gQPhdCM3ZWtpw1bxPT0I+lMdx52KHlzTNoJzQ2NvMX7HyzyDwBEiMgTrWQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-1.4.0.tgz", + "integrity": "sha512-mqypuqIDn+vQnaQ8tMdvCRn9BprZTH2j6nlFFv9hE6hB25n32F8rOZinos8OgblRi87X1qdBpjpb0Em1LGSG9w==", "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "^4.3.4", - "@microsoft/1ds-post-js": "^4.3.4", - "@microsoft/applicationinsights-web-basic": "^3.3.4" + "@microsoft/1ds-core-js": "^4.3.10", + "@microsoft/1ds-post-js": "^4.3.10", + "@microsoft/applicationinsights-web-basic": "^3.3.10" }, "engines": { "vscode": "^1.75.0" @@ -188,14 +191,30 @@ "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k= sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dev": true, + "license": "MIT", "dependencies": { "delayed-stream": "~1.0.0" }, @@ -203,67 +222,314 @@ "node": ">= 0.8" } }, + "node_modules/data-uri-to-buffer": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-4.0.1.tgz", + "integrity": "sha512-0R9ikRb668HB7QDxT1vkpuUBtqc53YyAwMwGeUFKRojY/NWKvdZ+9UYtRfGmhqNbRkTSVpMbmyhXipFFv2cb/A==", + "license": "MIT", + "engines": { + "node": ">= 12" + } + }, "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk= sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.4.0" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/fetch-blob": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/fetch-blob/-/fetch-blob-3.2.0.tgz", + "integrity": "sha512-7yAQpD2UMJzLi1Dqv7qFYnPbaPx7ZfFK6PiIxQ4PfkGPyNyl2Ugx+a/umUonmKqjhM4DnfbMvdX6otXq83soQQ==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "paypal", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "dependencies": { + "node-domexception": "^1.0.0", + "web-streams-polyfill": "^3.0.3" + }, + "engines": { + "node": "^12.20 || >= 14.13" + } + }, "node_modules/form-data": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz", - "integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", "dev": true, + "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" }, "engines": { "node": ">= 6" } }, + "node_modules/formdata-polyfill": { + "version": "4.0.10", + "resolved": "https://registry.npmjs.org/formdata-polyfill/-/formdata-polyfill-4.0.10.tgz", + "integrity": "sha512-buewHzMvYL29jdeQTVILecSaZKnt/RJWjoZCF5OW60Z67/GmSLBkOFM7qh1PI3zFNtJbaZL5eQu1vLfazOwj4g==", + "license": "MIT", + "dependencies": { + "fetch-blob": "^3.1.2" + }, + "engines": { + "node": ">=12.20.0" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/mime-db": { - "version": "1.44.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", - "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==", + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "2.1.27", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", - "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, + "license": "MIT", "dependencies": { - "mime-db": "1.44.0" + "mime-db": "1.52.0" }, "engines": { "node": ">= 0.6" } }, + "node_modules/node-domexception": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/node-domexception/-/node-domexception-1.0.0.tgz", + "integrity": "sha512-/jKZoMpw0F8GRwl4/eLROPA3cfcXtLApP0QzLmUT/HuPCZWyB7IY9ZrMeKw2O/nFIqPQB3PVM9aYm0F312AXDQ==", + "deprecated": "Use your platform's native DOMException instead", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/jimmywarting" + }, + { + "type": "github", + "url": "https://paypal.me/jimmywarting" + } + ], + "license": "MIT", + "engines": { + "node": ">=10.5.0" + } + }, "node_modules/node-fetch": { - "version": "2.6.7", - "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.7.tgz", - "integrity": "sha512-ZjMPFEfVx5j+y2yF35Kzx5sF7kDzxuDj6ziH4FFbOp87zKDZNx8yExJIb05OGF4Nlt9IHFIMBkRl41VdvcNdbQ==", + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-3.3.2.tgz", + "integrity": "sha512-dRB78srN/l6gqWulah9SrxeYnxeddIG30+GOqK/9OlLVyLg3HPnr6SqOWTWOXKRwC2eGYCkZ59NNuSgvSrpgOA==", + "license": "MIT", "dependencies": { - "whatwg-url": "^5.0.0" + "data-uri-to-buffer": "^4.0.0", + "fetch-blob": "^3.1.4", + "formdata-polyfill": "^4.0.10" }, "engines": { - "node": "4.x || >=6.0.0" - }, - "peerDependencies": { - "encoding": "^0.1.0" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, - "peerDependenciesMeta": { - "encoding": { - "optional": true - } + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/node-fetch" } }, "node_modules/tas-client": { @@ -271,16 +537,12 @@ "resolved": "https://registry.npmjs.org/tas-client/-/tas-client-0.2.33.tgz", "integrity": "sha512-V+uqV66BOQnWxvI6HjDnE4VkInmYZUQ4dgB7gzaDyFyFSK1i1nF/j7DpS9UbQAgV9NaF1XpcyuavnM1qOeiEIg==" }, - "node_modules/tr46": { - "version": "0.0.3", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-0.0.3.tgz", - "integrity": "sha1-gYT9NH2snNwYWZLzpmIuFLnZq2o= sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" - }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" }, "node_modules/vscode-tas-client": { "version": "0.1.84", @@ -293,18 +555,13 @@ "vscode": "^1.85.0" } }, - "node_modules/webidl-conversions": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", - "integrity": "sha1-JFNCdeKnvGvnvIZhHMFq4KVlSHE= sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==" - }, - "node_modules/whatwg-url": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-5.0.0.tgz", - "integrity": "sha1-lmRU6HZUYuN2RNNib2dCzotwll0= sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==", - "dependencies": { - "tr46": "~0.0.3", - "webidl-conversions": "^3.0.0" + "node_modules/web-streams-polyfill": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.3.3.tgz", + "integrity": "sha512-d2JWLCivmZYTSIoge9MsgFCZrt571BikcWGYkjC1khllbTeDlGqZ2D8vD8E/lJa8WGWbb7Plm8/XJYV7IJHZZw==", + "license": "MIT", + "engines": { + "node": ">= 8" } } } diff --git a/extensions/github-authentication/package.json b/extensions/github-authentication/package.json index 96fbcd75d400..0d5e22de242e 100644 --- a/extensions/github-authentication/package.json +++ b/extensions/github-authentication/package.json @@ -61,14 +61,14 @@ "vscode:prepublish": "npm run compile" }, "dependencies": { - "node-fetch": "2.6.7", - "@vscode/extension-telemetry": "^0.9.8", + "node-fetch": "3.3.2", + "@vscode/extension-telemetry": "^1.4.0", "vscode-tas-client": "^0.1.84" }, "devDependencies": { - "@types/mocha": "^9.1.1", - "@types/node": "20.x", - "@types/node-fetch": "^2.5.7" + "@types/mocha": "^10.0.10", + "@types/node": "25.x", + "@types/node-fetch": "^2.6.13" }, "repository": { "type": "git", diff --git a/extensions/github-authentication/src/config.ts b/extensions/github-authentication/src/config.ts index 30b9dd662653..8cd3a9ed7931 100644 --- a/extensions/github-authentication/src/config.ts +++ b/extensions/github-authentication/src/config.ts @@ -10,6 +10,10 @@ export interface IConfig { } // For easy access to mixin client ID and secret +// +// NOTE: GitHub client secrets cannot be secured when running in a native client so in other words, the client secret is +// not really a secret... so we allow the client secret in code. It is brought in before we publish VS Code. Reference: +// https://docs.github.com/en/apps/oauth-apps/building-oauth-apps/best-practices-for-creating-an-oauth-app#client-secrets export const Config: IConfig = { gitHubClientId: '01ab8ac9400c4e429b23' }; diff --git a/extensions/github/package-lock.json b/extensions/github/package-lock.json index cf7317a40a42..1ebbde734166 100644 --- a/extensions/github/package-lock.json +++ b/extensions/github/package-lock.json @@ -9,87 +9,87 @@ "version": "0.0.1", "license": "MIT", "dependencies": { - "@octokit/graphql": "8.2.0", - "@octokit/graphql-schema": "14.4.0", - "@octokit/rest": "21.1.0", - "@vscode/extension-telemetry": "^0.9.8", + "@octokit/graphql": "9.0.3", + "@octokit/graphql-schema": "15.26.1", + "@octokit/rest": "22.0.1", + "@vscode/extension-telemetry": "^1.4.0", "tunnel": "^0.0.6" }, "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "engines": { "vscode": "^1.41.0" } }, "node_modules/@microsoft/1ds-core-js": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.4.tgz", - "integrity": "sha512-3gbDUQgAO8EoyQTNcAEkxpuPnioC0May13P1l1l0NKZ128L9Ts/sj8QsfwCRTjHz0HThlA+4FptcAJXNYUy3rg==", + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.10.tgz", + "integrity": "sha512-5fSZmkGwWkH+mrIA5M1GYPZdPM+SjXwCCl2Am7VhFoVwOBJNhRnwvIpAdzw6sFjiebN/rz+/YH0NdxztGZSa9Q==", "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" } }, "node_modules/@microsoft/1ds-post-js": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.4.tgz", - "integrity": "sha512-nlKjWricDj0Tn68Dt0P8lX9a+X7LYrqJ6/iSfQwMfDhRIGLqW+wxx8gxS+iGWC/oc8zMQAeiZaemUpCwQcwpRQ==", + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.10.tgz", + "integrity": "sha512-VSLjc9cT+Y+eTiSfYltJHJCejn8oYr0E6Pq2BMhOEO7F6IyLGYIxzKKvo78ze9x+iHX7KPTATcZ+PFgjGXuNqg==", "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "4.3.4", + "@microsoft/1ds-core-js": "4.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" } }, "node_modules/@microsoft/applicationinsights-channel-js": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.4.tgz", - "integrity": "sha512-Z4nrxYwGKP9iyrYtm7iPQXVOFy4FsEsX0nDKkAi96Qpgw+vEh6NH4ORxMMuES0EollBQ3faJyvYCwckuCVIj0g==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.10.tgz", + "integrity": "sha512-iolFLz1ocWAzIQqHIEjjov3gNTPkgFQ4ArHnBcJEYoffOGWlJt6copaevS5YPI5rHzmbySsengZ8cLJJBBrXzQ==", "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-common": "3.3.4", - "@microsoft/applicationinsights-core-js": "3.3.4", + "@microsoft/applicationinsights-common": "3.3.10", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-common": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.4.tgz", - "integrity": "sha512-4ms16MlIvcP4WiUPqopifNxcWCcrXQJ2ADAK/75uok2mNQe6ZNRsqb/P+pvhUxc8A5HRlvoXPP1ptDSN5Girgw==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.10.tgz", + "integrity": "sha512-RVIenPIvNgZCbjJdALvLM4rNHgAFuHI7faFzHCgnI6S2WCUNGHeXlQTs9EUUrL+n2TPp9/cd0KKMILU5VVyYiA==", "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-core-js": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.4.tgz", - "integrity": "sha512-MummANF0mgKIkdvVvfmHQTBliK114IZLRhTL0X0Ep+zjDwWMHqYZgew0nlFKAl6ggu42abPZFK5afpE7qjtYJA==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.10.tgz", + "integrity": "sha512-5yKeyassZTq2l+SAO4npu6LPnbS++UD+M+Ghjm9uRzoBwD8tumFx0/F8AkSVqbniSREd+ztH/2q2foewa2RZyg==", "license": "MIT", "dependencies": { "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { "tslib": ">= 1.0.0" @@ -105,18 +105,18 @@ } }, "node_modules/@microsoft/applicationinsights-web-basic": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.4.tgz", - "integrity": "sha512-OpEPXr8vU/t/M8T9jvWJzJx/pCyygIiR1nGM/2PTde0wn7anl71Gxl5fWol7K/WwFEORNjkL3CEyWOyDc+28AA==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.10.tgz", + "integrity": "sha512-AZib5DAT3NU0VT0nLWEwXrnoMDDgZ/5S4dso01CNU5ELNxLdg+1fvchstlVdMy4FrAnxzs8Wf/GIQNFYOVgpAw==", "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-channel-js": "3.3.4", - "@microsoft/applicationinsights-common": "3.3.4", - "@microsoft/applicationinsights-core-js": "3.3.4", + "@microsoft/applicationinsights-channel-js": "3.3.10", + "@microsoft/applicationinsights-common": "3.3.10", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { "tslib": ">= 1.0.0" @@ -141,207 +141,209 @@ } }, "node_modules/@nevware21/ts-utils": { - "version": "0.11.6", - "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.11.6.tgz", - "integrity": "sha512-OUUJTh3fnaUSzg9DEHgv3d7jC+DnPL65mIO7RaR+jWve7+MmcgIvF79gY97DPQ4frH+IpNR78YAYd/dW4gK3kg==", + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.12.5.tgz", + "integrity": "sha512-JPQZWPKQJjj7kAftdEZL0XDFfbMgXCGiUAZe0d7EhLC3QlXTlZdSckGqqRIQ2QNl0VTEZyZUvRBw6Ednw089Fw==", "license": "MIT" }, "node_modules/@octokit/auth-token": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-5.1.2.tgz", - "integrity": "sha512-JcQDsBdg49Yky2w2ld20IHAlwr8d/d8N6NiOXbtuoPCqzbsiJgF633mVUw3x4mo0H5ypataQIX7SFu3yy44Mpw==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-6.0.0.tgz", + "integrity": "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==", "license": "MIT", "engines": { - "node": ">= 18" + "node": ">= 20" } }, "node_modules/@octokit/core": { - "version": "6.1.4", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-6.1.4.tgz", - "integrity": "sha512-lAS9k7d6I0MPN+gb9bKDt7X8SdxknYqAMh44S5L+lNqIN2NuV8nvv3g8rPp7MuRxcOpxpUIATWprO0C34a8Qmg==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-7.0.6.tgz", + "integrity": "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q==", "license": "MIT", "dependencies": { - "@octokit/auth-token": "^5.0.0", - "@octokit/graphql": "^8.1.2", - "@octokit/request": "^9.2.1", - "@octokit/request-error": "^6.1.7", - "@octokit/types": "^13.6.2", - "before-after-hook": "^3.0.2", + "@octokit/auth-token": "^6.0.0", + "@octokit/graphql": "9.0.3", + "@octokit/request": "^10.0.6", + "@octokit/request-error": "^7.0.2", + "@octokit/types": "^16.0.0", + "before-after-hook": "^4.0.0", "universal-user-agent": "^7.0.0" }, "engines": { - "node": ">= 18" + "node": ">= 20" } }, "node_modules/@octokit/endpoint": { - "version": "10.1.3", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-10.1.3.tgz", - "integrity": "sha512-nBRBMpKPhQUxCsQQeW+rCJ/OPSMcj3g0nfHn01zGYZXuNDvvXudF/TYY6APj5THlurerpFN4a/dQAIAaM6BYhA==", + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-11.0.2.tgz", + "integrity": "sha512-4zCpzP1fWc7QlqunZ5bSEjxc6yLAlRTnDwKtgXfcI/FxxGoqedDG8V2+xJ60bV2kODqcGB+nATdtap/XYq2NZQ==", "license": "MIT", "dependencies": { - "@octokit/types": "^13.6.2", + "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.2" }, "engines": { - "node": ">= 18" + "node": ">= 20" } }, "node_modules/@octokit/graphql": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-8.2.0.tgz", - "integrity": "sha512-gejfDywEml/45SqbWTWrhfwvLBrcGYhOn50sPOjIeVvH6i7D16/9xcFA8dAJNp2HMcd+g4vru41g4E2RBiZvfQ==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-9.0.3.tgz", + "integrity": "sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA==", "license": "MIT", "dependencies": { - "@octokit/request": "^9.1.4", - "@octokit/types": "^13.8.0", + "@octokit/request": "^10.0.6", + "@octokit/types": "^16.0.0", "universal-user-agent": "^7.0.0" }, "engines": { - "node": ">= 18" + "node": ">= 20" } }, "node_modules/@octokit/graphql-schema": { - "version": "14.4.0", - "resolved": "https://registry.npmjs.org/@octokit/graphql-schema/-/graphql-schema-14.4.0.tgz", - "integrity": "sha512-+O6/dsLlR6V9gv+t1lqsN+x73TLwyQWZpd3M8/eYnuny7VaznV9TAyUxf18tX8WBBS5IqtlLDk1nG+aSTPRZzQ==", + "version": "15.26.1", + "resolved": "https://registry.npmjs.org/@octokit/graphql-schema/-/graphql-schema-15.26.1.tgz", + "integrity": "sha512-RFDC2MpRBd4AxSRvUeBIVeBU7ojN/SxDfALUd7iVYOSeEK3gZaqR2MGOysj4Zh2xj2RY5fQAUT+Oqq7hWTraMA==", + "license": "MIT", "dependencies": { "graphql": "^16.0.0", "graphql-tag": "^2.10.3" } }, "node_modules/@octokit/openapi-types": { - "version": "23.0.1", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-23.0.1.tgz", - "integrity": "sha512-izFjMJ1sir0jn0ldEKhZ7xegCTj/ObmEDlEfpFrx4k/JyZSMRHbO3/rBwgE7f3m2DHt+RrNGIVw4wSmwnm3t/g==", + "version": "27.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-27.0.0.tgz", + "integrity": "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA==", "license": "MIT" }, "node_modules/@octokit/plugin-paginate-rest": { - "version": "11.4.2", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-11.4.2.tgz", - "integrity": "sha512-BXJ7XPCTDXFF+wxcg/zscfgw2O/iDPtNSkwwR1W1W5c4Mb3zav/M2XvxQ23nVmKj7jpweB4g8viMeCQdm7LMVA==", + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-14.0.0.tgz", + "integrity": "sha512-fNVRE7ufJiAA3XUrha2omTA39M6IXIc6GIZLvlbsm8QOQCYvpq/LkMNGyFlB1d8hTDzsAXa3OKtybdMAYsV/fw==", "license": "MIT", "dependencies": { - "@octokit/types": "^13.7.0" + "@octokit/types": "^16.0.0" }, "engines": { - "node": ">= 18" + "node": ">= 20" }, "peerDependencies": { "@octokit/core": ">=6" } }, "node_modules/@octokit/plugin-request-log": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-5.3.1.tgz", - "integrity": "sha512-n/lNeCtq+9ofhC15xzmJCNKP2BWTv8Ih2TTy+jatNCCq/gQP/V7rK3fjIfuz0pDWDALO/o/4QY4hyOF6TQQFUw==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-6.0.0.tgz", + "integrity": "sha512-UkOzeEN3W91/eBq9sPZNQ7sUBvYCqYbrrD8gTbBuGtHEuycE4/awMXcYvx6sVYo7LypPhmQwwpUe4Yyu4QZN5Q==", "license": "MIT", "engines": { - "node": ">= 18" + "node": ">= 20" }, "peerDependencies": { "@octokit/core": ">=6" } }, "node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "13.3.1", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-13.3.1.tgz", - "integrity": "sha512-o8uOBdsyR+WR8MK9Cco8dCgvG13H1RlM1nWnK/W7TEACQBFux/vPREgKucxUfuDQ5yi1T3hGf4C5ZmZXAERgwQ==", + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-17.0.0.tgz", + "integrity": "sha512-B5yCyIlOJFPqUUeiD0cnBJwWJO8lkJs5d8+ze9QDP6SvfiXSz1BF+91+0MeI1d2yxgOhU/O+CvtiZ9jSkHhFAw==", "license": "MIT", "dependencies": { - "@octokit/types": "^13.8.0" + "@octokit/types": "^16.0.0" }, "engines": { - "node": ">= 18" + "node": ">= 20" }, "peerDependencies": { "@octokit/core": ">=6" } }, "node_modules/@octokit/request": { - "version": "9.2.1", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-9.2.1.tgz", - "integrity": "sha512-TqHLIdw1KFvx8WvLc7Jv94r3C3+AzKY2FWq7c20zvrxmCIa6MCVkLCE/826NCXnml3LFJjLsidDh1BhMaGEDQw==", + "version": "10.0.7", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-10.0.7.tgz", + "integrity": "sha512-v93h0i1yu4idj8qFPZwjehoJx4j3Ntn+JhXsdJrG9pYaX6j/XRz2RmasMUHtNgQD39nrv/VwTWSqK0RNXR8upA==", "license": "MIT", "dependencies": { - "@octokit/endpoint": "^10.1.3", - "@octokit/request-error": "^6.1.6", - "@octokit/types": "^13.6.2", - "fast-content-type-parse": "^2.0.0", + "@octokit/endpoint": "^11.0.2", + "@octokit/request-error": "^7.0.2", + "@octokit/types": "^16.0.0", + "fast-content-type-parse": "^3.0.0", "universal-user-agent": "^7.0.2" }, "engines": { - "node": ">= 18" + "node": ">= 20" } }, "node_modules/@octokit/request-error": { - "version": "6.1.7", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-6.1.7.tgz", - "integrity": "sha512-69NIppAwaauwZv6aOzb+VVLwt+0havz9GT5YplkeJv7fG7a40qpLt/yZKyiDxAhgz0EtgNdNcb96Z0u+Zyuy2g==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-7.1.0.tgz", + "integrity": "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw==", "license": "MIT", "dependencies": { - "@octokit/types": "^13.6.2" + "@octokit/types": "^16.0.0" }, "engines": { - "node": ">= 18" + "node": ">= 20" } }, "node_modules/@octokit/rest": { - "version": "21.1.0", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-21.1.0.tgz", - "integrity": "sha512-93iLxcKDJboUpmnUyeJ6cRIi7z7cqTZT1K7kRK4LobGxwTwpsa+2tQQbRQNGy7IFDEAmrtkf4F4wBj3D5rVlJQ==", + "version": "22.0.1", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-22.0.1.tgz", + "integrity": "sha512-Jzbhzl3CEexhnivb1iQ0KJ7s5vvjMWcmRtq5aUsKmKDrRW6z3r84ngmiFKFvpZjpiU/9/S6ITPFRpn5s/3uQJw==", "license": "MIT", "dependencies": { - "@octokit/core": "^6.1.3", - "@octokit/plugin-paginate-rest": "^11.4.0", - "@octokit/plugin-request-log": "^5.3.1", - "@octokit/plugin-rest-endpoint-methods": "^13.3.0" + "@octokit/core": "^7.0.6", + "@octokit/plugin-paginate-rest": "^14.0.0", + "@octokit/plugin-request-log": "^6.0.0", + "@octokit/plugin-rest-endpoint-methods": "^17.0.0" }, "engines": { - "node": ">= 18" + "node": ">= 20" } }, "node_modules/@octokit/types": { - "version": "13.8.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-13.8.0.tgz", - "integrity": "sha512-x7DjTIbEpEWXK99DMd01QfWy0hd5h4EN+Q7shkdKds3otGQP+oWE/y0A76i1OvH9fygo4ddvNf7ZvF0t78P98A==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-16.0.0.tgz", + "integrity": "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg==", "license": "MIT", "dependencies": { - "@octokit/openapi-types": "^23.0.1" + "@octokit/openapi-types": "^27.0.0" } }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/@vscode/extension-telemetry": { - "version": "0.9.8", - "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.8.tgz", - "integrity": "sha512-7YcKoUvmHlIB8QYCE4FNzt3ErHi9gQPhdCM3ZWtpw1bxPT0I+lMdx52KHlzTNoJzQ2NvMX7HyzyDwBEiMgTrWQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-1.4.0.tgz", + "integrity": "sha512-mqypuqIDn+vQnaQ8tMdvCRn9BprZTH2j6nlFFv9hE6hB25n32F8rOZinos8OgblRi87X1qdBpjpb0Em1LGSG9w==", "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "^4.3.4", - "@microsoft/1ds-post-js": "^4.3.4", - "@microsoft/applicationinsights-web-basic": "^3.3.4" + "@microsoft/1ds-core-js": "^4.3.10", + "@microsoft/1ds-post-js": "^4.3.10", + "@microsoft/applicationinsights-web-basic": "^3.3.10" }, "engines": { "vscode": "^1.75.0" } }, "node_modules/before-after-hook": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-3.0.2.tgz", - "integrity": "sha512-Nik3Sc0ncrMK4UUdXQmAnRtzmNQTAAXmXIopizwZ1W1t8QmfJj+zL4OA2I7XPTPW5z5TDqv4hRo/JzouDJnX3A==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-4.0.0.tgz", + "integrity": "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==", "license": "Apache-2.0" }, "node_modules/fast-content-type-parse": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-2.0.1.tgz", - "integrity": "sha512-nGqtvLrj5w0naR6tDPfB4cUmYCqouzyQiz6C5y/LtcDllJdrcc6WaWW6iXyIIOErTa/XRybj28aasdn4LkVk6Q==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-3.0.0.tgz", + "integrity": "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==", "funding": [ { "type": "github", @@ -390,10 +392,11 @@ } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" }, "node_modules/universal-user-agent": { "version": "7.0.2", diff --git a/extensions/github/package.json b/extensions/github/package.json index 86adc2ddc4ed..1ab0f01da68d 100644 --- a/extensions/github/package.json +++ b/extensions/github/package.json @@ -227,14 +227,14 @@ "watch": "gulp watch-extension:github" }, "dependencies": { - "@octokit/graphql": "8.2.0", - "@octokit/graphql-schema": "14.4.0", - "@octokit/rest": "21.1.0", + "@octokit/graphql": "9.0.3", + "@octokit/graphql-schema": "15.26.1", + "@octokit/rest": "22.0.1", "tunnel": "^0.0.6", - "@vscode/extension-telemetry": "^0.9.8" + "@vscode/extension-telemetry": "^1.4.0" }, "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "repository": { "type": "git", diff --git a/extensions/grunt/package-lock.json b/extensions/grunt/package-lock.json index f0431ef8c852..9762d1fc3e98 100644 --- a/extensions/grunt/package-lock.json +++ b/extensions/grunt/package-lock.json @@ -9,26 +9,28 @@ "version": "1.0.0", "license": "MIT", "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "engines": { "vscode": "*" } }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" } } } diff --git a/extensions/grunt/package.json b/extensions/grunt/package.json index ae533cc0e47f..2c54ec24feb8 100644 --- a/extensions/grunt/package.json +++ b/extensions/grunt/package.json @@ -19,7 +19,7 @@ }, "dependencies": {}, "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "main": "./out/main", "activationEvents": [ diff --git a/extensions/gulp/package-lock.json b/extensions/gulp/package-lock.json index 9311609f962c..9703ac99541e 100644 --- a/extensions/gulp/package-lock.json +++ b/extensions/gulp/package-lock.json @@ -9,26 +9,28 @@ "version": "1.0.0", "license": "MIT", "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "engines": { "vscode": "*" } }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" } } } diff --git a/extensions/gulp/package.json b/extensions/gulp/package.json index 0c19b6884778..b1f5beff55fb 100644 --- a/extensions/gulp/package.json +++ b/extensions/gulp/package.json @@ -18,7 +18,7 @@ }, "dependencies": {}, "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "main": "./out/main", "activationEvents": [ diff --git a/extensions/html-language-features/package-lock.json b/extensions/html-language-features/package-lock.json index 20b145615338..1823392f858f 100644 --- a/extensions/html-language-features/package-lock.json +++ b/extensions/html-language-features/package-lock.json @@ -9,85 +9,106 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@vscode/extension-telemetry": "^0.9.8", - "vscode-languageclient": "^10.0.0-next.13", - "vscode-uri": "^3.0.8" + "@vscode/extension-telemetry": "^1.4.0", + "vscode-languageclient": "^10.0.0-next.19", + "vscode-uri": "^3.1.0" }, "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "engines": { "vscode": "^1.77.0" } }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, "node_modules/@microsoft/1ds-core-js": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.4.tgz", - "integrity": "sha512-3gbDUQgAO8EoyQTNcAEkxpuPnioC0May13P1l1l0NKZ128L9Ts/sj8QsfwCRTjHz0HThlA+4FptcAJXNYUy3rg==", + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.10.tgz", + "integrity": "sha512-5fSZmkGwWkH+mrIA5M1GYPZdPM+SjXwCCl2Am7VhFoVwOBJNhRnwvIpAdzw6sFjiebN/rz+/YH0NdxztGZSa9Q==", "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" } }, "node_modules/@microsoft/1ds-post-js": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.4.tgz", - "integrity": "sha512-nlKjWricDj0Tn68Dt0P8lX9a+X7LYrqJ6/iSfQwMfDhRIGLqW+wxx8gxS+iGWC/oc8zMQAeiZaemUpCwQcwpRQ==", + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.10.tgz", + "integrity": "sha512-VSLjc9cT+Y+eTiSfYltJHJCejn8oYr0E6Pq2BMhOEO7F6IyLGYIxzKKvo78ze9x+iHX7KPTATcZ+PFgjGXuNqg==", "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "4.3.4", + "@microsoft/1ds-core-js": "4.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" } }, "node_modules/@microsoft/applicationinsights-channel-js": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.4.tgz", - "integrity": "sha512-Z4nrxYwGKP9iyrYtm7iPQXVOFy4FsEsX0nDKkAi96Qpgw+vEh6NH4ORxMMuES0EollBQ3faJyvYCwckuCVIj0g==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.10.tgz", + "integrity": "sha512-iolFLz1ocWAzIQqHIEjjov3gNTPkgFQ4ArHnBcJEYoffOGWlJt6copaevS5YPI5rHzmbySsengZ8cLJJBBrXzQ==", "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-common": "3.3.4", - "@microsoft/applicationinsights-core-js": "3.3.4", + "@microsoft/applicationinsights-common": "3.3.10", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-common": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.4.tgz", - "integrity": "sha512-4ms16MlIvcP4WiUPqopifNxcWCcrXQJ2ADAK/75uok2mNQe6ZNRsqb/P+pvhUxc8A5HRlvoXPP1ptDSN5Girgw==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.10.tgz", + "integrity": "sha512-RVIenPIvNgZCbjJdALvLM4rNHgAFuHI7faFzHCgnI6S2WCUNGHeXlQTs9EUUrL+n2TPp9/cd0KKMILU5VVyYiA==", "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-core-js": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.4.tgz", - "integrity": "sha512-MummANF0mgKIkdvVvfmHQTBliK114IZLRhTL0X0Ep+zjDwWMHqYZgew0nlFKAl6ggu42abPZFK5afpE7qjtYJA==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.10.tgz", + "integrity": "sha512-5yKeyassZTq2l+SAO4npu6LPnbS++UD+M+Ghjm9uRzoBwD8tumFx0/F8AkSVqbniSREd+ztH/2q2foewa2RZyg==", "license": "MIT", "dependencies": { "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { "tslib": ">= 1.0.0" @@ -103,18 +124,18 @@ } }, "node_modules/@microsoft/applicationinsights-web-basic": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.4.tgz", - "integrity": "sha512-OpEPXr8vU/t/M8T9jvWJzJx/pCyygIiR1nGM/2PTde0wn7anl71Gxl5fWol7K/WwFEORNjkL3CEyWOyDc+28AA==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.10.tgz", + "integrity": "sha512-AZib5DAT3NU0VT0nLWEwXrnoMDDgZ/5S4dso01CNU5ELNxLdg+1fvchstlVdMy4FrAnxzs8Wf/GIQNFYOVgpAw==", "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-channel-js": "3.3.4", - "@microsoft/applicationinsights-common": "3.3.4", - "@microsoft/applicationinsights-core-js": "3.3.4", + "@microsoft/applicationinsights-channel-js": "3.3.10", + "@microsoft/applicationinsights-common": "3.3.10", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { "tslib": ">= 1.0.0" @@ -139,79 +160,55 @@ } }, "node_modules/@nevware21/ts-utils": { - "version": "0.11.6", - "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.11.6.tgz", - "integrity": "sha512-OUUJTh3fnaUSzg9DEHgv3d7jC+DnPL65mIO7RaR+jWve7+MmcgIvF79gY97DPQ4frH+IpNR78YAYd/dW4gK3kg==", + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.12.5.tgz", + "integrity": "sha512-JPQZWPKQJjj7kAftdEZL0XDFfbMgXCGiUAZe0d7EhLC3QlXTlZdSckGqqRIQ2QNl0VTEZyZUvRBw6Ednw089Fw==", "license": "MIT" }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/@vscode/extension-telemetry": { - "version": "0.9.8", - "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.8.tgz", - "integrity": "sha512-7YcKoUvmHlIB8QYCE4FNzt3ErHi9gQPhdCM3ZWtpw1bxPT0I+lMdx52KHlzTNoJzQ2NvMX7HyzyDwBEiMgTrWQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-1.4.0.tgz", + "integrity": "sha512-mqypuqIDn+vQnaQ8tMdvCRn9BprZTH2j6nlFFv9hE6hB25n32F8rOZinos8OgblRi87X1qdBpjpb0Em1LGSG9w==", "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "^4.3.4", - "@microsoft/1ds-post-js": "^4.3.4", - "@microsoft/applicationinsights-web-basic": "^3.3.4" + "@microsoft/1ds-core-js": "^4.3.10", + "@microsoft/1ds-post-js": "^4.3.10", + "@microsoft/applicationinsights-web-basic": "^3.3.10" }, "engines": { "vscode": "^1.75.0" } }, - "node_modules/balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c= sha512-9Y0g0Q8rmSt+H33DfKv7FOc3v+iRI+o1lbzt8jGcIosYW37IIW/2XVYq5NPdmaD5NQ59Nk26Kl/vZbwW9Fr8vg==" - }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^2.0.1" + "@isaacs/brace-expansion": "^5.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dependencies": { - "lru-cache": "^6.0.0" - }, + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -220,56 +217,56 @@ } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" }, "node_modules/vscode-jsonrpc": { - "version": "9.0.0-next.6", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.6.tgz", - "integrity": "sha512-KCSvUNsFiVciG9iqjJKBZOd66CN3ZKohDlYRmoOi+pd8l15MFLZ8wRG4c+wuzePGba/8WcCG2TM+C/GVlvuaeA==", + "version": "9.0.0-next.11", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.11.tgz", + "integrity": "sha512-u6LElQNbSiE9OugEEmrUKwH6+8BpPz2S5MDHvQUqHL//I4Q8GPikKLOUf856UnbLkZdhxaPrExac1lA3XwpIPA==", + "license": "MIT", "engines": { "node": ">=14.0.0" } }, "node_modules/vscode-languageclient": { - "version": "10.0.0-next.13", - "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-10.0.0-next.13.tgz", - "integrity": "sha512-KLsOMJoYpkk36PIgcOjyZ4AekOfzp4kdWdRRbVKeVvSIrwrn/4RSZr0NlD6EvUBBJSsJW4WDrYY7Y3znkqa6+w==", + "version": "10.0.0-next.19", + "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-10.0.0-next.19.tgz", + "integrity": "sha512-sJtO8y0Dxs4ue/DK0QgO/ATBfZwQdee3TqvCsoqUej/GZrBA01DTf4pbfswRxHsTxN2yH0haImUnMafWHtE4CQ==", "license": "MIT", "dependencies": { - "minimatch": "^9.0.3", - "semver": "^7.6.0", - "vscode-languageserver-protocol": "3.17.6-next.11" + "minimatch": "^10.0.3", + "semver": "^7.7.1", + "vscode-languageserver-protocol": "3.17.6-next.16" }, "engines": { "vscode": "^1.91.0" } }, "node_modules/vscode-languageserver-protocol": { - "version": "3.17.6-next.11", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.11.tgz", - "integrity": "sha512-GeJxEp1TiLsp79f8WG5n10wLViXfgFKb99hU9K8m7KDWM95/QFEqWkm79f9LVm54tUK74I91a9EeiQLCS/FABQ==", + "version": "3.17.6-next.16", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.16.tgz", + "integrity": "sha512-kQTjXEuyxMbdmmZ3U+Lib3oUl12xEKNc73RtWxPSDS3TFtjVwt98Q1CUzfDA9EUpsA24M46Bl6q3sLe9AUOKyw==", + "license": "MIT", "dependencies": { - "vscode-jsonrpc": "9.0.0-next.6", - "vscode-languageserver-types": "3.17.6-next.5" + "vscode-jsonrpc": "9.0.0-next.11", + "vscode-languageserver-types": "3.17.6-next.6" } }, "node_modules/vscode-languageserver-types": { - "version": "3.17.6-next.5", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.5.tgz", - "integrity": "sha512-QFmf3Yl1tCgUQfA77N9Me/LXldJXkIVypQbty2rJ1DNHQkC+iwvm4Z2tXg9czSwlhvv0pD4pbF5mT7WhAglolw==" + "version": "3.17.6-next.6", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.6.tgz", + "integrity": "sha512-aiJY5/yW+xzw7KPNlwi3gQtddq/3EIn5z8X8nCgJfaiAij2R1APKePngv+MUdLdYJBVTLu+Qa0ODsT+pHgYguQ==", + "license": "MIT" }, "node_modules/vscode-uri": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", - "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==" - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "license": "MIT" } } } diff --git a/extensions/html-language-features/package.json b/extensions/html-language-features/package.json index be411fe63a2b..d405d4198baa 100644 --- a/extensions/html-language-features/package.json +++ b/extensions/html-language-features/package.json @@ -258,12 +258,12 @@ ] }, "dependencies": { - "@vscode/extension-telemetry": "^0.9.8", - "vscode-languageclient": "^10.0.0-next.13", - "vscode-uri": "^3.0.8" + "@vscode/extension-telemetry": "^1.4.0", + "vscode-languageclient": "^10.0.0-next.19", + "vscode-uri": "^3.1.0" }, "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "repository": { "type": "git", diff --git a/extensions/html-language-features/server/package-lock.json b/extensions/html-language-features/server/package-lock.json index 5795436cf942..4d7c697e1f51 100644 --- a/extensions/html-language-features/server/package-lock.json +++ b/extensions/html-language-features/server/package-lock.json @@ -10,33 +10,35 @@ "license": "MIT", "dependencies": { "@vscode/l10n": "^0.0.18", - "vscode-css-languageservice": "^6.3.2", - "vscode-html-languageservice": "^5.3.2", - "vscode-languageserver": "^10.0.0-next.11", + "vscode-css-languageservice": "^6.3.9", + "vscode-html-languageservice": "^5.6.1", + "vscode-languageserver": "^10.0.0-next.16", "vscode-languageserver-textdocument": "^1.0.12", "vscode-uri": "^3.0.8" }, "devDependencies": { - "@types/mocha": "^9.1.1", - "@types/node": "20.x" + "@types/mocha": "^10.0.10", + "@types/node": "25.x" }, "engines": { "node": "*" } }, "node_modules/@types/mocha": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", - "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==", - "dev": true + "version": "10.0.10", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz", + "integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==", + "dev": true, + "license": "MIT" }, "node_modules/@types/node": { - "version": "20.12.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.5.tgz", - "integrity": "sha512-BD+BjQ9LS/D8ST9p5uqBxghlN+S42iuNxjsUGjeZobe/ciXzk2qb1B6IXc6AnRLS+yFJRpN2IPEHMzwspfDJNw==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/@vscode/l10n": { @@ -45,68 +47,72 @@ "integrity": "sha512-KYSIHVmslkaCDyw013pphY+d7x1qV8IZupYfeIfzNA+nsaWHbn5uPuQRvdRFsa9zFzGeudPuoGoZ1Op4jrJXIQ==" }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" }, "node_modules/vscode-css-languageservice": { - "version": "6.3.2", - "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-6.3.2.tgz", - "integrity": "sha512-GEpPxrUTAeXWdZWHev1OJU9lz2Q2/PPBxQ2TIRmLGvQiH3WZbqaNoute0n0ewxlgtjzTW3AKZT+NHySk5Rf4Eg==", + "version": "6.3.9", + "resolved": "https://registry.npmjs.org/vscode-css-languageservice/-/vscode-css-languageservice-6.3.9.tgz", + "integrity": "sha512-1tLWfp+TDM5ZuVWht3jmaY5y7O6aZmpeXLoHl5bv1QtRsRKt4xYGRMmdJa5Pqx/FTkgRbsna9R+Gn2xE+evVuA==", "license": "MIT", "dependencies": { "@vscode/l10n": "^0.0.18", "vscode-languageserver-textdocument": "^1.0.12", "vscode-languageserver-types": "3.17.5", - "vscode-uri": "^3.0.8" + "vscode-uri": "^3.1.0" } }, "node_modules/vscode-html-languageservice": { - "version": "5.3.2", - "resolved": "https://registry.npmjs.org/vscode-html-languageservice/-/vscode-html-languageservice-5.3.2.tgz", - "integrity": "sha512-3MgFQqVG+iQVNG7QI/slaoL7lJpne0nssX082kjUF1yn/YJa8BWCLeCJjM0YpTlp8A7JT1+J22mk4qSPx3NjSQ==", + "version": "5.6.1", + "resolved": "https://registry.npmjs.org/vscode-html-languageservice/-/vscode-html-languageservice-5.6.1.tgz", + "integrity": "sha512-5Mrqy5CLfFZUgkyhNZLA1Ye5g12Cb/v6VM7SxUzZUaRKWMDz4md+y26PrfRTSU0/eQAl3XpO9m2og+GGtDMuaA==", "license": "MIT", "dependencies": { "@vscode/l10n": "^0.0.18", "vscode-languageserver-textdocument": "^1.0.12", "vscode-languageserver-types": "^3.17.5", - "vscode-uri": "^3.0.8" + "vscode-uri": "^3.1.0" } }, "node_modules/vscode-jsonrpc": { - "version": "9.0.0-next.6", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.6.tgz", - "integrity": "sha512-KCSvUNsFiVciG9iqjJKBZOd66CN3ZKohDlYRmoOi+pd8l15MFLZ8wRG4c+wuzePGba/8WcCG2TM+C/GVlvuaeA==", + "version": "9.0.0-next.11", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.11.tgz", + "integrity": "sha512-u6LElQNbSiE9OugEEmrUKwH6+8BpPz2S5MDHvQUqHL//I4Q8GPikKLOUf856UnbLkZdhxaPrExac1lA3XwpIPA==", + "license": "MIT", "engines": { "node": ">=14.0.0" } }, "node_modules/vscode-languageserver": { - "version": "10.0.0-next.11", - "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-10.0.0-next.11.tgz", - "integrity": "sha512-cmobSrVDYhlh/t02vz/bV8nNpds8mus5HnILULae2iAvOjoaJPnTAp0jJWoYdUqTpIVzT9JV6JMKqLEvdqpeqg==", + "version": "10.0.0-next.16", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-10.0.0-next.16.tgz", + "integrity": "sha512-RbsYDOhddv1NtBCAR7+oVxxCmOpQUHhrtgUE0xz6J+BJGSCkfOqBCyLUIwSjKk2rK9llxUj/pR5aL8QCsXrxow==", "license": "MIT", "dependencies": { - "vscode-languageserver-protocol": "3.17.6-next.11" + "vscode-languageserver-protocol": "3.17.6-next.16" }, "bin": { "installServerIntoExtension": "bin/installServerIntoExtension" } }, "node_modules/vscode-languageserver-protocol": { - "version": "3.17.6-next.11", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.11.tgz", - "integrity": "sha512-GeJxEp1TiLsp79f8WG5n10wLViXfgFKb99hU9K8m7KDWM95/QFEqWkm79f9LVm54tUK74I91a9EeiQLCS/FABQ==", + "version": "3.17.6-next.16", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.16.tgz", + "integrity": "sha512-kQTjXEuyxMbdmmZ3U+Lib3oUl12xEKNc73RtWxPSDS3TFtjVwt98Q1CUzfDA9EUpsA24M46Bl6q3sLe9AUOKyw==", + "license": "MIT", "dependencies": { - "vscode-jsonrpc": "9.0.0-next.6", - "vscode-languageserver-types": "3.17.6-next.5" + "vscode-jsonrpc": "9.0.0-next.11", + "vscode-languageserver-types": "3.17.6-next.6" } }, "node_modules/vscode-languageserver-protocol/node_modules/vscode-languageserver-types": { - "version": "3.17.6-next.5", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.5.tgz", - "integrity": "sha512-QFmf3Yl1tCgUQfA77N9Me/LXldJXkIVypQbty2rJ1DNHQkC+iwvm4Z2tXg9czSwlhvv0pD4pbF5mT7WhAglolw==" + "version": "3.17.6-next.6", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.6.tgz", + "integrity": "sha512-aiJY5/yW+xzw7KPNlwi3gQtddq/3EIn5z8X8nCgJfaiAij2R1APKePngv+MUdLdYJBVTLu+Qa0ODsT+pHgYguQ==", + "license": "MIT" }, "node_modules/vscode-languageserver-textdocument": { "version": "1.0.12", @@ -119,9 +125,10 @@ "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==" }, "node_modules/vscode-uri": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", - "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "license": "MIT" } } } diff --git a/extensions/html-language-features/server/package.json b/extensions/html-language-features/server/package.json index 7017ee857218..34d2016e15ea 100644 --- a/extensions/html-language-features/server/package.json +++ b/extensions/html-language-features/server/package.json @@ -10,15 +10,15 @@ "main": "./out/node/htmlServerMain", "dependencies": { "@vscode/l10n": "^0.0.18", - "vscode-css-languageservice": "^6.3.2", - "vscode-html-languageservice": "^5.3.2", - "vscode-languageserver": "^10.0.0-next.11", + "vscode-css-languageservice": "^6.3.9", + "vscode-html-languageservice": "^5.6.1", + "vscode-languageserver": "^10.0.0-next.16", "vscode-languageserver-textdocument": "^1.0.12", "vscode-uri": "^3.0.8" }, "devDependencies": { - "@types/mocha": "^9.1.1", - "@types/node": "20.x" + "@types/mocha": "^10.0.10", + "@types/node": "25.x" }, "scripts": { "compile": "npx gulp compile-extension:html-language-features-server", diff --git a/extensions/html-language-features/server/src/modes/javascriptMode.ts b/extensions/html-language-features/server/src/modes/javascriptMode.ts index d820810c9203..3c864d53b4e7 100644 --- a/extensions/html-language-features/server/src/modes/javascriptMode.ts +++ b/extensions/html-language-features/server/src/modes/javascriptMode.ts @@ -12,20 +12,35 @@ import { } from './languageModes'; import { getWordAtText, isWhitespaceOnly, repeat } from '../utils/strings'; import { HTMLDocumentRegions } from './embeddedSupport'; +import { normalize, sep } from 'path'; import * as ts from 'typescript'; import { getSemanticTokens, getSemanticTokenLegend } from './javascriptSemanticTokens'; +import { statSync } from 'fs'; const JS_WORD_REGEX = /(-?\d*\.\d\w*)|([^\`\~\!\@\#\%\^\&\*\(\)\-\=\+\[\{\]\}\\\|\;\:\'\"\,\.\<\>\/\?\s]+)/g; +/** TypeScript does not handle schemes on file references, so normalize and remove the schemes when communicating with tsserver */ +function deschemeURI(uri: string) { + if (!uri.startsWith('file://')) { + return uri; + } + // This is replicating the logic in TypeScriptServiceClient.normalizedPath + const newPath = normalize(uri.replace('file://', '')); + + // Both \ and / must be escaped in regular expressions + return newPath.replace(new RegExp('\\' + sep, 'g'), '/'); +} + function getLanguageServiceHost(scriptKind: ts.ScriptKind) { const compilerOptions: ts.CompilerOptions = { allowNonTsExtensions: true, allowJs: true, lib: ['lib.es2020.full.d.ts'], target: ts.ScriptTarget.Latest, moduleResolution: ts.ModuleResolutionKind.Classic, experimentalDecorators: false }; let currentTextDocument = TextDocument.create('init', 'javascript', 1, ''); - const jsLanguageService = import(/* webpackChunkName: "javascriptLibs" */ './javascriptLibs.js').then(libs => { + let currentWorkspace: Workspace = undefined!; + const jsLanguageService = import(/* webpackChunkName: "javascriptLibs" */ './javascriptLibs').then(libs => { const host: ts.LanguageServiceHost = { getCompilationSettings: () => compilerOptions, - getScriptFileNames: () => [currentTextDocument.uri, 'jquery'], + getScriptFileNames: () => [deschemeURI(currentTextDocument.uri), 'jquery'], getScriptKind: (fileName) => { if (fileName === currentTextDocument.uri) { return scriptKind; @@ -33,15 +48,26 @@ function getLanguageServiceHost(scriptKind: ts.ScriptKind) { return fileName.substr(fileName.length - 2) === 'ts' ? ts.ScriptKind.TS : ts.ScriptKind.JS; }, getScriptVersion: (fileName: string) => { - if (fileName === currentTextDocument.uri) { + // Let the outer TextDocument give the version + if (fileName === deschemeURI(currentTextDocument.uri)) { return String(currentTextDocument.version); } - return '1'; // default lib an jquery.d.ts are static + // Default libs and jquery.d.ts are static. + // Include node_modules as a perf win + if (fileName.startsWith('lib.') || fileName === 'jquery' || fileName.includes('node_modules')) { + return '1'; + } + // Unsure how this could occur, but better to not raise with statSync + if (!ts.sys.fileExists(fileName)) { return '1'; } + // Use mtime from the fs + return String(statSync(fileName).mtimeMs); }, getScriptSnapshot: (fileName: string) => { let text = ''; - if (fileName === currentTextDocument.uri) { + if (fileName === deschemeURI(currentTextDocument.uri)) { text = currentTextDocument.getText(); + } else if (ts.sys.fileExists(fileName)) { + text = ts.sys.readFile(fileName, 'utf8')!; } else { text = libs.loadLibrary(fileName); } @@ -51,37 +77,24 @@ function getLanguageServiceHost(scriptKind: ts.ScriptKind) { getChangeRange: () => undefined }; }, - getCurrentDirectory: () => '', - getDefaultLibFileName: (_options: ts.CompilerOptions) => 'es2020.full', - readFile: (path: string, _encoding?: string | undefined): string | undefined => { - if (path === currentTextDocument.uri) { - return currentTextDocument.getText(); - } else { - return libs.loadLibrary(path); - } - }, - fileExists: (path: string): boolean => { - if (path === currentTextDocument.uri) { - return true; - } else { - return !!libs.loadLibrary(path); - } + getCurrentDirectory: () => { + const workspace = currentWorkspace && currentWorkspace.folders.find(ws => deschemeURI(currentTextDocument.uri).startsWith(deschemeURI(ws.uri))); + return workspace ? deschemeURI(workspace.uri) : ''; }, - directoryExists: (path: string): boolean => { - // typescript tries to first find libraries in node_modules/@types and node_modules/@typescript - // there's no node_modules in our setup - if (path.startsWith('node_modules')) { - return false; - } - return true; - - } + getDefaultLibFileName: (_options: ts.CompilerOptions) => 'es6', + fileExists: ts.sys.fileExists, + readFile: ts.sys.readFile, + readDirectory: ts.sys.readDirectory, + directoryExists: ts.sys.directoryExists, + getDirectories: ts.sys.getDirectories, }; + return ts.createLanguageService(host); }); return { - async getLanguageService(jsDocument: TextDocument): Promise { + async getLanguageService(jsDocument: TextDocument, workspace: Workspace): Promise { currentTextDocument = jsDocument; + currentWorkspace = workspace; return jsLanguageService; }, getCompilationSettings() { @@ -118,10 +131,12 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache !ignoredErrors.includes(d.code)).map((diag: ts.Diagnostic): Diagnostic => { + const languageService = await host.getLanguageService(jsDocument, workspace); + const filePath = deschemeURI(jsDocument.uri); + + const syntaxDiagnostics: ts.Diagnostic[] = languageService.getSyntacticDiagnostics(filePath); + const semanticDiagnostics = languageService.getSemanticDiagnostics(filePath); + return syntaxDiagnostics.concat(semanticDiagnostics).map((diag: ts.Diagnostic): Diagnostic => { return { range: convertRange(jsDocument, diag), severity: DiagnosticSeverity.Error, @@ -132,9 +147,12 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache { const jsDocument = jsDocuments.get(document); - const jsLanguageService = await host.getLanguageService(jsDocument); - const offset = jsDocument.offsetAt(position); - const completions = jsLanguageService.getCompletionsAtPosition(jsDocument.uri, offset, { includeExternalModuleExports: false, includeInsertTextCompletions: false }); + const jsLanguageService = await host.getLanguageService(jsDocument, workspace); + const filePath = deschemeURI(jsDocument.uri); + + let offset = jsDocument.offsetAt(position); + let completions = jsLanguageService.getCompletionsAtPosition(filePath, offset, { includeExternalModuleExports: false, includeInsertTextCompletions: false }); + if (!completions) { return { isIncomplete: false, items: [] }; } @@ -160,22 +178,25 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache { - if (isCompletionItemData(item.data)) { - const jsDocument = jsDocuments.get(document); - const jsLanguageService = await host.getLanguageService(jsDocument); - const details = jsLanguageService.getCompletionEntryDetails(jsDocument.uri, item.data.offset, item.label, undefined, undefined, undefined, undefined); - if (details) { - item.detail = ts.displayPartsToString(details.displayParts); - item.documentation = ts.displayPartsToString(details.documentation); - delete item.data; - } + const jsDocument = jsDocuments.get(document); + const jsLanguageService = await host.getLanguageService(jsDocument, workspace); + const filePath = deschemeURI(jsDocument.uri); + + // @ts-expect-error until 4.3 protocol update + let details = jsLanguageService.getCompletionEntryDetails(filePath, item.data.offset, item.label, undefined, undefined, undefined, undefined); + if (details) { + item.detail = ts.displayPartsToString(details.displayParts); + item.documentation = ts.displayPartsToString(details.documentation); + delete item.data; } return item; }, async doHover(document: TextDocument, position: Position): Promise { const jsDocument = jsDocuments.get(document); - const jsLanguageService = await host.getLanguageService(jsDocument); - const info = jsLanguageService.getQuickInfoAtPosition(jsDocument.uri, jsDocument.offsetAt(position)); + const jsLanguageService = await host.getLanguageService(jsDocument, workspace); + const filePath = deschemeURI(jsDocument.uri); + + let info = jsLanguageService.getQuickInfoAtPosition(filePath, jsDocument.offsetAt(position)); if (info) { const contents = ts.displayPartsToString(info.displayParts); return { @@ -187,8 +208,10 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache { const jsDocument = jsDocuments.get(document); - const jsLanguageService = await host.getLanguageService(jsDocument); - const signHelp = jsLanguageService.getSignatureHelpItems(jsDocument.uri, jsDocument.offsetAt(position), undefined); + const jsLanguageService = await host.getLanguageService(jsDocument, workspace); + const filePath = deschemeURI(jsDocument.uri); + + let signHelp = jsLanguageService.getSignatureHelpItems(filePath, jsDocument.offsetAt(position), undefined); if (signHelp) { const ret: SignatureHelp = { activeSignature: signHelp.selectedItemIndex, @@ -225,13 +248,15 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache { @@ -247,8 +272,10 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache { const jsDocument = jsDocuments.get(document); - const jsLanguageService = await host.getLanguageService(jsDocument); - const highlights = jsLanguageService.getDocumentHighlights(jsDocument.uri, jsDocument.offsetAt(position), [jsDocument.uri]); + const jsLanguageService = await host.getLanguageService(jsDocument, workspace); + const filePath = deschemeURI(jsDocument.uri); + + const highlights = jsLanguageService.getDocumentHighlights(filePath, jsDocument.offsetAt(position), [filePath]); const out: DocumentHighlight[] = []; for (const entry of highlights || []) { for (const highlight of entry.highlightSpans) { @@ -262,8 +289,10 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache { const jsDocument = jsDocuments.get(document); - const jsLanguageService = await host.getLanguageService(jsDocument); - const items = jsLanguageService.getNavigationBarItems(jsDocument.uri); + const jsLanguageService = await host.getLanguageService(jsDocument, workspace); + const filePath = deschemeURI(jsDocument.uri); + + let items = jsLanguageService.getNavigationBarItems(filePath); if (items) { const result: SymbolInformation[] = []; const existing = Object.create(null); @@ -299,8 +328,10 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache { const jsDocument = jsDocuments.get(document); - const jsLanguageService = await host.getLanguageService(jsDocument); - const definition = jsLanguageService.getDefinitionAtPosition(jsDocument.uri, jsDocument.offsetAt(position)); + const jsLanguageService = await host.getLanguageService(jsDocument, workspace); + const filePath = deschemeURI(jsDocument.uri); + + let definition = jsLanguageService.getDefinitionAtPosition(filePath, jsDocument.offsetAt(position)); if (definition) { return definition.filter(d => d.fileName === jsDocument.uri).map(d => { return { @@ -313,10 +344,12 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache { const jsDocument = jsDocuments.get(document); - const jsLanguageService = await host.getLanguageService(jsDocument); - const references = jsLanguageService.getReferencesAtPosition(jsDocument.uri, jsDocument.offsetAt(position)); + const jsLanguageService = await host.getLanguageService(jsDocument, workspace); + const filePath = deschemeURI(jsDocument.uri); + + let references = jsLanguageService.getReferencesAtPosition(filePath, jsDocument.offsetAt(position)); if (references) { - return references.filter(d => d.fileName === jsDocument.uri).map(d => { + return references.filter(d => d.fileName === filePath).map(d => { return { uri: document.uri, range: convertRange(jsDocument, d.textSpan) @@ -327,17 +360,20 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache { const jsDocument = jsDocuments.get(document); - const jsLanguageService = await host.getLanguageService(jsDocument); + const jsLanguageService = await host.getLanguageService(jsDocument, workspace); + const filePath = deschemeURI(jsDocument.uri); + function convertSelectionRange(selectionRange: ts.SelectionRange): SelectionRange { const parent = selectionRange.parent ? convertSelectionRange(selectionRange.parent) : undefined; return SelectionRange.create(convertRange(jsDocument, selectionRange.textSpan), parent); } - const range = jsLanguageService.getSmartSelectionRange(jsDocument.uri, jsDocument.offsetAt(position)); + const range = jsLanguageService.getSmartSelectionRange(filePath, jsDocument.offsetAt(position)); return convertSelectionRange(range); }, async format(document: TextDocument, range: Range, formatParams: FormattingOptions, settings: Settings = globalSettings): Promise { const jsDocument = documentRegions.get(document).getEmbeddedDocument('javascript', true); - const jsLanguageService = await host.getLanguageService(jsDocument); + const jsLanguageService = await host.getLanguageService(jsDocument, workspace); + const filePath = deschemeURI(jsDocument.uri); const formatterSettings = settings && settings.javascript && settings.javascript.format; @@ -350,7 +386,7 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache { const jsDocument = jsDocuments.get(document); - const jsLanguageService = await host.getLanguageService(jsDocument); - const spans = jsLanguageService.getOutliningSpans(jsDocument.uri); - const ranges: FoldingRange[] = []; - for (const span of spans) { - const curr = convertRange(jsDocument, span.textSpan); - const startLine = curr.start.line; - const endLine = curr.end.line; + const jsLanguageService = await host.getLanguageService(jsDocument, workspace); + const filePath = deschemeURI(jsDocument.uri); + + let spans = jsLanguageService.getOutliningSpans(filePath); + let ranges: FoldingRange[] = []; + for (let span of spans) { + let curr = convertRange(jsDocument, span.textSpan); + let startLine = curr.start.line; + let endLine = curr.end.line; if (startLine < endLine) { const foldingRange: FoldingRange = { startLine, endLine }; const match = document.getText(curr).match(/^\s*\/(?:(\/\s*#(?:end)?region\b)|(\*|\/))/); @@ -396,8 +434,10 @@ export function getJavaScriptMode(documentRegions: LanguageModelCache { const jsDocument = jsDocuments.get(document); - const jsLanguageService = await host.getLanguageService(jsDocument); - return [...getSemanticTokens(jsLanguageService, jsDocument, jsDocument.uri)]; + const jsLanguageService = await host.getLanguageService(jsDocument, workspace); + const filePath = deschemeURI(jsDocument.uri); + + return getSemanticTokens(jsLanguageService, jsDocument, filePath); }, getSemanticTokenLegend(): { types: string[]; modifiers: string[] } { return getSemanticTokenLegend(); diff --git a/extensions/html-language-features/server/src/test/completions.test.ts b/extensions/html-language-features/server/src/test/completions.test.ts index fbad266e2dea..6cc0e0d3377f 100644 --- a/extensions/html-language-features/server/src/test/completions.test.ts +++ b/extensions/html-language-features/server/src/test/completions.test.ts @@ -94,6 +94,21 @@ suite('HTML Completion', () => { }); }); +suite('JSDoc Imports', () => { + const fixtureRoot = path.resolve(__dirname, '../../src/test/jsdocImportFixtures'); + const fixtureWorkspace = { name: 'fixture', uri: URI.file(fixtureRoot).toString() }; + const indexHtmlUri = URI.file(path.resolve(fixtureRoot, 'index.html')).toString(); + + test('Imports across files', async () => { + await testCompletionFor('', { + items: [ + { label: 'other', }, + { label: 'property', }, + ] + }, indexHtmlUri, [fixtureWorkspace] ); + }); +}); + suite('HTML Path Completion', () => { const triggerSuggestCommand = { title: 'Suggest', diff --git a/extensions/html-language-features/server/src/test/jsdocImportFixtures/index.html b/extensions/html-language-features/server/src/test/jsdocImportFixtures/index.html new file mode 100644 index 000000000000..8b137891791f --- /dev/null +++ b/extensions/html-language-features/server/src/test/jsdocImportFixtures/index.html @@ -0,0 +1 @@ + diff --git a/extensions/html-language-features/server/src/test/jsdocImportFixtures/index.js b/extensions/html-language-features/server/src/test/jsdocImportFixtures/index.js new file mode 100644 index 000000000000..a4a092d83492 --- /dev/null +++ b/extensions/html-language-features/server/src/test/jsdocImportFixtures/index.js @@ -0,0 +1,4 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ diff --git a/extensions/terminal-suggest/src/fig/api-binding-wrappers/executeCommandWrappers.ts b/extensions/html-language-features/server/src/test/jsdocImportFixtures/jsDocTypes.ts similarity index 69% rename from extensions/terminal-suggest/src/fig/api-binding-wrappers/executeCommandWrappers.ts rename to extensions/html-language-features/server/src/test/jsdocImportFixtures/jsDocTypes.ts index 98ac1a298a5f..938dd5d7b2d5 100644 --- a/extensions/terminal-suggest/src/fig/api-binding-wrappers/executeCommandWrappers.ts +++ b/extensions/html-language-features/server/src/test/jsdocImportFixtures/jsDocTypes.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { executeCommandTimeout } from './executeCommand'; - -export const executeCommand: Fig.ExecuteCommandFunction = (args) => - executeCommandTimeout(args); +export type SomeType = { + property: string + other: number +}; diff --git a/extensions/ipynb/.vscode/launch.json b/extensions/ipynb/.vscode/launch.json index 30130a429d58..eaf7cac3cbd6 100644 --- a/extensions/ipynb/.vscode/launch.json +++ b/extensions/ipynb/.vscode/launch.json @@ -14,6 +14,47 @@ ], "request": "launch", "type": "extensionHost" - } + }, + { + // Run this first: https://github.com/microsoft/vscode-jupyter/blob/main/src/test/datascience/setupTestEnvs.cmd + // Then specify either a grep below or mark a test as 'test.only' to run the test that's failing. + "name": "Tests", + "type": "extensionHost", + "request": "launch", + "runtimeExecutable": "${execPath}", + "args": [ + "--extensionDevelopmentPath=${workspaceFolder}", + "--extensionTestsPath=${workspaceFolder}/out/test/index.node.js" + ], + "env": { + "VSC_JUPYTER_FORCE_LOGGING": "1", + "VSC_JUPYTER_CI_TEST_GREP": "", // Leave as `VSCode Notebook` to run only Notebook tests. + "VSC_JUPYTER_CI_TEST_INVERT_GREP": "", // Initialize this to invert the grep (exclude tests with value defined in grep). + "CI_PYTHON_PATH": "", // Update with path to real python interpereter used for testing. + "VSC_JUPYTER_CI_RUN_NON_PYTHON_NB_TEST": "", // Initialize this to run tests again Julia & other kernels. + // "TF_BUILD": "", // Set to anything to force full logging + "TEST_FILES_SUFFIX": "*.vscode.test,*.vscode.common.test", + "VSC_JUPYTER_REMOTE_NATIVE_TEST": "false", // Change to `true` to run the Native Notebook tests with remote jupyter connections. + "VSC_JUPYTER_NON_RAW_NATIVE_TEST": "false", // Change to `true` to run the Native Notebook tests with non-raw kernels (i.e. local jupyter server). + "XVSC_JUPYTER_INSTRUMENT_CODE_FOR_COVERAGE": "1", + "XVSC_JUPYTER_INSTRUMENT_CODE_FOR_COVERAGE_HTML": "1", //Enable to get full coverage repor (in coverage folder). + "VSC_JUPYTER_EXPOSE_SVC": "1" + // "VSC_JUPYTER_CI_IS_CONDA": "true" // Enable to run conda tests + }, + "sourceMaps": true, + "outFiles": [ + "${workspaceFolder}/out/**/*.js", + "!${workspaceFolder}/**/node_modules**/*" + ], + "preLaunchTask": "Compile", + "skipFiles": [ + "/**" + ], + "presentation": { + "group": "2_tests", + "order": 6 + } + }, + ] } \ No newline at end of file diff --git a/extensions/ipynb/package-lock.json b/extensions/ipynb/package-lock.json index 7042d6a22b2e..71770e62a187 100644 --- a/extensions/ipynb/package-lock.json +++ b/extensions/ipynb/package-lock.json @@ -10,11 +10,11 @@ "license": "MIT", "dependencies": { "@enonic/fnv-plus": "^1.3.0", - "detect-indent": "^6.0.0" + "detect-indent": "^7.0.2" }, "devDependencies": { - "@jupyterlab/nbformat": "^3.2.9", - "@types/markdown-it": "12.2.3" + "@jupyterlab/nbformat": "^4.5.3", + "@types/markdown-it": "14.1.2" }, "engines": { "vscode": "^1.57.0" @@ -26,51 +26,67 @@ "integrity": "sha512-BCN9uNWH8AmiP7BXBJqEinUY9KXalmRzo+L0cB/mQsmFfzODxwQrbvxCHXUNH2iP+qKkWYtB4vyy8N62PViMFw==" }, "node_modules/@jupyterlab/nbformat": { - "version": "3.4.3", - "resolved": "https://registry.npmjs.org/@jupyterlab/nbformat/-/nbformat-3.4.3.tgz", - "integrity": "sha512-i/yADrwhhAJJCUOTa+fEBMyJO7fvX9Y73I0B7V6dQhGcrmrEKLC3wk4yOo63+jRntd5+dupbiOtz3w1ncIXwIA==", + "version": "4.5.3", + "resolved": "https://registry.npmjs.org/@jupyterlab/nbformat/-/nbformat-4.5.3.tgz", + "integrity": "sha512-9LIyOpcQIVLxjQ3XIvjpIID5AtgGnCEniUFMmlvfJxo4WFtpS3AykTcwri0TRWhgPmo1ijNp+iTiMJmm1hl07g==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@lumino/coreutils": "^1.11.0" + "@lumino/coreutils": "^2.2.2" } }, + "node_modules/@lumino/algorithm": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@lumino/algorithm/-/algorithm-2.0.4.tgz", + "integrity": "sha512-gddBhESPqu25KWLeAK9Kz8tS9Ph7P45i0CNG7Ia4XMhK9PHLtTsBdJTC9jP+MqhbzC8zDT/4ekvYRV9ojRPj7Q==", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/@lumino/coreutils": { - "version": "1.12.0", - "resolved": "https://registry.npmjs.org/@lumino/coreutils/-/coreutils-1.12.0.tgz", - "integrity": "sha512-DSglh4ylmLi820CNx9soJmDJCpUgymckdWeGWuN0Ash5g60oQvrQDfosVxEhzmNvtvXv45WZEqSBzDP6E5SEmQ==", + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/@lumino/coreutils/-/coreutils-2.2.2.tgz", + "integrity": "sha512-zaKJaK7rawPATn2BGHkbMrR6oK3s9PxNe9KreLwWF2dB4ZBHDiEmNLRyHRorfJ7XqVOEXAsAAj0jFn+qJPC/4Q==", "dev": true, - "peerDependencies": { - "crypto": "1.0.1" + "license": "BSD-3-Clause", + "dependencies": { + "@lumino/algorithm": "^2.0.4" } }, "node_modules/@types/linkify-it": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-3.0.2.tgz", - "integrity": "sha512-HZQYqbiFVWufzCwexrvh694SOim8z2d+xJl5UNamcvQFejLY/2YUtzXHYi3cHdI7PMlS8ejH2slRAOJQ32aNbA==", - "dev": true + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "dev": true, + "license": "MIT" }, "node_modules/@types/markdown-it": { - "version": "12.2.3", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", - "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", "dev": true, + "license": "MIT", "dependencies": { - "@types/linkify-it": "*", - "@types/mdurl": "*" + "@types/linkify-it": "^5", + "@types/mdurl": "^2" } }, "node_modules/@types/mdurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-1.0.2.tgz", - "integrity": "sha512-eC4U9MlIcu2q0KQmXszyn5Akca/0jrQmwDRgpAMJai7qBWq4amIQhZyNau4VYGtCeALvW1/NtjzJJ567aZxfKA==", - "dev": true + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "dev": true, + "license": "MIT" }, "node_modules/detect-indent": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", - "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-7.0.2.tgz", + "integrity": "sha512-y+8xyqdGLL+6sh0tVeHcfP/QDd8gUgbasolJJpY7NgeQGSZ739bDtSiaiDgtoicy+mtYB81dKLxO9xRhCyIB3A==", + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12.20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } } } diff --git a/extensions/ipynb/package.json b/extensions/ipynb/package.json index f7ee3c344e70..1437cbba722f 100644 --- a/extensions/ipynb/package.json +++ b/extensions/ipynb/package.json @@ -74,7 +74,8 @@ { "command": "notebook.cellOutput.addToChat", "title": "%addCellOutputToChat.title%", - "category": "Notebook" + "category": "Notebook", + "enablement": "chatIsEnabled" }, { "command": "notebook.cellOutput.openInTextEditor", @@ -161,11 +162,11 @@ }, "dependencies": { "@enonic/fnv-plus": "^1.3.0", - "detect-indent": "^6.0.0" + "detect-indent": "^7.0.2" }, "devDependencies": { - "@jupyterlab/nbformat": "^3.2.9", - "@types/markdown-it": "12.2.3" + "@jupyterlab/nbformat": "^4.5.3", + "@types/markdown-it": "14.1.2" }, "repository": { "type": "git", diff --git a/extensions/ipynb/src/constants.ts b/extensions/ipynb/src/constants.ts index 9a82ccfae390..b72185968a81 100644 --- a/extensions/ipynb/src/constants.ts +++ b/extensions/ipynb/src/constants.ts @@ -5,7 +5,7 @@ import type { DocumentSelector } from 'vscode'; -export const defaultNotebookFormat = { major: 4, minor: 2 }; +export const defaultNotebookFormat = { major: 4, minor: 5 }; export const ATTACHMENT_CLEANUP_COMMANDID = 'ipynb.cleanInvalidImageAttachment'; export const JUPYTER_NOTEBOOK_MARKDOWN_SELECTOR: DocumentSelector = { notebookType: 'jupyter-notebook', language: 'markdown' }; diff --git a/extensions/ipynb/src/helper.ts b/extensions/ipynb/src/helper.ts index beab091f5c69..6d67b7d529fa 100644 --- a/extensions/ipynb/src/helper.ts +++ b/extensions/ipynb/src/helper.ts @@ -147,13 +147,15 @@ export interface ITask { /** * Copied from src/vs/base/common/uuid.ts */ -export function generateUuid() { - // use `randomValues` if possible - function getRandomValues(bucket: Uint8Array): Uint8Array { - for (let i = 0; i < bucket.length; i++) { - bucket[i] = Math.floor(Math.random() * 256); - } - return bucket; +export function generateUuid(): string { + // use `randomUUID` if possible + if (typeof crypto.randomUUID === 'function') { + // see https://developer.mozilla.org/en-US/docs/Web/API/Window/crypto + // > Although crypto is available on all windows, the returned Crypto object only has one + // > usable feature in insecure contexts: the getRandomValues() method. + // > In general, you should use this API only in secure contexts. + + return crypto.randomUUID.bind(crypto)(); } // prep-work @@ -164,7 +166,7 @@ export function generateUuid() { } // get data - getRandomValues(_data); + crypto.getRandomValues(_data); // set version bits _data[6] = (_data[6] & 0x0f) | 0x40; diff --git a/extensions/ipynb/src/ipynbMain.ts b/extensions/ipynb/src/ipynbMain.ts index e4fe302d1882..cc55d39e112a 100644 --- a/extensions/ipynb/src/ipynbMain.ts +++ b/extensions/ipynb/src/ipynbMain.ts @@ -8,6 +8,7 @@ import { activate as keepNotebookModelStoreInSync } from './notebookModelStoreSy import { notebookImagePasteSetup } from './notebookImagePaste'; import { AttachmentCleaner } from './notebookAttachmentCleaner'; import { serializeNotebookToString } from './serializers'; +import { defaultNotebookFormat } from './constants'; // From {nbformat.INotebookMetadata} in @jupyterlab/coreutils type NotebookMetadata = { @@ -86,8 +87,8 @@ export function activate(context: vscode.ExtensionContext, serializer: vscode.No data.metadata = { cells: [], metadata: {}, - nbformat: 4, - nbformat_minor: 2 + nbformat: defaultNotebookFormat.major, + nbformat_minor: defaultNotebookFormat.minor, }; const doc = await vscode.workspace.openNotebookDocument('jupyter-notebook', data); await vscode.window.showNotebookDocument(doc); diff --git a/extensions/ipynb/src/notebookSerializer.ts b/extensions/ipynb/src/notebookSerializer.ts index 6eb1fab75d80..486ce55b9e74 100644 --- a/extensions/ipynb/src/notebookSerializer.ts +++ b/extensions/ipynb/src/notebookSerializer.ts @@ -8,7 +8,7 @@ import detectIndent from 'detect-indent'; import * as vscode from 'vscode'; import { getPreferredLanguage, jupyterNotebookModelToNotebookData } from './deserializers'; import * as fnv from '@enonic/fnv-plus'; -import { serializeNotebookToString } from './serializers'; +import { serializeNotebookToBytes } from './serializers'; export abstract class NotebookSerializerBase extends vscode.Disposable implements vscode.NotebookSerializer { protected disposed: boolean = false; @@ -81,8 +81,7 @@ export abstract class NotebookSerializerBase extends vscode.Disposable implement return new Uint8Array(0); } - const serialized = serializeNotebookToString(data); - return new TextEncoder().encode(serialized); + return serializeNotebookToBytes(data); } } diff --git a/extensions/ipynb/src/notebookSerializerWorker.ts b/extensions/ipynb/src/notebookSerializerWorker.ts index 594af6da7916..6470132d1a7f 100644 --- a/extensions/ipynb/src/notebookSerializerWorker.ts +++ b/extensions/ipynb/src/notebookSerializerWorker.ts @@ -4,15 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import { parentPort } from 'worker_threads'; -import { serializeNotebookToString } from './serializers'; +import { serializeNotebookToBytes } from './serializers'; import type { NotebookData } from 'vscode'; if (parentPort) { parentPort.on('message', ({ id, data }: { id: string; data: NotebookData }) => { if (parentPort) { - const json = serializeNotebookToString(data); - const bytes = new TextEncoder().encode(json); + const bytes = serializeNotebookToBytes(data); parentPort.postMessage({ id, data: bytes }); } }); diff --git a/extensions/ipynb/src/serializers.ts b/extensions/ipynb/src/serializers.ts index 2d305df2feaf..cbff5d1b2d52 100644 --- a/extensions/ipynb/src/serializers.ts +++ b/extensions/ipynb/src/serializers.ts @@ -465,6 +465,95 @@ export function serializeNotebookToString(data: NotebookData): string { return serializeNotebookToJSON(notebookContent, indentAmount); } + +export function serializeNotebookToBytes(data: NotebookData): Uint8Array { + const notebookContent = getNotebookMetadata(data); + // use the preferred language from document metadata or the first cell language as the notebook preferred cell language + const preferredCellLanguage = notebookContent.metadata?.language_info?.name ?? data.cells.find(cell => cell.kind === 2)?.languageId; + + notebookContent.cells = data.cells + .map(cell => createJupyterCellFromNotebookCell(cell, preferredCellLanguage)) + .map(pruneCell); + + const indentAmount = data.metadata && 'indentAmount' in data.metadata && typeof data.metadata.indentAmount === 'string' ? + data.metadata.indentAmount : + ' '; + + return serializeNotebookToJSONBytes(notebookContent, indentAmount); +} + + +function serializeNotebookToJSONBytes(notebookContent: Partial, indentAmount: string): Uint8Array { + // const sorted = sortObjectPropertiesRecursively(notebookContent); + + // Using JSON.stringify can fail, if the entire size of the resulting JSON string exceeds the maximum size of a single string in JS. + // Hence build the JSON bytes in chunks. + return jsonToBytes(notebookContent, indentAmount); +} + +function jsonToBytes(jsonObject: any, indentAmount: string = ' '): Uint8Array { + const encoder = new TextEncoder(); + const arrays: Uint8Array[] = []; + + function encodeValue(value: any, indent: string): void { + if (typeof value === 'object' && value !== null) { + if (Array.isArray(value)) { + arrays.push(encoder.encode('[')); + if (value.length > 0) { + for (let i = 0; i < value.length; i++) { + if (i > 0) { + arrays.push(encoder.encode(',')); + } + arrays.push(encoder.encode('\n' + indent + indentAmount)); + encodeValue(value[i], indent + indentAmount); + } + arrays.push(encoder.encode('\n' + indent + ']')); + } else { + arrays.push(encoder.encode(']')); + } + } else { + arrays.push(encoder.encode('{')); + // // ipynb always sorts keys in alphabetical order. + const keys = Object.keys(value).sort(); + if (keys.length > 0) { + for (let i = 0; i < keys.length; i++) { + if (i > 0) { + arrays.push(encoder.encode(',')); + } + arrays.push(encoder.encode('\n' + indent + indentAmount + `"${keys[i]}": `)); + encodeValue(value[keys[i]], indent + indentAmount); + } + arrays.push(encoder.encode('\n' + indent + '}')); + } else { + arrays.push(encoder.encode('}')); + } + } + } else if (typeof value === 'string') { + arrays.push(encoder.encode(`"${value}"`)); + } else { + arrays.push(encoder.encode(String(value))); + } + } + + encodeValue(jsonObject, ''); + // ipynb always ends with a trailing new line (we add this so that SCMs do not show unnecessary changes, resulting from a missing trailing new line). + arrays.push(encoder.encode('\n')); + + return concatenateUint8Arrays(arrays); +} + +function concatenateUint8Arrays(arrays: Uint8Array[]): Uint8Array { + const totalLength = arrays.reduce((acc, arr) => acc + arr.length, 0); + const result = new Uint8Array(totalLength); + let offset = 0; + arrays.forEach(arr => { + result.set(arr, offset); + offset += arr.length; + }); + return result; +} + + function serializeNotebookToJSON(notebookContent: Partial, indentAmount: string): string { // ipynb always ends with a trailing new line (we add this so that SCMs do not show unnecessary changes, resulting from a missing trailing new line). const sorted = sortObjectPropertiesRecursively(notebookContent); diff --git a/extensions/ipynb/src/test/notebookModelStoreSync.test.ts b/extensions/ipynb/src/test/notebookModelStoreSync.test.ts index eab921672150..7174678ad61c 100644 --- a/extensions/ipynb/src/test/notebookModelStoreSync.test.ts +++ b/extensions/ipynb/src/test/notebookModelStoreSync.test.ts @@ -101,7 +101,8 @@ suite(`Notebook Model Store Sync`, () => { assert.strictEqual(editsApplied.length, 0); assert.strictEqual(cellMetadataUpdates.length, 0); }); - test('Adding cell will result in an update to the metadata', async () => { + test('Adding cell to nbformat 4.2 notebook will result in adding empty metadata', async () => { + sinon.stub(notebook, 'metadata').get(() => ({ nbformat: 4, nbformat_minor: 2 })); const cell: NotebookCell = { document: {} as any, executionSummary: {}, @@ -131,7 +132,7 @@ suite(`Notebook Model Store Sync`, () => { const newMetadata = cellMetadataUpdates[0].newCellMetadata; assert.deepStrictEqual(newMetadata, { execution_count: null, metadata: {} }); }); - test('Add cell id if nbformat is 4.5', async () => { + test('Added cell will have a cell id if nbformat is 4.5', async () => { sinon.stub(notebook, 'metadata').get(() => ({ nbformat: 4, nbformat_minor: 5 })); const cell: NotebookCell = { document: {} as any, diff --git a/extensions/ipynb/src/test/serializers.test.ts b/extensions/ipynb/src/test/serializers.test.ts index e132b6b2b1d1..237b382ab33a 100644 --- a/extensions/ipynb/src/test/serializers.test.ts +++ b/extensions/ipynb/src/test/serializers.test.ts @@ -8,7 +8,7 @@ import type * as nbformat from '@jupyterlab/nbformat'; import * as assert from 'assert'; import * as vscode from 'vscode'; import { jupyterCellOutputToCellOutput, jupyterNotebookModelToNotebookData } from '../deserializers'; -import { createMarkdownCellFromNotebookCell, getCellMetadata } from '../serializers'; +import { createMarkdownCellFromNotebookCell, getCellMetadata, serializeNotebookToBytes, serializeNotebookToString } from '../serializers'; function deepStripProperties(obj: any, props: string[]) { for (const prop in obj) { @@ -135,6 +135,77 @@ suite(`ipynb serializer`, () => { }); }); + + test.only('JSON serialization (empty notebook)', () => { + const cells: nbformat.ICell[] = [ + ]; + const notebook = jupyterNotebookModelToNotebookData({ cells }, 'python'); + assert.ok(notebook); + + const jsonStr = serializeNotebookToString(notebook); + const bytes = serializeNotebookToBytes(notebook); + + assert.strictEqual(jsonStr, new TextDecoder().decode(bytes)); + }); + + test.only('JSON serialization (empty metadata with cells)', () => { + const cells: nbformat.ICell[] = [ + { + cell_type: 'code', + execution_count: 10, + outputs: [], + source: 'print(1)', + metadata: {} + }, + { + cell_type: 'markdown', + source: '# HEAD', + metadata: {} + } + ]; + const notebook = jupyterNotebookModelToNotebookData({ cells }, 'python'); + assert.ok(notebook); + + const jsonStr = serializeNotebookToString(notebook); + const bytes = serializeNotebookToBytes(notebook); + + assert.strictEqual(jsonStr, new TextDecoder().decode(bytes)); + }); + + test.only('JSON serialization (with metadata containing functions & cells)', () => { + const cells: nbformat.ICell[] = [ + { + cell_type: 'code', + execution_count: 10, + outputs: [], + source: 'print(1)', + metadata: { + jupyter: { + outputs_hidden: true, + scrolled: false + }, + custom: { + test: function () { + return 'Hello'; + } + } as any + } + }, + { + cell_type: 'markdown', + source: '# HEAD', + metadata: {} + } + ]; + const notebook = jupyterNotebookModelToNotebookData({ cells, metadata: { kernelspec: { display_name: 'foo', name: 'bar' }, language_info: { name: 'python', mimetype: 'xyz', file_extension: '.py' }, orig_nbformat: 4 } }, 'python'); + assert.ok(notebook); + + const jsonStr = serializeNotebookToString(notebook); + const bytes = serializeNotebookToBytes(notebook); + + assert.strictEqual(jsonStr, new TextDecoder().decode(bytes)); + }); + suite('Outputs', () => { function validateCellOutputTranslation( outputs: nbformat.IOutput[], @@ -717,4 +788,5 @@ suite(`ipynb serializer`, () => { }); }); }); + }); diff --git a/extensions/jake/package-lock.json b/extensions/jake/package-lock.json index ff50538c25e2..65202510fc94 100644 --- a/extensions/jake/package-lock.json +++ b/extensions/jake/package-lock.json @@ -9,26 +9,28 @@ "version": "1.0.0", "license": "MIT", "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "engines": { "vscode": "*" } }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" } } } diff --git a/extensions/jake/package.json b/extensions/jake/package.json index 1d5d1250db0e..234bc2d3723c 100644 --- a/extensions/jake/package.json +++ b/extensions/jake/package.json @@ -18,7 +18,7 @@ }, "dependencies": {}, "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "main": "./out/main", "activationEvents": [ diff --git a/extensions/javascript/javascript-language-configuration.json b/extensions/javascript/javascript-language-configuration.json index c2444c453b2b..20827982072c 100644 --- a/extensions/javascript/javascript-language-configuration.json +++ b/extensions/javascript/javascript-language-configuration.json @@ -231,7 +231,7 @@ // Add // when pressing enter from inside line comment { "beforeText": { - "pattern": "(?=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, "node_modules/@microsoft/1ds-core-js": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.4.tgz", - "integrity": "sha512-3gbDUQgAO8EoyQTNcAEkxpuPnioC0May13P1l1l0NKZ128L9Ts/sj8QsfwCRTjHz0HThlA+4FptcAJXNYUy3rg==", + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.10.tgz", + "integrity": "sha512-5fSZmkGwWkH+mrIA5M1GYPZdPM+SjXwCCl2Am7VhFoVwOBJNhRnwvIpAdzw6sFjiebN/rz+/YH0NdxztGZSa9Q==", "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" } }, "node_modules/@microsoft/1ds-post-js": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.4.tgz", - "integrity": "sha512-nlKjWricDj0Tn68Dt0P8lX9a+X7LYrqJ6/iSfQwMfDhRIGLqW+wxx8gxS+iGWC/oc8zMQAeiZaemUpCwQcwpRQ==", + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.10.tgz", + "integrity": "sha512-VSLjc9cT+Y+eTiSfYltJHJCejn8oYr0E6Pq2BMhOEO7F6IyLGYIxzKKvo78ze9x+iHX7KPTATcZ+PFgjGXuNqg==", "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "4.3.4", + "@microsoft/1ds-core-js": "4.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" } }, "node_modules/@microsoft/applicationinsights-channel-js": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.4.tgz", - "integrity": "sha512-Z4nrxYwGKP9iyrYtm7iPQXVOFy4FsEsX0nDKkAi96Qpgw+vEh6NH4ORxMMuES0EollBQ3faJyvYCwckuCVIj0g==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.10.tgz", + "integrity": "sha512-iolFLz1ocWAzIQqHIEjjov3gNTPkgFQ4ArHnBcJEYoffOGWlJt6copaevS5YPI5rHzmbySsengZ8cLJJBBrXzQ==", "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-common": "3.3.4", - "@microsoft/applicationinsights-core-js": "3.3.4", + "@microsoft/applicationinsights-common": "3.3.10", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-common": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.4.tgz", - "integrity": "sha512-4ms16MlIvcP4WiUPqopifNxcWCcrXQJ2ADAK/75uok2mNQe6ZNRsqb/P+pvhUxc8A5HRlvoXPP1ptDSN5Girgw==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.10.tgz", + "integrity": "sha512-RVIenPIvNgZCbjJdALvLM4rNHgAFuHI7faFzHCgnI6S2WCUNGHeXlQTs9EUUrL+n2TPp9/cd0KKMILU5VVyYiA==", "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-core-js": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.4.tgz", - "integrity": "sha512-MummANF0mgKIkdvVvfmHQTBliK114IZLRhTL0X0Ep+zjDwWMHqYZgew0nlFKAl6ggu42abPZFK5afpE7qjtYJA==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.10.tgz", + "integrity": "sha512-5yKeyassZTq2l+SAO4npu6LPnbS++UD+M+Ghjm9uRzoBwD8tumFx0/F8AkSVqbniSREd+ztH/2q2foewa2RZyg==", "license": "MIT", "dependencies": { "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { "tslib": ">= 1.0.0" @@ -103,18 +124,18 @@ } }, "node_modules/@microsoft/applicationinsights-web-basic": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.4.tgz", - "integrity": "sha512-OpEPXr8vU/t/M8T9jvWJzJx/pCyygIiR1nGM/2PTde0wn7anl71Gxl5fWol7K/WwFEORNjkL3CEyWOyDc+28AA==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.10.tgz", + "integrity": "sha512-AZib5DAT3NU0VT0nLWEwXrnoMDDgZ/5S4dso01CNU5ELNxLdg+1fvchstlVdMy4FrAnxzs8Wf/GIQNFYOVgpAw==", "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-channel-js": "3.3.4", - "@microsoft/applicationinsights-common": "3.3.4", - "@microsoft/applicationinsights-core-js": "3.3.4", + "@microsoft/applicationinsights-channel-js": "3.3.10", + "@microsoft/applicationinsights-common": "3.3.10", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { "tslib": ">= 1.0.0" @@ -139,67 +160,45 @@ } }, "node_modules/@nevware21/ts-utils": { - "version": "0.11.6", - "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.11.6.tgz", - "integrity": "sha512-OUUJTh3fnaUSzg9DEHgv3d7jC+DnPL65mIO7RaR+jWve7+MmcgIvF79gY97DPQ4frH+IpNR78YAYd/dW4gK3kg==", + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.12.5.tgz", + "integrity": "sha512-JPQZWPKQJjj7kAftdEZL0XDFfbMgXCGiUAZe0d7EhLC3QlXTlZdSckGqqRIQ2QNl0VTEZyZUvRBw6Ednw089Fw==", "license": "MIT" }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/@vscode/extension-telemetry": { - "version": "0.9.8", - "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.8.tgz", - "integrity": "sha512-7YcKoUvmHlIB8QYCE4FNzt3ErHi9gQPhdCM3ZWtpw1bxPT0I+lMdx52KHlzTNoJzQ2NvMX7HyzyDwBEiMgTrWQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-1.4.0.tgz", + "integrity": "sha512-mqypuqIDn+vQnaQ8tMdvCRn9BprZTH2j6nlFFv9hE6hB25n32F8rOZinos8OgblRi87X1qdBpjpb0Em1LGSG9w==", "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "^4.3.4", - "@microsoft/1ds-post-js": "^4.3.4", - "@microsoft/applicationinsights-web-basic": "^3.3.4" + "@microsoft/1ds-core-js": "^4.3.10", + "@microsoft/1ds-post-js": "^4.3.10", + "@microsoft/applicationinsights-web-basic": "^3.3.10" }, "engines": { "vscode": "^1.75.0" } }, - "node_modules/balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c= sha512-9Y0g0Q8rmSt+H33DfKv7FOc3v+iRI+o1lbzt8jGcIosYW37IIW/2XVYq5NPdmaD5NQ59Nk26Kl/vZbwW9Fr8vg==" - }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, "node_modules/minimatch": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", - "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^2.0.1" + "@isaacs/brace-expansion": "^5.0.0" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -211,12 +210,10 @@ "integrity": "sha512-bH6E4PMmsEXYrLX6Kr1vu+xI3HproB1vECAwaPSJeroLE1kpWE3HR27uB4icx+6YORu1ajqBJXxuedv8ZQg5Lw==" }, "node_modules/semver": { - "version": "7.6.0", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz", - "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==", - "dependencies": { - "lru-cache": "^6.0.0" - }, + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -225,51 +222,50 @@ } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" }, "node_modules/vscode-jsonrpc": { - "version": "9.0.0-next.6", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.6.tgz", - "integrity": "sha512-KCSvUNsFiVciG9iqjJKBZOd66CN3ZKohDlYRmoOi+pd8l15MFLZ8wRG4c+wuzePGba/8WcCG2TM+C/GVlvuaeA==", + "version": "9.0.0-next.11", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.11.tgz", + "integrity": "sha512-u6LElQNbSiE9OugEEmrUKwH6+8BpPz2S5MDHvQUqHL//I4Q8GPikKLOUf856UnbLkZdhxaPrExac1lA3XwpIPA==", + "license": "MIT", "engines": { "node": ">=14.0.0" } }, "node_modules/vscode-languageclient": { - "version": "10.0.0-next.13", - "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-10.0.0-next.13.tgz", - "integrity": "sha512-KLsOMJoYpkk36PIgcOjyZ4AekOfzp4kdWdRRbVKeVvSIrwrn/4RSZr0NlD6EvUBBJSsJW4WDrYY7Y3znkqa6+w==", + "version": "10.0.0-next.19", + "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-10.0.0-next.19.tgz", + "integrity": "sha512-sJtO8y0Dxs4ue/DK0QgO/ATBfZwQdee3TqvCsoqUej/GZrBA01DTf4pbfswRxHsTxN2yH0haImUnMafWHtE4CQ==", "license": "MIT", "dependencies": { - "minimatch": "^9.0.3", - "semver": "^7.6.0", - "vscode-languageserver-protocol": "3.17.6-next.11" + "minimatch": "^10.0.3", + "semver": "^7.7.1", + "vscode-languageserver-protocol": "3.17.6-next.16" }, "engines": { "vscode": "^1.91.0" } }, "node_modules/vscode-languageserver-protocol": { - "version": "3.17.6-next.11", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.11.tgz", - "integrity": "sha512-GeJxEp1TiLsp79f8WG5n10wLViXfgFKb99hU9K8m7KDWM95/QFEqWkm79f9LVm54tUK74I91a9EeiQLCS/FABQ==", + "version": "3.17.6-next.16", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.16.tgz", + "integrity": "sha512-kQTjXEuyxMbdmmZ3U+Lib3oUl12xEKNc73RtWxPSDS3TFtjVwt98Q1CUzfDA9EUpsA24M46Bl6q3sLe9AUOKyw==", + "license": "MIT", "dependencies": { - "vscode-jsonrpc": "9.0.0-next.6", - "vscode-languageserver-types": "3.17.6-next.5" + "vscode-jsonrpc": "9.0.0-next.11", + "vscode-languageserver-types": "3.17.6-next.6" } }, "node_modules/vscode-languageserver-types": { - "version": "3.17.6-next.5", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.5.tgz", - "integrity": "sha512-QFmf3Yl1tCgUQfA77N9Me/LXldJXkIVypQbty2rJ1DNHQkC+iwvm4Z2tXg9czSwlhvv0pD4pbF5mT7WhAglolw==" - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "version": "3.17.6-next.6", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.6.tgz", + "integrity": "sha512-aiJY5/yW+xzw7KPNlwi3gQtddq/3EIn5z8X8nCgJfaiAij2R1APKePngv+MUdLdYJBVTLu+Qa0ODsT+pHgYguQ==", + "license": "MIT" } } } diff --git a/extensions/json-language-features/package.json b/extensions/json-language-features/package.json index cf4a7f162dab..4bcef4b8e5d0 100644 --- a/extensions/json-language-features/package.json +++ b/extensions/json-language-features/package.json @@ -167,12 +167,12 @@ ] }, "dependencies": { - "@vscode/extension-telemetry": "^0.9.8", + "@vscode/extension-telemetry": "^1.4.0", "request-light": "^0.8.0", - "vscode-languageclient": "^10.0.0-next.13" + "vscode-languageclient": "^10.0.0-next.19" }, "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "repository": { "type": "git", diff --git a/extensions/json-language-features/server/package-lock.json b/extensions/json-language-features/server/package-lock.json index 384ce045c9c6..2f4201db9e41 100644 --- a/extensions/json-language-features/server/package-lock.json +++ b/extensions/json-language-features/server/package-lock.json @@ -20,26 +20,28 @@ "vscode-json-languageserver": "bin/vscode-json-languageserver" }, "devDependencies": { - "@types/mocha": "^9.1.1", - "@types/node": "20.x" + "@types/mocha": "^10.0.10", + "@types/node": "25.x" }, "engines": { "node": "*" } }, "node_modules/@types/mocha": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", - "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==", - "dev": true + "version": "10.0.10", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz", + "integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==", + "dev": true, + "license": "MIT" }, "node_modules/@types/node": { - "version": "20.12.5", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.12.5.tgz", - "integrity": "sha512-BD+BjQ9LS/D8ST9p5uqBxghlN+S42iuNxjsUGjeZobe/ciXzk2qb1B6IXc6AnRLS+yFJRpN2IPEHMzwspfDJNw==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/@vscode/l10n": { @@ -58,57 +60,61 @@ "integrity": "sha512-bH6E4PMmsEXYrLX6Kr1vu+xI3HproB1vECAwaPSJeroLE1kpWE3HR27uB4icx+6YORu1ajqBJXxuedv8ZQg5Lw==" }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" }, "node_modules/vscode-json-languageservice": { - "version": "5.4.3", - "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-5.4.3.tgz", - "integrity": "sha512-NVSEQDloP9NYccuqKg4eI46kutZpwucBY4csBB6FCxbM7AZVoBt0oxTItPVA+ZwhnG1bg/fmiBRAwcGJyNQoPA==", + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/vscode-json-languageservice/-/vscode-json-languageservice-5.7.1.tgz", + "integrity": "sha512-sMK2F8p7St0lJCr/4IfbQRoEUDUZRR7Ud0IiSl8I/JtN+m9Gv+FJlNkSAYns2R7Ebm/PKxqUuWYOfBej/rAdBQ==", "license": "MIT", "dependencies": { "@vscode/l10n": "^0.0.18", "jsonc-parser": "^3.3.1", "vscode-languageserver-textdocument": "^1.0.12", "vscode-languageserver-types": "^3.17.5", - "vscode-uri": "^3.0.8" + "vscode-uri": "^3.1.0" } }, "node_modules/vscode-jsonrpc": { - "version": "9.0.0-next.6", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.6.tgz", - "integrity": "sha512-KCSvUNsFiVciG9iqjJKBZOd66CN3ZKohDlYRmoOi+pd8l15MFLZ8wRG4c+wuzePGba/8WcCG2TM+C/GVlvuaeA==", + "version": "9.0.0-next.11", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-9.0.0-next.11.tgz", + "integrity": "sha512-u6LElQNbSiE9OugEEmrUKwH6+8BpPz2S5MDHvQUqHL//I4Q8GPikKLOUf856UnbLkZdhxaPrExac1lA3XwpIPA==", + "license": "MIT", "engines": { "node": ">=14.0.0" } }, "node_modules/vscode-languageserver": { - "version": "10.0.0-next.11", - "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-10.0.0-next.11.tgz", - "integrity": "sha512-cmobSrVDYhlh/t02vz/bV8nNpds8mus5HnILULae2iAvOjoaJPnTAp0jJWoYdUqTpIVzT9JV6JMKqLEvdqpeqg==", + "version": "10.0.0-next.16", + "resolved": "https://registry.npmjs.org/vscode-languageserver/-/vscode-languageserver-10.0.0-next.16.tgz", + "integrity": "sha512-RbsYDOhddv1NtBCAR7+oVxxCmOpQUHhrtgUE0xz6J+BJGSCkfOqBCyLUIwSjKk2rK9llxUj/pR5aL8QCsXrxow==", "license": "MIT", "dependencies": { - "vscode-languageserver-protocol": "3.17.6-next.11" + "vscode-languageserver-protocol": "3.17.6-next.16" }, "bin": { "installServerIntoExtension": "bin/installServerIntoExtension" } }, "node_modules/vscode-languageserver-protocol": { - "version": "3.17.6-next.11", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.11.tgz", - "integrity": "sha512-GeJxEp1TiLsp79f8WG5n10wLViXfgFKb99hU9K8m7KDWM95/QFEqWkm79f9LVm54tUK74I91a9EeiQLCS/FABQ==", + "version": "3.17.6-next.16", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.6-next.16.tgz", + "integrity": "sha512-kQTjXEuyxMbdmmZ3U+Lib3oUl12xEKNc73RtWxPSDS3TFtjVwt98Q1CUzfDA9EUpsA24M46Bl6q3sLe9AUOKyw==", + "license": "MIT", "dependencies": { - "vscode-jsonrpc": "9.0.0-next.6", - "vscode-languageserver-types": "3.17.6-next.5" + "vscode-jsonrpc": "9.0.0-next.11", + "vscode-languageserver-types": "3.17.6-next.6" } }, "node_modules/vscode-languageserver-protocol/node_modules/vscode-languageserver-types": { - "version": "3.17.6-next.5", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.5.tgz", - "integrity": "sha512-QFmf3Yl1tCgUQfA77N9Me/LXldJXkIVypQbty2rJ1DNHQkC+iwvm4Z2tXg9czSwlhvv0pD4pbF5mT7WhAglolw==" + "version": "3.17.6-next.6", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.6-next.6.tgz", + "integrity": "sha512-aiJY5/yW+xzw7KPNlwi3gQtddq/3EIn5z8X8nCgJfaiAij2R1APKePngv+MUdLdYJBVTLu+Qa0ODsT+pHgYguQ==", + "license": "MIT" }, "node_modules/vscode-languageserver-textdocument": { "version": "1.0.12", @@ -121,9 +127,10 @@ "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==" }, "node_modules/vscode-uri": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", - "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "license": "MIT" } } } diff --git a/extensions/json-language-features/server/package.json b/extensions/json-language-features/server/package.json index 6dcd82930d23..cd0a660c53b9 100644 --- a/extensions/json-language-features/server/package.json +++ b/extensions/json-language-features/server/package.json @@ -20,8 +20,8 @@ "vscode-uri": "^3.0.8" }, "devDependencies": { - "@types/mocha": "^9.1.1", - "@types/node": "20.x" + "@types/mocha": "^10.0.10", + "@types/node": "25.x" }, "scripts": { "prepublishOnly": "npm run clean && npm run compile", diff --git a/extensions/json/build/update-grammars.js b/extensions/json/build/update-grammars.js index 2b7f76f8f909..13356a2c4c4e 100644 --- a/extensions/json/build/update-grammars.js +++ b/extensions/json/build/update-grammars.js @@ -9,7 +9,7 @@ var updateGrammar = require('vscode-grammar-updater'); function adaptJSON(grammar, name, replacementScope, replaceeScope = 'json') { grammar.name = name; grammar.scopeName = `source${replacementScope}`; - const regex = new RegExp(`\.${replaceeScope}`, 'g'); + const regex = new RegExp(`\\.${replaceeScope}`, 'g'); var fixScopeNames = function (rule) { if (typeof rule.name === 'string') { rule.name = rule.name.replace(regex, replacementScope); diff --git a/extensions/latex/cgmanifest.json b/extensions/latex/cgmanifest.json index c798b2aedd75..d937ba4f4304 100644 --- a/extensions/latex/cgmanifest.json +++ b/extensions/latex/cgmanifest.json @@ -6,11 +6,11 @@ "git": { "name": "jlelong/vscode-latex-basics", "repositoryUrl": "https://github.com/jlelong/vscode-latex-basics", - "commitHash": "dfa69a16a1154dbc820dc1111d72faa6954dd1e2" + "commitHash": "df6ef817c932d24da5cc72927344a547e463cc65" } }, "license": "MIT", - "version": "1.10.0", + "version": "1.9.0", "description": "The files in syntaxes/ were originally part of https://github.com/James-Yu/LaTeX-Workshop. They have been extracted in the hope that they can useful outside of the LaTeX-Workshop extension.", "licenseDetail": [ "Copyright (c) vscode-latex-basics authors", diff --git a/extensions/latex/syntaxes/LaTeX.tmLanguage.json b/extensions/latex/syntaxes/LaTeX.tmLanguage.json index d7201e8c652f..06c4c59c60e8 100644 --- a/extensions/latex/syntaxes/LaTeX.tmLanguage.json +++ b/extensions/latex/syntaxes/LaTeX.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/jlelong/vscode-latex-basics/commit/7a35f5e0f19b28f5f1366579e2a9ad34df4f40c9", + "version": "https://github.com/jlelong/vscode-latex-basics/commit/7b75bae583f3f9802c533e021f882428872c572c", "name": "LaTeX", "scopeName": "text.tex.latex", "patterns": [ @@ -2349,16 +2349,16 @@ ] } }, - "contentName": "meta.embedded.markdown_latex_combined", + "contentName": "meta.embedded.internal_only_markdown_latex_combined", "end": "(\\\\end\\{markdown\\})", "patterns": [ { - "include": "text.tex.markdown_latex_combined" + "include": "text.tex.internal_only_markdown_latex_combined" } ] }, { - "begin": "(\\s*\\\\begin\\{(\\w+\\*?)\\})", + "begin": "(\\s*\\\\begin\\{(\\p{Alphabetic}+\\*?)\\})", "captures": { "1": { "patterns": [ @@ -3192,7 +3192,7 @@ "name": "punctuation.definition.arguments.end.latex" } }, - "match": "\\s*((\\\\)(?:begin|end))(\\{)([a-zA-Z]*\\*?)(\\})(?:(\\[)([^\\]]*)(\\])){,2}(?:(\\{)([^{}]*)(\\}))?" + "match": "\\s*((\\\\)(?:begin|end))(\\{)(\\p{Alphabetic}+\\*?)(\\})(?:(\\[)([^\\]]*)(\\])){,2}(?:(\\{)([^{}]*)(\\}))?" }, "definition-label": { "begin": "((\\\\)z?label)((?:\\[[^\\[]*?\\])*)(\\{)", diff --git a/extensions/latex/syntaxes/markdown-latex-combined.tmLanguage.json b/extensions/latex/syntaxes/markdown-latex-combined.tmLanguage.json index 566ede8e6c36..2211906fc7b3 100644 --- a/extensions/latex/syntaxes/markdown-latex-combined.tmLanguage.json +++ b/extensions/latex/syntaxes/markdown-latex-combined.tmLanguage.json @@ -4,9 +4,9 @@ "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/jlelong/vscode-latex-basics/commit/dfa69a16a1154dbc820dc1111d72faa6954dd1e2", + "version": "https://github.com/jlelong/vscode-latex-basics/commit/ed0a6d72377d89eb90280f4159f2d6cdbd63a3a3", "name": "Markdown", - "scopeName": "text.tex.markdown_latex_combined", + "scopeName": "text.tex.internal_only_markdown_latex_combined", "patterns": [ { "include": "text.tex.latex" diff --git a/extensions/markdown-language-features/package-lock.json b/extensions/markdown-language-features/package-lock.json index 63d93c4070a0..1a3a0f589b93 100644 --- a/extensions/markdown-language-features/package-lock.json +++ b/extensions/markdown-language-features/package-lock.json @@ -9,103 +9,103 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@vscode/extension-telemetry": "^0.9.8", + "@vscode/extension-telemetry": "^1.4.0", "dompurify": "^3.2.4", - "highlight.js": "^11.8.0", - "markdown-it": "^12.3.2", + "highlight.js": "^11.11.1", + "markdown-it": "^14.1.0", "markdown-it-front-matter": "^0.2.4", - "morphdom": "^2.6.1", - "picomatch": "^2.3.1", + "morphdom": "^2.7.8", + "picomatch": "^4.0.3", "punycode": "^2.3.1", - "vscode-languageclient": "^8.0.2", - "vscode-languageserver-textdocument": "^1.0.11", - "vscode-markdown-languageserver": "^0.5.0-alpha.9", - "vscode-uri": "^3.0.3" + "vscode-languageclient": "^9.0.1", + "vscode-languageserver-textdocument": "^1.0.12", + "vscode-markdown-languageserver": "^0.5.0-alpha.12", + "vscode-uri": "^3.1.0" }, "devDependencies": { - "@types/dompurify": "^3.0.5", - "@types/lodash.throttle": "^4.1.3", - "@types/markdown-it": "12.2.3", - "@types/picomatch": "^2.3.0", - "@types/vscode-notebook-renderer": "^1.60.0", - "@types/vscode-webview": "^1.57.0", - "@vscode/markdown-it-katex": "^1.1.1", + "@types/dompurify": "^3.2.0", + "@types/lodash.throttle": "^4.1.9", + "@types/markdown-it": "14.1.2", + "@types/picomatch": "^4.0.2", + "@types/vscode-notebook-renderer": "^1.72.4", + "@types/vscode-webview": "^1.57.5", + "@vscode/markdown-it-katex": "^1.1.2", "lodash.throttle": "^4.1.1", - "vscode-languageserver-types": "^3.17.2", - "vscode-markdown-languageservice": "^0.3.0-alpha.3" + "vscode-languageserver-types": "^3.17.5", + "vscode-markdown-languageservice": "^0.4.0" }, "engines": { "vscode": "^1.70.0" } }, "node_modules/@microsoft/1ds-core-js": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.4.tgz", - "integrity": "sha512-3gbDUQgAO8EoyQTNcAEkxpuPnioC0May13P1l1l0NKZ128L9Ts/sj8QsfwCRTjHz0HThlA+4FptcAJXNYUy3rg==", + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.10.tgz", + "integrity": "sha512-5fSZmkGwWkH+mrIA5M1GYPZdPM+SjXwCCl2Am7VhFoVwOBJNhRnwvIpAdzw6sFjiebN/rz+/YH0NdxztGZSa9Q==", "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" } }, "node_modules/@microsoft/1ds-post-js": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.4.tgz", - "integrity": "sha512-nlKjWricDj0Tn68Dt0P8lX9a+X7LYrqJ6/iSfQwMfDhRIGLqW+wxx8gxS+iGWC/oc8zMQAeiZaemUpCwQcwpRQ==", + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.10.tgz", + "integrity": "sha512-VSLjc9cT+Y+eTiSfYltJHJCejn8oYr0E6Pq2BMhOEO7F6IyLGYIxzKKvo78ze9x+iHX7KPTATcZ+PFgjGXuNqg==", "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "4.3.4", + "@microsoft/1ds-core-js": "4.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" } }, "node_modules/@microsoft/applicationinsights-channel-js": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.4.tgz", - "integrity": "sha512-Z4nrxYwGKP9iyrYtm7iPQXVOFy4FsEsX0nDKkAi96Qpgw+vEh6NH4ORxMMuES0EollBQ3faJyvYCwckuCVIj0g==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.10.tgz", + "integrity": "sha512-iolFLz1ocWAzIQqHIEjjov3gNTPkgFQ4ArHnBcJEYoffOGWlJt6copaevS5YPI5rHzmbySsengZ8cLJJBBrXzQ==", "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-common": "3.3.4", - "@microsoft/applicationinsights-core-js": "3.3.4", + "@microsoft/applicationinsights-common": "3.3.10", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-common": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.4.tgz", - "integrity": "sha512-4ms16MlIvcP4WiUPqopifNxcWCcrXQJ2ADAK/75uok2mNQe6ZNRsqb/P+pvhUxc8A5HRlvoXPP1ptDSN5Girgw==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.10.tgz", + "integrity": "sha512-RVIenPIvNgZCbjJdALvLM4rNHgAFuHI7faFzHCgnI6S2WCUNGHeXlQTs9EUUrL+n2TPp9/cd0KKMILU5VVyYiA==", "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-core-js": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.4.tgz", - "integrity": "sha512-MummANF0mgKIkdvVvfmHQTBliK114IZLRhTL0X0Ep+zjDwWMHqYZgew0nlFKAl6ggu42abPZFK5afpE7qjtYJA==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.10.tgz", + "integrity": "sha512-5yKeyassZTq2l+SAO4npu6LPnbS++UD+M+Ghjm9uRzoBwD8tumFx0/F8AkSVqbniSREd+ztH/2q2foewa2RZyg==", "license": "MIT", "dependencies": { "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { "tslib": ">= 1.0.0" @@ -121,18 +121,18 @@ } }, "node_modules/@microsoft/applicationinsights-web-basic": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.4.tgz", - "integrity": "sha512-OpEPXr8vU/t/M8T9jvWJzJx/pCyygIiR1nGM/2PTde0wn7anl71Gxl5fWol7K/WwFEORNjkL3CEyWOyDc+28AA==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.10.tgz", + "integrity": "sha512-AZib5DAT3NU0VT0nLWEwXrnoMDDgZ/5S4dso01CNU5ELNxLdg+1fvchstlVdMy4FrAnxzs8Wf/GIQNFYOVgpAw==", "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-channel-js": "3.3.4", - "@microsoft/applicationinsights-common": "3.3.4", - "@microsoft/applicationinsights-core-js": "3.3.4", + "@microsoft/applicationinsights-channel-js": "3.3.10", + "@microsoft/applicationinsights-common": "3.3.10", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { "tslib": ">= 1.0.0" @@ -157,18 +157,20 @@ } }, "node_modules/@nevware21/ts-utils": { - "version": "0.11.6", - "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.11.6.tgz", - "integrity": "sha512-OUUJTh3fnaUSzg9DEHgv3d7jC+DnPL65mIO7RaR+jWve7+MmcgIvF79gY97DPQ4frH+IpNR78YAYd/dW4gK3kg==", + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.12.5.tgz", + "integrity": "sha512-JPQZWPKQJjj7kAftdEZL0XDFfbMgXCGiUAZe0d7EhLC3QlXTlZdSckGqqRIQ2QNl0VTEZyZUvRBw6Ednw089Fw==", "license": "MIT" }, "node_modules/@types/dompurify": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.0.5.tgz", - "integrity": "sha512-1Wg0g3BtQF7sSb27fJQAKck1HECM6zV1EB66j8JH9i3LCjYabJa0FSdiSgsD5K/RbrsR0SiraKacLB+T8ZVYAg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/@types/dompurify/-/dompurify-3.2.0.tgz", + "integrity": "sha512-Fgg31wv9QbLDA0SpTOXO3MaxySc4DKGLi8sna4/Utjo4r3ZRPdCt4UQee8BWr+Q5z21yifghREPJGYaEOEIACg==", + "deprecated": "This is a stub types definition. dompurify provides its own type definitions, so you do not need this installed.", "dev": true, + "license": "MIT", "dependencies": { - "@types/trusted-types": "*" + "dompurify": "*" } }, "node_modules/@types/linkify-it": { @@ -184,22 +186,24 @@ "dev": true }, "node_modules/@types/lodash.throttle": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/@types/lodash.throttle/-/lodash.throttle-4.1.3.tgz", - "integrity": "sha512-FUm7uMuYRX7dzqmgX02bxdBwC75owUxGA4dDKtFePDLJ6N1ofXxkRX3NhJV8wOrNs/wCjaY6sDVJrD1lbyERoQ==", + "version": "4.1.9", + "resolved": "https://registry.npmjs.org/@types/lodash.throttle/-/lodash.throttle-4.1.9.tgz", + "integrity": "sha512-PCPVfpfueguWZQB7pJQK890F2scYKoDUL3iM522AptHWn7d5NQmeS/LTEHIcLr5PaTzl3dK2Z0xSUHHTHwaL5g==", "dev": true, + "license": "MIT", "dependencies": { "@types/lodash": "*" } }, "node_modules/@types/markdown-it": { - "version": "12.2.3", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-12.2.3.tgz", - "integrity": "sha512-GKMHFfv3458yYy+v/N8gjufHO6MSZKCOXpZc5GXIWWy8uldwfmPn98vp81gZ5f9SVw8YYBctgfJ22a2d7AOMeQ==", + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", "dev": true, + "license": "MIT", "dependencies": { - "@types/linkify-it": "*", - "@types/mdurl": "*" + "@types/linkify-it": "^5", + "@types/mdurl": "^2" } }, "node_modules/@types/mdurl": { @@ -209,39 +213,42 @@ "dev": true }, "node_modules/@types/picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/@types/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-O397rnSS9iQI4OirieAtsDqvCj4+3eY1J+EPdNTKuHuRWIfUoGyzX294o8C4KJYaLqgSrd2o60c5EqCU8Zv02g==", - "dev": true + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@types/picomatch/-/picomatch-4.0.2.tgz", + "integrity": "sha512-qHHxQ+P9PysNEGbALT8f8YOSHW0KJu6l2xU8DYY0fu/EmGxXdVnuTLvFUvBgPJMSqXq29SYHveejeAha+4AYgA==", + "dev": true, + "license": "MIT" }, "node_modules/@types/trusted-types": { "version": "2.0.7", "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", - "devOptional": true, - "license": "MIT" + "license": "MIT", + "optional": true }, "node_modules/@types/vscode-notebook-renderer": { - "version": "1.60.0", - "resolved": "https://registry.npmjs.org/@types/vscode-notebook-renderer/-/vscode-notebook-renderer-1.60.0.tgz", - "integrity": "sha512-u7TD2uuEZTVuitx0iijOJdKI0JLiQP6PsSBSRy2XmHXUOXcp5p1S56NrjOEDoF+PIHd3NL3eO6KTRSf5nukDqQ==", - "dev": true + "version": "1.72.4", + "resolved": "https://registry.npmjs.org/@types/vscode-notebook-renderer/-/vscode-notebook-renderer-1.72.4.tgz", + "integrity": "sha512-bdKO41c6Dc24pH/O/eM/jqfCwGH4zc76o/g/6Gt1y/eg/bvvqP2/VpbV+Sa5Te2sZekFRcbYnSSFTKo3wcVGUg==", + "dev": true, + "license": "MIT" }, "node_modules/@types/vscode-webview": { - "version": "1.57.0", - "resolved": "https://registry.npmjs.org/@types/vscode-webview/-/vscode-webview-1.57.0.tgz", - "integrity": "sha512-x3Cb/SMa1IwRHfSvKaZDZOTh4cNoG505c3NjTqGlMC082m++x/ETUmtYniDsw6SSmYzZXO8KBNhYxR0+VqymqA==", - "dev": true + "version": "1.57.5", + "resolved": "https://registry.npmjs.org/@types/vscode-webview/-/vscode-webview-1.57.5.tgz", + "integrity": "sha512-iBAUYNYkz+uk1kdsq05fEcoh8gJmwT3lqqFPN7MGyjQ3HVloViMdo7ZJ8DFIP8WOK74PjOEilosqAyxV2iUFUw==", + "dev": true, + "license": "MIT" }, "node_modules/@vscode/extension-telemetry": { - "version": "0.9.8", - "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.8.tgz", - "integrity": "sha512-7YcKoUvmHlIB8QYCE4FNzt3ErHi9gQPhdCM3ZWtpw1bxPT0I+lMdx52KHlzTNoJzQ2NvMX7HyzyDwBEiMgTrWQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-1.4.0.tgz", + "integrity": "sha512-mqypuqIDn+vQnaQ8tMdvCRn9BprZTH2j6nlFFv9hE6hB25n32F8rOZinos8OgblRi87X1qdBpjpb0Em1LGSG9w==", "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "^4.3.4", - "@microsoft/1ds-post-js": "^4.3.4", - "@microsoft/applicationinsights-web-basic": "^3.3.4" + "@microsoft/1ds-core-js": "^4.3.10", + "@microsoft/1ds-post-js": "^4.3.10", + "@microsoft/applicationinsights-web-basic": "^3.3.10" }, "engines": { "vscode": "^1.75.0" @@ -253,9 +260,9 @@ "integrity": "sha512-ukOMWnCg1tCvT7WnDfsUKQOFDQGsyR5tNgRpwmqi+5/vzU3ghdDXzvIM4IOPdSb3OeSsBNvmSL8nxIVOqi2WXA==" }, "node_modules/@vscode/markdown-it-katex": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@vscode/markdown-it-katex/-/markdown-it-katex-1.1.1.tgz", - "integrity": "sha512-3KTlbsRBPJQLE2YmLL7K6nunTlU+W9T5+FjfNdWuIUKgxSS6HWLQHaO3L4MkJi7z7MpIPpY+g4N+cWNBPE/MSA==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@vscode/markdown-it-katex/-/markdown-it-katex-1.1.2.tgz", + "integrity": "sha512-+4IIv5PgrmhKvW/3LpkpkGg257OViEhXkOOgCyj5KMsjsOfnRXkni8XAuuF9Ui5p3B8WnUovlDXAQNb8RJ/RaQ==", "dev": true, "license": "MIT", "dependencies": { @@ -270,7 +277,8 @@ "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", - "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" + "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", + "license": "MIT" }, "node_modules/boolbase": { "version": "1.0.0", @@ -278,12 +286,12 @@ "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==" }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "license": "MIT", "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" + "balanced-match": "^1.0.0" } }, "node_modules/commander": { @@ -296,11 +304,6 @@ "node": ">= 12" } }, - "node_modules/concat-map": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" - }, "node_modules/css-select": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.1.0.tgz", @@ -366,9 +369,9 @@ } }, "node_modules/dompurify": { - "version": "3.2.4", - "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.2.4.tgz", - "integrity": "sha512-ysFSFEDVduQpyhzAob/kkuJjf5zWkZD8/A9ywSp1byueyuCfHamrCBa14/Oc2iiB0e51B+NpxSl5gmzn+Ms/mg==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.1.tgz", + "integrity": "sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==", "license": "(MPL-2.0 OR Apache-2.0)", "optionalDependencies": { "@types/trusted-types": "^2.0.7" @@ -407,9 +410,10 @@ } }, "node_modules/highlight.js": { - "version": "11.8.0", - "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.8.0.tgz", - "integrity": "sha512-MedQhoqVdr0U6SSnWPzfiadUcDHfN/Wzq25AkXiQv9oiOO/sG0S7XkvpFIqWBl9Yq1UYyYOOVORs5UW2XlPyzg==", + "version": "11.11.1", + "resolved": "https://registry.npmjs.org/highlight.js/-/highlight.js-11.11.1.tgz", + "integrity": "sha512-Xwwo44whKBVCYoliBQwaPvtd/2tYFkRQtXDWj1nackaV2JPXx3L0+Jvd8/qCJ2p+ML0/XVkJ2q+Mr+UVdpJK5w==", + "license": "BSD-3-Clause", "engines": { "node": ">=12.0.0" } @@ -432,11 +436,12 @@ } }, "node_modules/linkify-it": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", - "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-5aHCbzQRADcdP+ATqnDuhhJ/MRIqDkZX5pyjFHRRysS8vZ5AbqGEoFIb6pYHPZ+L/OC2Lc+xT8uHVVR5CAK/wQ==", + "license": "MIT", "dependencies": { - "uc.micro": "^1.0.1" + "uc.micro": "^2.0.0" } }, "node_modules/lodash.throttle": { @@ -457,18 +462,20 @@ } }, "node_modules/markdown-it": { - "version": "12.3.2", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", - "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", + "license": "MIT", "dependencies": { "argparse": "^2.0.1", - "entities": "~2.1.0", - "linkify-it": "^3.0.1", - "mdurl": "^1.0.1", - "uc.micro": "^1.0.5" + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" }, "bin": { - "markdown-it": "bin/markdown-it.js" + "markdown-it": "bin/markdown-it.mjs" } }, "node_modules/markdown-it-front-matter": { @@ -476,34 +483,29 @@ "resolved": "https://registry.npmjs.org/markdown-it-front-matter/-/markdown-it-front-matter-0.2.4.tgz", "integrity": "sha512-25GUs0yjS2hLl8zAemVndeEzThB1p42yxuDEKbd4JlL3jiz+jsm6e56Ya8B0VREOkNxLYB4TTwaoPJ3ElMmW+w==" }, - "node_modules/markdown-it/node_modules/entities": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", - "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", - "funding": { - "url": "https://github.com/fb55/entities?sponsor=1" - } - }, "node_modules/mdurl": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", - "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==", + "license": "MIT" }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "license": "ISC", "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.1" }, "engines": { - "node": "*" + "node": ">=10" } }, "node_modules/morphdom": { - "version": "2.6.1", - "resolved": "https://registry.npmjs.org/morphdom/-/morphdom-2.6.1.tgz", - "integrity": "sha512-Y8YRbAEP3eKykroIBWrjcfMw7mmwJfjhqdpSvoqinu8Y702nAwikpXcNFDiIkyvfCLxLM9Wu95RZqo4a9jFBaA==" + "version": "2.7.8", + "resolved": "https://registry.npmjs.org/morphdom/-/morphdom-2.7.8.tgz", + "integrity": "sha512-D/fR4xgGUyVRbdMGU6Nejea1RFzYxYtyurG4Fbv2Fi/daKlWKuXGLOdXtl+3eIwL110cI2hz1ZojGICjjFLgTg==", + "license": "MIT" }, "node_modules/node-html-parser": { "version": "6.1.13", @@ -526,11 +528,12 @@ } }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "license": "MIT", "engines": { - "node": ">=8.6" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" @@ -544,6 +547,15 @@ "node": ">=6" } }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "license": "MIT", + "engines": { + "node": ">=6" + } + }, "node_modules/semver": { "version": "7.5.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", @@ -559,29 +571,32 @@ } }, "node_modules/uc.micro": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", - "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==" + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "license": "MIT" }, "node_modules/vscode-jsonrpc": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.0.2.tgz", - "integrity": "sha512-RY7HwI/ydoC1Wwg4gJ3y6LpU9FJRZAUnTYMXthqhFXXu77ErDd/xkREpGuk4MyYkk4a+XDWAMqe0S3KkelYQEQ==", + "version": "8.2.0", + "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", + "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", + "license": "MIT", "engines": { "node": ">=14.0.0" } }, "node_modules/vscode-languageclient": { - "version": "8.0.2", - "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-8.0.2.tgz", - "integrity": "sha512-lHlthJtphG9gibGb/y72CKqQUxwPsMXijJVpHEC2bvbFqxmkj9LwQ3aGU9dwjBLqsX1S4KjShYppLvg1UJDF/Q==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-9.0.1.tgz", + "integrity": "sha512-JZiimVdvimEuHh5olxhxkht09m3JzUGwggb5eRUkzzJhZ2KjCN0nh55VfiED9oez9DyF8/fz1g1iBV3h+0Z2EA==", + "license": "MIT", "dependencies": { - "minimatch": "^3.0.4", - "semver": "^7.3.5", - "vscode-languageserver-protocol": "3.17.2" + "minimatch": "^5.1.0", + "semver": "^7.3.7", + "vscode-languageserver-protocol": "3.17.5" }, "engines": { - "vscode": "^1.67.0" + "vscode": "^1.82.0" } }, "node_modules/vscode-languageserver": { @@ -596,23 +611,26 @@ } }, "node_modules/vscode-languageserver-protocol": { - "version": "3.17.2", - "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.2.tgz", - "integrity": "sha512-8kYisQ3z/SQ2kyjlNeQxbkkTNmVFoQCqkmGrzLH6A9ecPlgTbp3wDTnUNqaUxYr4vlAcloxx8zwy7G5WdguYNg==", + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", + "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", + "license": "MIT", "dependencies": { - "vscode-jsonrpc": "8.0.2", - "vscode-languageserver-types": "3.17.2" + "vscode-jsonrpc": "8.2.0", + "vscode-languageserver-types": "3.17.5" } }, "node_modules/vscode-languageserver-textdocument": { - "version": "1.0.11", - "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.11.tgz", - "integrity": "sha512-X+8T3GoiwTVlJbicx/sIAF+yuJAqz8VvwJyoMVhwEMoEKE/fkDmrqUgDMyBECcM2A2frVZIUj5HI/ErRXCfOeA==" + "version": "1.0.12", + "resolved": "https://registry.npmjs.org/vscode-languageserver-textdocument/-/vscode-languageserver-textdocument-1.0.12.tgz", + "integrity": "sha512-cxWNPesCnQCcMPeenjKKsOCKQZ/L6Tv19DTRIGuLWe32lyzWhihGVJ/rcckZXJxfdKCFvRLS3fpBIsV/ZGX4zA==", + "license": "MIT" }, "node_modules/vscode-languageserver-types": { - "version": "3.17.2", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.2.tgz", - "integrity": "sha512-zHhCWatviizPIq9B7Vh9uvrH6x3sK8itC84HkamnBWoDFJtzBf7SWlpLCZUit72b3os45h6RWQNC9xHRDF8dRA==" + "version": "3.17.5", + "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", + "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", + "license": "MIT" }, "node_modules/vscode-languageserver/node_modules/vscode-jsonrpc": { "version": "8.1.0", @@ -637,30 +655,39 @@ "integrity": "sha512-SYU4z1dL0PyIMd4Vj8YOqFvHu7Hz/enbWtpfnVbJHU4Nd1YNYx8u0ennumc6h48GQNeOLxmwySmnADouT/AuZA==" }, "node_modules/vscode-markdown-languageserver": { - "version": "0.5.0-alpha.9", - "resolved": "https://registry.npmjs.org/vscode-markdown-languageserver/-/vscode-markdown-languageserver-0.5.0-alpha.9.tgz", - "integrity": "sha512-60jiPHgkstgkyODCN8qCJ4xAOrP/EoKyISmEAcJ7ILT5k2kAJF9JFEl3LvVZ+11HGGMJ2lm1L+lT2/JHvu5Pgg==", + "version": "0.5.0-alpha.12", + "resolved": "https://registry.npmjs.org/vscode-markdown-languageserver/-/vscode-markdown-languageserver-0.5.0-alpha.12.tgz", + "integrity": "sha512-cDRJKwWPZBHrrwufTHrhuZqGgBEGJcYo29Iwhvgh2BgTnIB+fp6Vs62LlqNUu25qsDjmZLLkIEQxntcX4kbfUQ==", + "license": "MIT", "dependencies": { "@vscode/l10n": "^0.0.11", "vscode-languageserver": "^8.1.0", "vscode-languageserver-textdocument": "^1.0.8", "vscode-languageserver-types": "^3.17.3", - "vscode-markdown-languageservice": "^0.5.0-alpha.8", + "vscode-markdown-languageservice": "^0.5.0-alpha.11", "vscode-uri": "^3.0.7" }, "engines": { "node": "*" } }, - "node_modules/vscode-markdown-languageserver/node_modules/vscode-languageserver-types": { - "version": "3.17.5", - "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", - "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==" + "node_modules/vscode-markdown-languageserver/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } }, "node_modules/vscode-markdown-languageserver/node_modules/vscode-markdown-languageservice": { - "version": "0.5.0-alpha.8", - "resolved": "https://registry.npmjs.org/vscode-markdown-languageservice/-/vscode-markdown-languageservice-0.5.0-alpha.8.tgz", - "integrity": "sha512-b2NgVMZvzI/7hRL32Kcu9neAAPFQzkcf/Fqwlxbz9p1/Q7aIorGACOGGo00s72AJtwjkCJ29eVJwUlFMFbPKqA==", + "version": "0.5.0-alpha.11", + "resolved": "https://registry.npmjs.org/vscode-markdown-languageservice/-/vscode-markdown-languageservice-0.5.0-alpha.11.tgz", + "integrity": "sha512-P1uBMAD5iylgpcweWCU1kQwk8SZngktnljXsZk1vFPorXv1mrEI7BkBpOUU0fhVssKgvFlCNLkI7KmwZLC7pdA==", + "license": "MIT", "dependencies": { "@vscode/l10n": "^0.0.10", "node-html-parser": "^6.1.5", @@ -676,19 +703,22 @@ "node_modules/vscode-markdown-languageserver/node_modules/vscode-markdown-languageservice/node_modules/@vscode/l10n": { "version": "0.0.10", "resolved": "https://registry.npmjs.org/@vscode/l10n/-/l10n-0.0.10.tgz", - "integrity": "sha512-E1OCmDcDWa0Ya7vtSjp/XfHFGqYJfh+YPC1RkATU71fTac+j1JjCcB3qwSzmlKAighx2WxhLlfhS0RwAN++PFQ==" + "integrity": "sha512-E1OCmDcDWa0Ya7vtSjp/XfHFGqYJfh+YPC1RkATU71fTac+j1JjCcB3qwSzmlKAighx2WxhLlfhS0RwAN++PFQ==", + "license": "MIT" }, "node_modules/vscode-markdown-languageservice": { - "version": "0.3.0-alpha.3", - "resolved": "https://registry.npmjs.org/vscode-markdown-languageservice/-/vscode-markdown-languageservice-0.3.0-alpha.3.tgz", - "integrity": "sha512-KPjIuCkSqabkzci7TnlLKep5FYIC45tS7UC5H8zoOii7aoKJru5mZBDXJt86bM3XTgnfpW7rUYqhNnvXbbCBbw==", + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/vscode-markdown-languageservice/-/vscode-markdown-languageservice-0.4.0.tgz", + "integrity": "sha512-3C8pZlC0ofHEYmWwHgenxL6//XrpkrgyytrqNpMlft46q9uBxSUfcXtEGt7wIDNLWsvmgqPqHBwEnBFtLwrWFA==", "dev": true, + "license": "MIT", "dependencies": { "@vscode/l10n": "^0.0.10", + "node-html-parser": "^6.1.5", "picomatch": "^2.3.1", - "vscode-languageserver-textdocument": "^1.0.5", - "vscode-languageserver-types": "^3.17.1", - "vscode-uri": "^3.0.3" + "vscode-languageserver-textdocument": "^1.0.8", + "vscode-languageserver-types": "^3.17.3", + "vscode-uri": "^3.0.7" }, "engines": { "node": "*" @@ -700,10 +730,24 @@ "integrity": "sha512-E1OCmDcDWa0Ya7vtSjp/XfHFGqYJfh+YPC1RkATU71fTac+j1JjCcB3qwSzmlKAighx2WxhLlfhS0RwAN++PFQ==", "dev": true }, + "node_modules/vscode-markdown-languageservice/node_modules/picomatch": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", + "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, "node_modules/vscode-uri": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", - "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "license": "MIT" }, "node_modules/yallist": { "version": "4.0.0", diff --git a/extensions/markdown-language-features/package.json b/extensions/markdown-language-features/package.json index 326287ac7a64..6f5157b29ab9 100644 --- a/extensions/markdown-language-features/package.json +++ b/extensions/markdown-language-features/package.json @@ -442,16 +442,6 @@ "%configuration.markdown.suggest.paths.includeWorkspaceHeaderCompletions.onSingleOrDoubleHash%" ] }, - "markdown.trace.extension": { - "type": "string", - "enum": [ - "off", - "verbose" - ], - "default": "off", - "description": "%markdown.trace.extension.desc%", - "scope": "window" - }, "markdown.trace.server": { "type": "string", "scope": "window", @@ -773,30 +763,30 @@ "watch-web": "npx webpack-cli --config extension-browser.webpack.config --mode none --watch --info-verbosity verbose" }, "dependencies": { - "@vscode/extension-telemetry": "^0.9.8", + "@vscode/extension-telemetry": "^1.4.0", "dompurify": "^3.2.4", - "highlight.js": "^11.8.0", - "markdown-it": "^12.3.2", + "highlight.js": "^11.11.1", + "markdown-it": "^14.1.0", "markdown-it-front-matter": "^0.2.4", - "morphdom": "^2.6.1", - "picomatch": "^2.3.1", + "morphdom": "^2.7.8", + "picomatch": "^4.0.3", "punycode": "^2.3.1", - "vscode-languageclient": "^8.0.2", - "vscode-languageserver-textdocument": "^1.0.11", - "vscode-markdown-languageserver": "^0.5.0-alpha.9", - "vscode-uri": "^3.0.3" + "vscode-languageclient": "^9.0.1", + "vscode-languageserver-textdocument": "^1.0.12", + "vscode-markdown-languageserver": "^0.5.0-alpha.12", + "vscode-uri": "^3.1.0" }, "devDependencies": { - "@types/dompurify": "^3.0.5", - "@types/lodash.throttle": "^4.1.3", - "@types/markdown-it": "12.2.3", - "@types/picomatch": "^2.3.0", - "@types/vscode-notebook-renderer": "^1.60.0", - "@types/vscode-webview": "^1.57.0", - "@vscode/markdown-it-katex": "^1.1.1", + "@types/dompurify": "^3.2.0", + "@types/lodash.throttle": "^4.1.9", + "@types/markdown-it": "14.1.2", + "@types/picomatch": "^4.0.2", + "@types/vscode-notebook-renderer": "^1.72.4", + "@types/vscode-webview": "^1.57.5", + "@vscode/markdown-it-katex": "^1.1.2", "lodash.throttle": "^4.1.1", - "vscode-languageserver-types": "^3.17.2", - "vscode-markdown-languageservice": "^0.3.0-alpha.3" + "vscode-languageserver-types": "^3.17.5", + "vscode-markdown-languageservice": "^0.4.0" }, "repository": { "type": "git", diff --git a/extensions/markdown-language-features/preview-src/index.ts b/extensions/markdown-language-features/preview-src/index.ts index 08c6759f05b4..8153953b606b 100644 --- a/extensions/markdown-language-features/preview-src/index.ts +++ b/extensions/markdown-language-features/preview-src/index.ts @@ -10,6 +10,7 @@ import { getEditorLineNumberForPageOffset, scrollToRevealSourceLine, getLineElem import { SettingsManager, getData, getRawData } from './settings'; import throttle = require('lodash.throttle'); import morphdom from 'morphdom'; +import DOMPurify from 'dompurify'; import type { ToWebviewMessage } from '../types/previewMessaging'; import { isOfScheme, Schemes } from '../src/util/schemes'; @@ -68,7 +69,7 @@ onceDocumentLoaded(() => { getRawData('data-initial-md-content'), 'text/html' ); - document.body.appendChild(markDownHtml.body); + document.body.append(...markDownHtml.body.children); // Restore const scrollProgress = state.scrollProgress; @@ -206,7 +207,8 @@ window.addEventListener('message', async event => { const root = document.querySelector('.markdown-body')!; const parser = new DOMParser(); - const newContent = parser.parseFromString(data.content, 'text/html'); // CodeQL [SM03712] This renderers content from the workspace into the Markdown preview. Webviews (and the markdown preview) have many other security measures in place to make this safe + const sanitizedContent = DOMPurify.sanitize(data.content); + const newContent = parser.parseFromString(sanitizedContent, 'text/html'); // CodeQL [SM03712] This renderers content from the workspace into the Markdown preview. Webviews (and the markdown preview) have many other security measures in place to make this safe // Strip out meta http-equiv tags for (const metaElement of Array.from(newContent.querySelectorAll('meta'))) { diff --git a/extensions/markdown-language-features/src/languageFeatures/copyFiles/shared.ts b/extensions/markdown-language-features/src/languageFeatures/copyFiles/shared.ts index 273fa56a6bb5..17f7ad1b8050 100644 --- a/extensions/markdown-language-features/src/languageFeatures/copyFiles/shared.ts +++ b/extensions/markdown-language-features/src/languageFeatures/copyFiles/shared.ts @@ -302,7 +302,7 @@ function escapeMarkdownLinkPath(mdPath: string): string { } function escapeBrackets(value: string): string { - value = value.replace(/[\[\]]/g, '\\$&'); // CodeQL [SM02383] The Markdown is fully sanitized after being rendered. + value = value.replace(/\\/g, '\\\\').replace(/[\[\]]/g, '\\$&'); // CodeQL [SM02383] The Markdown is fully sanitized after being rendered. return value; } diff --git a/extensions/markdown-language-features/src/languageFeatures/copyFiles/snippets.ts b/extensions/markdown-language-features/src/languageFeatures/copyFiles/snippets.ts index 854cf2175d0c..2430d21c342d 100644 --- a/extensions/markdown-language-features/src/languageFeatures/copyFiles/snippets.ts +++ b/extensions/markdown-language-features/src/languageFeatures/copyFiles/snippets.ts @@ -7,7 +7,7 @@ * Resolves variables in a VS Code snippet style string */ export function resolveSnippet(snippetString: string, vars: ReadonlyMap): string { - return snippetString.replaceAll(/(?\\\$)|(?\w+)(?:\/(?(?:\\\/|[^\}])+?)\/(?(?:\\\/|[^\}])+?)\/)?\}/g, (match, _escape, name, pattern, replacement, _offset, _str, groups) => { + return snippetString.replaceAll(/(?\\\$)|(?\w+)(?:\/(?(?:\\\/|[^\/}])+?)\/(?(?:\\\/|[^\/}])+?)\/)?\}/g, (match, _escape, name, pattern, replacement, _offset, _str, groups) => { if (groups?.['escape']) { return '$'; } diff --git a/extensions/markdown-language-features/src/logging.ts b/extensions/markdown-language-features/src/logging.ts index 6c114ae40e57..b5ea76f36081 100644 --- a/extensions/markdown-language-features/src/logging.ts +++ b/extensions/markdown-language-features/src/logging.ts @@ -6,87 +6,24 @@ import * as vscode from 'vscode'; import { Disposable } from './util/dispose'; -enum Trace { - Off, - Verbose -} - -namespace Trace { - export function fromString(value: string): Trace { - value = value.toLowerCase(); - switch (value) { - case 'off': - return Trace.Off; - case 'verbose': - return Trace.Verbose; - default: - return Trace.Off; - } - } -} export interface ILogger { - verbose(title: string, message: string, data?: any): void; + trace(title: string, message: string, data?: any): void; } export class VsCodeOutputLogger extends Disposable implements ILogger { - private _trace?: Trace; - - private _outputChannelValue?: vscode.OutputChannel; + private _outputChannelValue?: vscode.LogOutputChannel; private get _outputChannel() { - this._outputChannelValue ??= this._register(vscode.window.createOutputChannel('Markdown')); + this._outputChannelValue ??= this._register(vscode.window.createOutputChannel('Markdown', { log: true })); return this._outputChannelValue; } constructor() { super(); - - this._register(vscode.workspace.onDidChangeConfiguration(() => { - this._updateConfiguration(); - })); - - this._updateConfiguration(); - } - - public verbose(title: string, message: string, data?: any): void { - if (this._trace === Trace.Verbose) { - this._appendLine(`[Verbose ${this._now()}] ${title}: ${message}`); - if (data) { - this._appendLine(VsCodeOutputLogger._data2String(data)); - } - } - } - - private _now(): string { - const now = new Date(); - return String(now.getUTCHours()).padStart(2, '0') - + ':' + String(now.getMinutes()).padStart(2, '0') - + ':' + String(now.getUTCSeconds()).padStart(2, '0') + '.' + String(now.getMilliseconds()).padStart(3, '0'); - } - - private _updateConfiguration(): void { - this._trace = this._readTrace(); - } - - private _appendLine(value: string): void { - this._outputChannel.appendLine(value); - } - - private _readTrace(): Trace { - return Trace.fromString(vscode.workspace.getConfiguration().get('markdown.trace.extension', 'off')); } - private static _data2String(data: any): string { - if (data instanceof Error) { - if (typeof data.stack === 'string') { - return data.stack; - } - return data.message; - } - if (typeof data === 'string') { - return data; - } - return JSON.stringify(data, undefined, 2); + public trace(title: string, message: string, data?: any): void { + this._outputChannel.trace(`${title}: ${message}`, ...(data ? [JSON.stringify(data, null, 4)] : [])); } } diff --git a/extensions/markdown-language-features/src/markdownEngine.ts b/extensions/markdown-language-features/src/markdownEngine.ts index 83925eab1324..89363a77a86d 100644 --- a/extensions/markdown-language-features/src/markdownEngine.ts +++ b/extensions/markdown-language-features/src/markdownEngine.ts @@ -186,7 +186,7 @@ export class MarkdownItEngine implements IMdParser { return cached; } - this._logger.verbose('MarkdownItEngine', `tokenizeDocument - ${document.uri}`); + this._logger.trace('MarkdownItEngine', `tokenizeDocument - ${document.uri}`); const tokens = this._tokenizeString(document.getText(), engine); this._tokenCache.update(document, config, tokens); return tokens; diff --git a/extensions/markdown-language-features/src/preview/documentRenderer.ts b/extensions/markdown-language-features/src/preview/documentRenderer.ts index eeab8e19d9d6..8a39c9cdf4d3 100644 --- a/extensions/markdown-language-features/src/preview/documentRenderer.ts +++ b/extensions/markdown-language-features/src/preview/documentRenderer.ts @@ -79,7 +79,7 @@ export class MdDocumentRenderer { webviewResourceRoot: resourceProvider.asWebviewUri(markdownDocument.uri).toString(), }; - this._logger.verbose('DocumentRenderer', `provideTextDocumentContent - ${markdownDocument.uri}`, initialData); + this._logger.trace('DocumentRenderer', `provideTextDocumentContent - ${markdownDocument.uri}`, initialData); // Content Security Policy const nonce = getNonce(); diff --git a/extensions/markdown-language-features/src/preview/preview.ts b/extensions/markdown-language-features/src/preview/preview.ts index 7ccbc625b473..2d4186df9ae3 100644 --- a/extensions/markdown-language-features/src/preview/preview.ts +++ b/extensions/markdown-language-features/src/preview/preview.ts @@ -221,7 +221,7 @@ class MarkdownPreview extends Disposable implements WebviewResourceProvider { return; } - this._logger.verbose('MarkdownPreview', 'updateForView', { markdownFile: this._resource }); + this._logger.trace('MarkdownPreview', 'updateForView', { markdownFile: this._resource }); this._line = topLine; this.postMessage({ type: 'updateView', diff --git a/extensions/markdown-language-features/src/test/nulLogging.ts b/extensions/markdown-language-features/src/test/nulLogging.ts index a786ab83b4f2..035e7d74c6dc 100644 --- a/extensions/markdown-language-features/src/test/nulLogging.ts +++ b/extensions/markdown-language-features/src/test/nulLogging.ts @@ -6,7 +6,7 @@ import { ILogger } from '../logging'; export const nulLogger = new class implements ILogger { - verbose(): void { + trace(): void { // noop } }; diff --git a/extensions/markdown-math/package-lock.json b/extensions/markdown-math/package-lock.json index 73ae907e680a..869798dc0689 100644 --- a/extensions/markdown-math/package-lock.json +++ b/extensions/markdown-math/package-lock.json @@ -9,32 +9,52 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@vscode/markdown-it-katex": "^1.1.1" + "@vscode/markdown-it-katex": "^1.1.2" }, "devDependencies": { - "@types/markdown-it": "^0.0.0", - "@types/vscode-notebook-renderer": "^1.60.0" + "@types/markdown-it": "^14.1.2", + "@types/vscode-notebook-renderer": "^1.72.4" }, "engines": { "vscode": "^1.54.0" } }, + "node_modules/@types/linkify-it": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@types/linkify-it/-/linkify-it-5.0.0.tgz", + "integrity": "sha512-sVDA58zAw4eWAffKOaQH5/5j3XeayukzDk+ewSsnv3p4yJEZHCCzMDiZM8e0OUrRvmpGZ85jf4yDHkHsgBNr9Q==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/markdown-it": { - "version": "0.0.0", - "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-0.0.0.tgz", - "integrity": "sha512-rLEOTm6Wi9M8GFnIK7VczXSEThIN/eVoevpTYVk+FD/DPX3N15Sj9b3vkjjDY63U0Zw1yawf13CI92CCHpC5kw==", - "dev": true + "version": "14.1.2", + "resolved": "https://registry.npmjs.org/@types/markdown-it/-/markdown-it-14.1.2.tgz", + "integrity": "sha512-promo4eFwuiW+TfGxhi+0x3czqTYJkG8qB17ZUJiVF10Xm7NLVRSLUsfRTU/6h1e24VvRnXCx+hG7li58lkzog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/linkify-it": "^5", + "@types/mdurl": "^2" + } + }, + "node_modules/@types/mdurl": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/@types/mdurl/-/mdurl-2.0.0.tgz", + "integrity": "sha512-RGdgjQUZba5p6QEFAVx2OGb8rQDL/cPRG7GiedRzMcJ1tYnUANBncjbSB1NRGwbvjcPeikRABz2nshyPk1bhWg==", + "dev": true, + "license": "MIT" }, "node_modules/@types/vscode-notebook-renderer": { - "version": "1.72.0", - "resolved": "https://registry.npmjs.org/@types/vscode-notebook-renderer/-/vscode-notebook-renderer-1.72.0.tgz", - "integrity": "sha512-5iTjb39DpLn03ULUwrDR3L2Dy59RV4blSUHy0oLdQuIY11PhgWO4mXIcoFS0VxY1GZQ4IcjSf3ooT2Jrrcahnw==", - "dev": true + "version": "1.72.4", + "resolved": "https://registry.npmjs.org/@types/vscode-notebook-renderer/-/vscode-notebook-renderer-1.72.4.tgz", + "integrity": "sha512-bdKO41c6Dc24pH/O/eM/jqfCwGH4zc76o/g/6Gt1y/eg/bvvqP2/VpbV+Sa5Te2sZekFRcbYnSSFTKo3wcVGUg==", + "dev": true, + "license": "MIT" }, "node_modules/@vscode/markdown-it-katex": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/@vscode/markdown-it-katex/-/markdown-it-katex-1.1.1.tgz", - "integrity": "sha512-3KTlbsRBPJQLE2YmLL7K6nunTlU+W9T5+FjfNdWuIUKgxSS6HWLQHaO3L4MkJi7z7MpIPpY+g4N+cWNBPE/MSA==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@vscode/markdown-it-katex/-/markdown-it-katex-1.1.2.tgz", + "integrity": "sha512-+4IIv5PgrmhKvW/3LpkpkGg257OViEhXkOOgCyj5KMsjsOfnRXkni8XAuuF9Ui5p3B8WnUovlDXAQNb8RJ/RaQ==", "license": "MIT", "dependencies": { "katex": "^0.16.4" diff --git a/extensions/markdown-math/package.json b/extensions/markdown-math/package.json index 6e599ae2a0eb..f397c9ca66be 100644 --- a/extensions/markdown-math/package.json +++ b/extensions/markdown-math/package.json @@ -111,14 +111,14 @@ "build-notebook": "node ./esbuild" }, "devDependencies": { - "@types/markdown-it": "^0.0.0", - "@types/vscode-notebook-renderer": "^1.60.0" + "@types/markdown-it": "^14.1.2", + "@types/vscode-notebook-renderer": "^1.72.4" }, "repository": { "type": "git", "url": "https://github.com/microsoft/vscode.git" }, "dependencies": { - "@vscode/markdown-it-katex": "^1.1.1" + "@vscode/markdown-it-katex": "^1.1.2" } } diff --git a/extensions/media-preview/media/imagePreview.js b/extensions/media-preview/media/imagePreview.js index ab8ad542a2d9..282e2a82cb94 100644 --- a/extensions/media-preview/media/imagePreview.js +++ b/extensions/media-preview/media/imagePreview.js @@ -311,7 +311,45 @@ document.body.classList.remove('loading'); }); - image.src = settings.src; + /** + * Validate and ensure only safe image sources can be used. + * @param {string} src + * @return {boolean} + */ + function isSafeImageSrc(src) { + try { + if (typeof src !== 'string' || src.length > 2048) { + return false; + } + // Allow http, https, file, and data URIs for images only + const allowedSchemes = ['http:', 'https:', 'file:']; + const urlMatch = src.match(/^([a-zA-Z0-9+.-]+):/); + if (!urlMatch) { + return false; + } + const scheme = urlMatch[1].toLowerCase() + ':'; + if (allowedSchemes.includes(scheme)) { + return true; + } + if (scheme === 'data:') { + // Allow only image media types in data URLs + // Disallow SVG images for data URIs to mitigate XSS + return /^data:image\/(png|jpe?g|gif|bmp|webp);base64,/.test(src); + } + return false; + } catch { + return false; + } + } + + if (isSafeImageSrc(settings.src)) { + image.src = settings.src; + } else { + console.error('Unsafe image src detected:', settings.src); + image.src = ''; + document.body.classList.add('error'); + document.body.classList.remove('loading'); + } document.querySelector('.open-file-link')?.addEventListener('click', (e) => { e.preventDefault(); diff --git a/extensions/media-preview/media/videoPreview.js b/extensions/media-preview/media/videoPreview.js index eeed26972a31..8ff6ddcb8d52 100644 --- a/extensions/media-preview/media/videoPreview.js +++ b/extensions/media-preview/media/videoPreview.js @@ -5,6 +5,18 @@ // @ts-check "use strict"; +// Returns true if src is a safe URL for video.src +function isSafeVideoSrc(src) { + try { + const allowedProtocols = ['http:', 'https:', 'blob:', 'filesystem:', '']; + // If relative URL, use document.baseURI as base + const url = new URL(src, document.baseURI); + return allowedProtocols.includes(url.protocol); + } catch { + return false; + } +} + (function () { // @ts-ignore const vscode = acquireVsCodeApi(); @@ -28,7 +40,7 @@ // Elements const video = document.createElement('video'); - if (settings.src !== null) { + if (settings.src !== null && isSafeVideoSrc(settings.src)) { video.src = settings.src; } video.playsInline = true; diff --git a/extensions/media-preview/package-lock.json b/extensions/media-preview/package-lock.json index d26855f3ad25..7255e5522992 100644 --- a/extensions/media-preview/package-lock.json +++ b/extensions/media-preview/package-lock.json @@ -9,81 +9,81 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@vscode/extension-telemetry": "^0.9.8", - "vscode-uri": "^3.0.6" + "@vscode/extension-telemetry": "^1.4.0", + "vscode-uri": "^3.1.0" }, "engines": { "vscode": "^1.70.0" } }, "node_modules/@microsoft/1ds-core-js": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.4.tgz", - "integrity": "sha512-3gbDUQgAO8EoyQTNcAEkxpuPnioC0May13P1l1l0NKZ128L9Ts/sj8QsfwCRTjHz0HThlA+4FptcAJXNYUy3rg==", + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.10.tgz", + "integrity": "sha512-5fSZmkGwWkH+mrIA5M1GYPZdPM+SjXwCCl2Am7VhFoVwOBJNhRnwvIpAdzw6sFjiebN/rz+/YH0NdxztGZSa9Q==", "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" } }, "node_modules/@microsoft/1ds-post-js": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.4.tgz", - "integrity": "sha512-nlKjWricDj0Tn68Dt0P8lX9a+X7LYrqJ6/iSfQwMfDhRIGLqW+wxx8gxS+iGWC/oc8zMQAeiZaemUpCwQcwpRQ==", + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.10.tgz", + "integrity": "sha512-VSLjc9cT+Y+eTiSfYltJHJCejn8oYr0E6Pq2BMhOEO7F6IyLGYIxzKKvo78ze9x+iHX7KPTATcZ+PFgjGXuNqg==", "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "4.3.4", + "@microsoft/1ds-core-js": "4.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" } }, "node_modules/@microsoft/applicationinsights-channel-js": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.4.tgz", - "integrity": "sha512-Z4nrxYwGKP9iyrYtm7iPQXVOFy4FsEsX0nDKkAi96Qpgw+vEh6NH4ORxMMuES0EollBQ3faJyvYCwckuCVIj0g==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.10.tgz", + "integrity": "sha512-iolFLz1ocWAzIQqHIEjjov3gNTPkgFQ4ArHnBcJEYoffOGWlJt6copaevS5YPI5rHzmbySsengZ8cLJJBBrXzQ==", "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-common": "3.3.4", - "@microsoft/applicationinsights-core-js": "3.3.4", + "@microsoft/applicationinsights-common": "3.3.10", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-common": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.4.tgz", - "integrity": "sha512-4ms16MlIvcP4WiUPqopifNxcWCcrXQJ2ADAK/75uok2mNQe6ZNRsqb/P+pvhUxc8A5HRlvoXPP1ptDSN5Girgw==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.10.tgz", + "integrity": "sha512-RVIenPIvNgZCbjJdALvLM4rNHgAFuHI7faFzHCgnI6S2WCUNGHeXlQTs9EUUrL+n2TPp9/cd0KKMILU5VVyYiA==", "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-core-js": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.4.tgz", - "integrity": "sha512-MummANF0mgKIkdvVvfmHQTBliK114IZLRhTL0X0Ep+zjDwWMHqYZgew0nlFKAl6ggu42abPZFK5afpE7qjtYJA==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.10.tgz", + "integrity": "sha512-5yKeyassZTq2l+SAO4npu6LPnbS++UD+M+Ghjm9uRzoBwD8tumFx0/F8AkSVqbniSREd+ztH/2q2foewa2RZyg==", "license": "MIT", "dependencies": { "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { "tslib": ">= 1.0.0" @@ -99,18 +99,18 @@ } }, "node_modules/@microsoft/applicationinsights-web-basic": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.4.tgz", - "integrity": "sha512-OpEPXr8vU/t/M8T9jvWJzJx/pCyygIiR1nGM/2PTde0wn7anl71Gxl5fWol7K/WwFEORNjkL3CEyWOyDc+28AA==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.10.tgz", + "integrity": "sha512-AZib5DAT3NU0VT0nLWEwXrnoMDDgZ/5S4dso01CNU5ELNxLdg+1fvchstlVdMy4FrAnxzs8Wf/GIQNFYOVgpAw==", "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-channel-js": "3.3.4", - "@microsoft/applicationinsights-common": "3.3.4", - "@microsoft/applicationinsights-core-js": "3.3.4", + "@microsoft/applicationinsights-channel-js": "3.3.10", + "@microsoft/applicationinsights-common": "3.3.10", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { "tslib": ">= 1.0.0" @@ -135,29 +135,30 @@ } }, "node_modules/@nevware21/ts-utils": { - "version": "0.11.6", - "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.11.6.tgz", - "integrity": "sha512-OUUJTh3fnaUSzg9DEHgv3d7jC+DnPL65mIO7RaR+jWve7+MmcgIvF79gY97DPQ4frH+IpNR78YAYd/dW4gK3kg==", + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.12.5.tgz", + "integrity": "sha512-JPQZWPKQJjj7kAftdEZL0XDFfbMgXCGiUAZe0d7EhLC3QlXTlZdSckGqqRIQ2QNl0VTEZyZUvRBw6Ednw089Fw==", "license": "MIT" }, "node_modules/@vscode/extension-telemetry": { - "version": "0.9.8", - "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.8.tgz", - "integrity": "sha512-7YcKoUvmHlIB8QYCE4FNzt3ErHi9gQPhdCM3ZWtpw1bxPT0I+lMdx52KHlzTNoJzQ2NvMX7HyzyDwBEiMgTrWQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-1.4.0.tgz", + "integrity": "sha512-mqypuqIDn+vQnaQ8tMdvCRn9BprZTH2j6nlFFv9hE6hB25n32F8rOZinos8OgblRi87X1qdBpjpb0Em1LGSG9w==", "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "^4.3.4", - "@microsoft/1ds-post-js": "^4.3.4", - "@microsoft/applicationinsights-web-basic": "^3.3.4" + "@microsoft/1ds-core-js": "^4.3.10", + "@microsoft/1ds-post-js": "^4.3.10", + "@microsoft/applicationinsights-web-basic": "^3.3.10" }, "engines": { "vscode": "^1.75.0" } }, "node_modules/vscode-uri": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.6.tgz", - "integrity": "sha512-fmL7V1eiDBFRRnu+gfRWTzyPpNIHJTc4mWnFkwBUmO9U3KPgJAmTx7oxi2bl/Rh6HLdU7+4C9wlj0k2E4AdKFQ==" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "license": "MIT" } } } diff --git a/extensions/media-preview/package.json b/extensions/media-preview/package.json index 7e2b70293fc3..1e9dbac00429 100644 --- a/extensions/media-preview/package.json +++ b/extensions/media-preview/package.json @@ -126,8 +126,8 @@ "watch-web": "npx webpack-cli --config extension-browser.webpack.config --mode none --watch --info-verbosity verbose" }, "dependencies": { - "@vscode/extension-telemetry": "^0.9.8", - "vscode-uri": "^3.0.6" + "@vscode/extension-telemetry": "^1.4.0", + "vscode-uri": "^3.1.0" }, "repository": { "type": "git", diff --git a/extensions/merge-conflict/package-lock.json b/extensions/merge-conflict/package-lock.json index 5ee68d290f01..933227c77d11 100644 --- a/extensions/merge-conflict/package-lock.json +++ b/extensions/merge-conflict/package-lock.json @@ -9,83 +9,83 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@vscode/extension-telemetry": "^0.9.8" + "@vscode/extension-telemetry": "^1.4.0" }, "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "engines": { "vscode": "^1.5.0" } }, "node_modules/@microsoft/1ds-core-js": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.4.tgz", - "integrity": "sha512-3gbDUQgAO8EoyQTNcAEkxpuPnioC0May13P1l1l0NKZ128L9Ts/sj8QsfwCRTjHz0HThlA+4FptcAJXNYUy3rg==", + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.10.tgz", + "integrity": "sha512-5fSZmkGwWkH+mrIA5M1GYPZdPM+SjXwCCl2Am7VhFoVwOBJNhRnwvIpAdzw6sFjiebN/rz+/YH0NdxztGZSa9Q==", "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" } }, "node_modules/@microsoft/1ds-post-js": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.4.tgz", - "integrity": "sha512-nlKjWricDj0Tn68Dt0P8lX9a+X7LYrqJ6/iSfQwMfDhRIGLqW+wxx8gxS+iGWC/oc8zMQAeiZaemUpCwQcwpRQ==", + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.10.tgz", + "integrity": "sha512-VSLjc9cT+Y+eTiSfYltJHJCejn8oYr0E6Pq2BMhOEO7F6IyLGYIxzKKvo78ze9x+iHX7KPTATcZ+PFgjGXuNqg==", "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "4.3.4", + "@microsoft/1ds-core-js": "4.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" } }, "node_modules/@microsoft/applicationinsights-channel-js": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.4.tgz", - "integrity": "sha512-Z4nrxYwGKP9iyrYtm7iPQXVOFy4FsEsX0nDKkAi96Qpgw+vEh6NH4ORxMMuES0EollBQ3faJyvYCwckuCVIj0g==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.10.tgz", + "integrity": "sha512-iolFLz1ocWAzIQqHIEjjov3gNTPkgFQ4ArHnBcJEYoffOGWlJt6copaevS5YPI5rHzmbySsengZ8cLJJBBrXzQ==", "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-common": "3.3.4", - "@microsoft/applicationinsights-core-js": "3.3.4", + "@microsoft/applicationinsights-common": "3.3.10", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-common": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.4.tgz", - "integrity": "sha512-4ms16MlIvcP4WiUPqopifNxcWCcrXQJ2ADAK/75uok2mNQe6ZNRsqb/P+pvhUxc8A5HRlvoXPP1ptDSN5Girgw==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.10.tgz", + "integrity": "sha512-RVIenPIvNgZCbjJdALvLM4rNHgAFuHI7faFzHCgnI6S2WCUNGHeXlQTs9EUUrL+n2TPp9/cd0KKMILU5VVyYiA==", "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-core-js": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.4.tgz", - "integrity": "sha512-MummANF0mgKIkdvVvfmHQTBliK114IZLRhTL0X0Ep+zjDwWMHqYZgew0nlFKAl6ggu42abPZFK5afpE7qjtYJA==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.10.tgz", + "integrity": "sha512-5yKeyassZTq2l+SAO4npu6LPnbS++UD+M+Ghjm9uRzoBwD8tumFx0/F8AkSVqbniSREd+ztH/2q2foewa2RZyg==", "license": "MIT", "dependencies": { "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { "tslib": ">= 1.0.0" @@ -101,18 +101,18 @@ } }, "node_modules/@microsoft/applicationinsights-web-basic": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.4.tgz", - "integrity": "sha512-OpEPXr8vU/t/M8T9jvWJzJx/pCyygIiR1nGM/2PTde0wn7anl71Gxl5fWol7K/WwFEORNjkL3CEyWOyDc+28AA==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.10.tgz", + "integrity": "sha512-AZib5DAT3NU0VT0nLWEwXrnoMDDgZ/5S4dso01CNU5ELNxLdg+1fvchstlVdMy4FrAnxzs8Wf/GIQNFYOVgpAw==", "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-channel-js": "3.3.4", - "@microsoft/applicationinsights-common": "3.3.4", - "@microsoft/applicationinsights-core-js": "3.3.4", + "@microsoft/applicationinsights-channel-js": "3.3.10", + "@microsoft/applicationinsights-common": "3.3.10", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { "tslib": ">= 1.0.0" @@ -137,39 +137,41 @@ } }, "node_modules/@nevware21/ts-utils": { - "version": "0.11.6", - "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.11.6.tgz", - "integrity": "sha512-OUUJTh3fnaUSzg9DEHgv3d7jC+DnPL65mIO7RaR+jWve7+MmcgIvF79gY97DPQ4frH+IpNR78YAYd/dW4gK3kg==", + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.12.5.tgz", + "integrity": "sha512-JPQZWPKQJjj7kAftdEZL0XDFfbMgXCGiUAZe0d7EhLC3QlXTlZdSckGqqRIQ2QNl0VTEZyZUvRBw6Ednw089Fw==", "license": "MIT" }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/@vscode/extension-telemetry": { - "version": "0.9.8", - "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.8.tgz", - "integrity": "sha512-7YcKoUvmHlIB8QYCE4FNzt3ErHi9gQPhdCM3ZWtpw1bxPT0I+lMdx52KHlzTNoJzQ2NvMX7HyzyDwBEiMgTrWQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-1.4.0.tgz", + "integrity": "sha512-mqypuqIDn+vQnaQ8tMdvCRn9BprZTH2j6nlFFv9hE6hB25n32F8rOZinos8OgblRi87X1qdBpjpb0Em1LGSG9w==", "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "^4.3.4", - "@microsoft/1ds-post-js": "^4.3.4", - "@microsoft/applicationinsights-web-basic": "^3.3.4" + "@microsoft/1ds-core-js": "^4.3.10", + "@microsoft/1ds-post-js": "^4.3.10", + "@microsoft/applicationinsights-web-basic": "^3.3.10" }, "engines": { "vscode": "^1.75.0" } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" } } } diff --git a/extensions/merge-conflict/package.json b/extensions/merge-conflict/package.json index de56c9c22cfe..4224f851f144 100644 --- a/extensions/merge-conflict/package.json +++ b/extensions/merge-conflict/package.json @@ -166,10 +166,10 @@ } }, "dependencies": { - "@vscode/extension-telemetry": "^0.9.8" + "@vscode/extension-telemetry": "^1.4.0" }, "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "repository": { "type": "git", diff --git a/extensions/microsoft-authentication/package-lock.json b/extensions/microsoft-authentication/package-lock.json index 4aae40c5f170..a4f02d576094 100644 --- a/extensions/microsoft-authentication/package-lock.json +++ b/extensions/microsoft-authentication/package-lock.json @@ -10,18 +10,18 @@ "license": "MIT", "dependencies": { "@azure/ms-rest-azure-env": "^2.0.0", - "@azure/msal-node": "^2.16.2", - "@azure/msal-node-extensions": "^1.5.0", - "@vscode/extension-telemetry": "^0.9.8", + "@azure/msal-node": "^5.0.2", + "@azure/msal-node-extensions": "^5.0.2", + "@vscode/extension-telemetry": "^1.4.0", "keytar": "file:./packageMocks/keytar", "vscode-tas-client": "^0.1.84" }, "devDependencies": { - "@types/node": "20.x", - "@types/node-fetch": "^2.5.7", - "@types/randombytes": "^2.0.0", - "@types/sha.js": "^2.4.0", - "@types/uuid": "8.0.0" + "@types/node": "25.x", + "@types/node-fetch": "^2.6.13", + "@types/randombytes": "^2.0.3", + "@types/sha.js": "^2.4.4", + "@types/uuid": "11.0.0" }, "engines": { "vscode": "^1.42.0" @@ -33,118 +33,118 @@ "integrity": "sha512-dG76W7ElfLi+fbTjnZVGj+M9e0BIEJmRxU6fHaUQ12bZBe8EJKYb2GV50YWNaP2uJiVQ5+7nXEVj1VN1UQtaEw==" }, "node_modules/@azure/msal-common": { - "version": "14.16.0", - "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-14.16.0.tgz", - "integrity": "sha512-1KOZj9IpcDSwpNiQNjt0jDYZpQvNZay7QAEi/5DLubay40iGYtLzya/jbjRPLyOTZhEKyL1MzPuw2HqBCjceYA==", + "version": "16.0.2", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-16.0.2.tgz", + "integrity": "sha512-ZJ/UR7lyqIntURrIJCyvScwJFanM9QhJYcJCheB21jZofGKpP9QxWgvADANo7UkresHKzV+6YwoeZYP7P7HvUg==", "license": "MIT", "engines": { "node": ">=0.8.0" } }, "node_modules/@azure/msal-node": { - "version": "2.16.2", - "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-2.16.2.tgz", - "integrity": "sha512-An7l1hEr0w1HMMh1LU+rtDtqL7/jw74ORlc9Wnh06v7TU/xpG39/Zdr1ZJu3QpjUfKJ+E0/OXMW8DRSWTlh7qQ==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-5.0.2.tgz", + "integrity": "sha512-3tHeJghckgpTX98TowJoXOjKGuds0L+FKfeHJtoZFl2xvwE6RF65shZJzMQ5EQZWXzh3sE1i9gE+m3aRMachjA==", "license": "MIT", "dependencies": { - "@azure/msal-common": "14.16.0", + "@azure/msal-common": "16.0.2", "jsonwebtoken": "^9.0.0", "uuid": "^8.3.0" }, "engines": { - "node": ">=16" + "node": ">=20" } }, "node_modules/@azure/msal-node-extensions": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@azure/msal-node-extensions/-/msal-node-extensions-1.5.0.tgz", - "integrity": "sha512-UfEyh2xmJHKH64zPS/SbN1bd9adV4ZWGp1j2OSwIuhVraqpUXyXZ1LpDpiUqg/peTgLLtx20qrHOzYT0kKzmxQ==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/@azure/msal-node-extensions/-/msal-node-extensions-5.0.2.tgz", + "integrity": "sha512-695boK9IFmHrYPviFgj5DaeJ65iUGjzHiSIAgzyIRfwTO8k3J4KrF+xbFy2uC9JZ2U8HHEobi7UGloc768HhUQ==", "hasInstallScript": true, "license": "MIT", "dependencies": { - "@azure/msal-common": "14.16.0", - "@azure/msal-node-runtime": "^0.17.1", + "@azure/msal-common": "16.0.2", + "@azure/msal-node-runtime": "^0.20.0", "keytar": "^7.8.0" }, "engines": { - "node": ">=16" + "node": ">=20" } }, "node_modules/@azure/msal-node-runtime": { - "version": "0.17.1", - "resolved": "https://registry.npmjs.org/@azure/msal-node-runtime/-/msal-node-runtime-0.17.1.tgz", - "integrity": "sha512-qAfTg+iGJsg+XvD9nmknI63+XuoX32oT+SX4wJdFz7CS6ETVpSHoroHVaUmsTU1H7H0+q1/ZkP988gzPRMYRsg==", + "version": "0.20.1", + "resolved": "https://registry.npmjs.org/@azure/msal-node-runtime/-/msal-node-runtime-0.20.1.tgz", + "integrity": "sha512-WVbMedbJHjt9M+qeZMH/6U1UmjXsKaMB6fN8OZUtGY7UVNYofrowZNx4nVvWN/ajPKBQCEW4Rr/MwcRuA8HGcQ==", "hasInstallScript": true, "license": "MIT" }, "node_modules/@microsoft/1ds-core-js": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.4.tgz", - "integrity": "sha512-3gbDUQgAO8EoyQTNcAEkxpuPnioC0May13P1l1l0NKZ128L9Ts/sj8QsfwCRTjHz0HThlA+4FptcAJXNYUy3rg==", + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.10.tgz", + "integrity": "sha512-5fSZmkGwWkH+mrIA5M1GYPZdPM+SjXwCCl2Am7VhFoVwOBJNhRnwvIpAdzw6sFjiebN/rz+/YH0NdxztGZSa9Q==", "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" } }, "node_modules/@microsoft/1ds-post-js": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.4.tgz", - "integrity": "sha512-nlKjWricDj0Tn68Dt0P8lX9a+X7LYrqJ6/iSfQwMfDhRIGLqW+wxx8gxS+iGWC/oc8zMQAeiZaemUpCwQcwpRQ==", + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.10.tgz", + "integrity": "sha512-VSLjc9cT+Y+eTiSfYltJHJCejn8oYr0E6Pq2BMhOEO7F6IyLGYIxzKKvo78ze9x+iHX7KPTATcZ+PFgjGXuNqg==", "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "4.3.4", + "@microsoft/1ds-core-js": "4.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" } }, "node_modules/@microsoft/applicationinsights-channel-js": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.4.tgz", - "integrity": "sha512-Z4nrxYwGKP9iyrYtm7iPQXVOFy4FsEsX0nDKkAi96Qpgw+vEh6NH4ORxMMuES0EollBQ3faJyvYCwckuCVIj0g==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.10.tgz", + "integrity": "sha512-iolFLz1ocWAzIQqHIEjjov3gNTPkgFQ4ArHnBcJEYoffOGWlJt6copaevS5YPI5rHzmbySsengZ8cLJJBBrXzQ==", "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-common": "3.3.4", - "@microsoft/applicationinsights-core-js": "3.3.4", + "@microsoft/applicationinsights-common": "3.3.10", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-common": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.4.tgz", - "integrity": "sha512-4ms16MlIvcP4WiUPqopifNxcWCcrXQJ2ADAK/75uok2mNQe6ZNRsqb/P+pvhUxc8A5HRlvoXPP1ptDSN5Girgw==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.10.tgz", + "integrity": "sha512-RVIenPIvNgZCbjJdALvLM4rNHgAFuHI7faFzHCgnI6S2WCUNGHeXlQTs9EUUrL+n2TPp9/cd0KKMILU5VVyYiA==", "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-core-js": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.4.tgz", - "integrity": "sha512-MummANF0mgKIkdvVvfmHQTBliK114IZLRhTL0X0Ep+zjDwWMHqYZgew0nlFKAl6ggu42abPZFK5afpE7qjtYJA==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.10.tgz", + "integrity": "sha512-5yKeyassZTq2l+SAO4npu6LPnbS++UD+M+Ghjm9uRzoBwD8tumFx0/F8AkSVqbniSREd+ztH/2q2foewa2RZyg==", "license": "MIT", "dependencies": { "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { "tslib": ">= 1.0.0" @@ -160,18 +160,18 @@ } }, "node_modules/@microsoft/applicationinsights-web-basic": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.4.tgz", - "integrity": "sha512-OpEPXr8vU/t/M8T9jvWJzJx/pCyygIiR1nGM/2PTde0wn7anl71Gxl5fWol7K/WwFEORNjkL3CEyWOyDc+28AA==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.10.tgz", + "integrity": "sha512-AZib5DAT3NU0VT0nLWEwXrnoMDDgZ/5S4dso01CNU5ELNxLdg+1fvchstlVdMy4FrAnxzs8Wf/GIQNFYOVgpAw==", "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-channel-js": "3.3.4", - "@microsoft/applicationinsights-common": "3.3.4", - "@microsoft/applicationinsights-core-js": "3.3.4", + "@microsoft/applicationinsights-channel-js": "3.3.10", + "@microsoft/applicationinsights-common": "3.3.10", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { "tslib": ">= 1.0.0" @@ -196,63 +196,72 @@ } }, "node_modules/@nevware21/ts-utils": { - "version": "0.11.6", - "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.11.6.tgz", - "integrity": "sha512-OUUJTh3fnaUSzg9DEHgv3d7jC+DnPL65mIO7RaR+jWve7+MmcgIvF79gY97DPQ4frH+IpNR78YAYd/dW4gK3kg==", + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.12.5.tgz", + "integrity": "sha512-JPQZWPKQJjj7kAftdEZL0XDFfbMgXCGiUAZe0d7EhLC3QlXTlZdSckGqqRIQ2QNl0VTEZyZUvRBw6Ednw089Fw==", "license": "MIT" }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/@types/node-fetch": { - "version": "2.5.7", - "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.5.7.tgz", - "integrity": "sha512-o2WVNf5UhWRkxlf6eq+jMZDu7kjgpgJfl4xVNlvryc95O/6F2ld8ztKX+qu+Rjyet93WAWm5LjeX9H5FGkODvw==", + "version": "2.6.13", + "resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.13.tgz", + "integrity": "sha512-QGpRVpzSaUs30JBSGPjOg4Uveu384erbHBoT1zeONvyCfwQxIkUshLAOqN/k9EjGviPRmWTTe6aH2qySWKTVSw==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*", - "form-data": "^3.0.0" + "form-data": "^4.0.4" } }, "node_modules/@types/randombytes": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/randombytes/-/randombytes-2.0.0.tgz", - "integrity": "sha512-bz8PhAVlwN72vqefzxa14DKNT8jK/mV66CSjwdVQM/k3Th3EPKfUtdMniwZgMedQTFuywAsfjnZsg+pEnltaMA==", + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@types/randombytes/-/randombytes-2.0.3.tgz", + "integrity": "sha512-+NRgihTfuURllWCiIAhm1wsJqzsocnqXM77V/CalsdJIYSRGEHMnritxh+6EsBklshC+clo1KgnN14qgSGeQdw==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } }, "node_modules/@types/sha.js": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/@types/sha.js/-/sha.js-2.4.0.tgz", - "integrity": "sha512-amxKgPy6WJTKuw8mpUwjX2BSxuBtBmZfRwIUDIuPJKNwGN8CWDli8JTg5ONTWOtcTkHIstvT7oAhhYXqEjStHQ==", + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/sha.js/-/sha.js-2.4.4.tgz", + "integrity": "sha512-Qukd+D6S2Hm0wLVt2Vh+/eWBIoUt+wF8jWjBsG4F8EFQRwKtYvtXCPcNl2OEUQ1R+eTr3xuSaBYUyM3WD1x/Qw==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*" } }, "node_modules/@types/uuid": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.0.0.tgz", - "integrity": "sha512-xSQfNcvOiE5f9dyd4Kzxbof1aTrLobL278pGLKOZI6esGfZ7ts9Ka16CzIN6Y8hFHE1C7jIBZokULhK1bOgjRw==", - "dev": true + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-11.0.0.tgz", + "integrity": "sha512-HVyk8nj2m+jcFRNazzqyVKiZezyhDKrGUA3jlEcg/nZ6Ms+qHwocba1Y/AaVaznJTAM9xpdFSh+ptbNrhOGvZA==", + "deprecated": "This is a stub types definition. uuid provides its own type definitions, so you do not need this installed.", + "dev": true, + "license": "MIT", + "dependencies": { + "uuid": "*" + } }, "node_modules/@vscode/extension-telemetry": { - "version": "0.9.8", - "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.8.tgz", - "integrity": "sha512-7YcKoUvmHlIB8QYCE4FNzt3ErHi9gQPhdCM3ZWtpw1bxPT0I+lMdx52KHlzTNoJzQ2NvMX7HyzyDwBEiMgTrWQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-1.4.0.tgz", + "integrity": "sha512-mqypuqIDn+vQnaQ8tMdvCRn9BprZTH2j6nlFFv9hE6hB25n32F8rOZinos8OgblRi87X1qdBpjpb0Em1LGSG9w==", "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "^4.3.4", - "@microsoft/1ds-post-js": "^4.3.4", - "@microsoft/applicationinsights-web-basic": "^3.3.4" + "@microsoft/1ds-core-js": "^4.3.10", + "@microsoft/1ds-post-js": "^4.3.10", + "@microsoft/applicationinsights-web-basic": "^3.3.10" }, "engines": { "vscode": "^1.75.0" @@ -261,19 +270,36 @@ "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k= sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true, + "license": "MIT" }, "node_modules/buffer-equal-constant-time": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", - "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==" + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "license": "BSD-3-Clause" + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } }, "node_modules/combined-stream": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", "dev": true, + "license": "MIT", "dependencies": { "delayed-stream": "~1.0.0" }, @@ -284,34 +310,207 @@ "node_modules/delayed-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk= sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.4.0" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/ecdsa-sig-formatter": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "license": "Apache-2.0", "dependencies": { "safe-buffer": "^5.0.1" } }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/form-data": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.0.tgz", - "integrity": "sha512-CKMFDglpbMi6PyN+brwB9Q/GOw0eAnsrEZDgcsH5Krhz5Od/haKHAX0NmQfha2zPPz0JpWzA7GJHGSnvCRLWsg==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", "dev": true, + "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" }, "engines": { "node": ">= 6" } }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/jsonwebtoken": { "version": "9.0.2", "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.2.tgz", @@ -334,21 +533,23 @@ } }, "node_modules/jwa": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.1.tgz", - "integrity": "sha512-qiLX/xhEEFKUAJ6FiBMbes3w9ATzyk5W7Hvzpa/SLYdxNtng+gcurvrI7TbACjIXlsJyr05/S1oUhZrc63evQA==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-1.4.2.tgz", + "integrity": "sha512-eeH5JO+21J78qMvTIDdBXidBd6nG2kZjg5Ohz/1fpa28Z4CcsWUzJ1ZZyFq/3z3N17aZy+ZuBoHljASbL1WfOw==", + "license": "MIT", "dependencies": { - "buffer-equal-constant-time": "1.0.1", + "buffer-equal-constant-time": "^1.0.1", "ecdsa-sig-formatter": "1.0.11", "safe-buffer": "^5.0.1" } }, "node_modules/jws": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.2.tgz", - "integrity": "sha512-YHlZCB6lMTllWDtSPHz/ZXTsi8S00usEV6v1tjq8tOUZzw7DpSDWVXjXDre6ed1w/pd495ODpHZYSdkRTsa0HA==", + "version": "3.2.3", + "resolved": "https://registry.npmjs.org/jws/-/jws-3.2.3.tgz", + "integrity": "sha512-byiJ0FLRdLdSVSReO/U4E7RoEyOCKnEnEPMjq3HxWtvzLsV08/i5RQKsFVNkCldrCaPr2vDNAOMsfs8T/Hze7g==", + "license": "MIT", "dependencies": { - "jwa": "^1.4.1", + "jwa": "^1.4.2", "safe-buffer": "^5.0.1" } }, @@ -391,22 +592,34 @@ "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==" }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/mime-db": { - "version": "1.44.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", - "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==", + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "2.1.27", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", - "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, + "license": "MIT", "dependencies": { - "mime-db": "1.44.0" + "mime-db": "1.52.0" }, "engines": { "node": ">= 0.6" @@ -437,7 +650,8 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/semver": { "version": "7.6.2", @@ -456,10 +670,11 @@ "integrity": "sha512-V+uqV66BOQnWxvI6HjDnE4VkInmYZUQ4dgB7gzaDyFyFSK1i1nF/j7DpS9UbQAgV9NaF1XpcyuavnM1qOeiEIg==" }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" }, "node_modules/uuid": { "version": "8.3.2", diff --git a/extensions/microsoft-authentication/package.json b/extensions/microsoft-authentication/package.json index 4170a7787cfa..17d6ebdcc55f 100644 --- a/extensions/microsoft-authentication/package.json +++ b/extensions/microsoft-authentication/package.json @@ -115,23 +115,6 @@ "tags": [ "onExP" ] - }, - "microsoft-authentication.clientIdVersion": { - "type": "string", - "default": "v1", - "enum": [ - "v2", - "v1" - ], - "enumDescriptions": [ - "%microsoft-authentication.clientIdVersion.enumDescriptions.v2%", - "%microsoft-authentication.clientIdVersion.enumDescriptions.v1%" - ], - "markdownDescription": "%microsoft-authentication.clientIdVersion.description%", - "tags": [ - "onExP", - "experimental" - ] } } } @@ -148,17 +131,17 @@ "watch-web": "npx webpack-cli --config extension-browser.webpack.config --mode none --watch --info-verbosity verbose" }, "devDependencies": { - "@types/node": "20.x", - "@types/node-fetch": "^2.5.7", - "@types/randombytes": "^2.0.0", - "@types/sha.js": "^2.4.0", - "@types/uuid": "8.0.0" + "@types/node": "25.x", + "@types/node-fetch": "^2.6.13", + "@types/randombytes": "^2.0.3", + "@types/sha.js": "^2.4.4", + "@types/uuid": "11.0.0" }, "dependencies": { "@azure/ms-rest-azure-env": "^2.0.0", - "@azure/msal-node": "^2.16.2", - "@azure/msal-node-extensions": "^1.5.0", - "@vscode/extension-telemetry": "^0.9.8", + "@azure/msal-node": "^5.0.2", + "@azure/msal-node-extensions": "^5.0.2", + "@vscode/extension-telemetry": "^1.4.0", "keytar": "file:./packageMocks/keytar", "vscode-tas-client": "^0.1.84" }, diff --git a/extensions/microsoft-authentication/package.nls.json b/extensions/microsoft-authentication/package.nls.json index ece95ac75c30..c8e0189c08f9 100644 --- a/extensions/microsoft-authentication/package.nls.json +++ b/extensions/microsoft-authentication/package.nls.json @@ -12,9 +12,6 @@ }, "microsoft-authentication.implementation.enumDescriptions.msal": "Use the Microsoft Authentication Library (MSAL) to sign in with a Microsoft account.", "microsoft-authentication.implementation.enumDescriptions.classic": "(deprecated) Use the classic authentication flow to sign in with a Microsoft account.", - "microsoft-authentication.clientIdVersion.description": "The version of the Microsoft Account client ID to use for signing in with a Microsoft account. Only change this if you have been asked to. The default is `v1`.", - "microsoft-authentication.clientIdVersion.enumDescriptions.v1": "Use the v1 Microsoft Account client ID to sign in with a Microsoft account.", - "microsoft-authentication.clientIdVersion.enumDescriptions.v2": "Use the v2 Microsoft Account client ID to sign in with a Microsoft account.", "microsoft-sovereign-cloud.environment.description": { "message": "The Sovereign Cloud to use for authentication. If you select `custom`, you must also set the `#microsoft-sovereign-cloud.customEnvironment#` setting.", "comment": [ diff --git a/extensions/microsoft-authentication/src/common/accountAccess.ts b/extensions/microsoft-authentication/src/common/accountAccess.ts index a8fdeefef987..27d56b0bc1b7 100644 --- a/extensions/microsoft-authentication/src/common/accountAccess.ts +++ b/extensions/microsoft-authentication/src/common/accountAccess.ts @@ -3,35 +3,52 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable, Event, EventEmitter, SecretStorage } from 'vscode'; +import { Disposable, Event, EventEmitter, LogOutputChannel, SecretStorage } from 'vscode'; import { AccountInfo } from '@azure/msal-node'; -interface IAccountAccess { +export interface IAccountAccess { onDidAccountAccessChange: Event; isAllowedAccess(account: AccountInfo): boolean; - setAllowedAccess(account: AccountInfo, allowed: boolean): void; + setAllowedAccess(account: AccountInfo, allowed: boolean): Promise; } -export class ScopedAccountAccess implements IAccountAccess { +export class ScopedAccountAccess implements IAccountAccess, Disposable { private readonly _onDidAccountAccessChangeEmitter = new EventEmitter(); readonly onDidAccountAccessChange = this._onDidAccountAccessChangeEmitter.event; - private readonly _accountAccessSecretStorage: AccountAccessSecretStorage; - private value = new Array(); - constructor( - private readonly _secretStorage: SecretStorage, - private readonly _cloudName: string, - private readonly _clientId: string, - private readonly _authority: string + private readonly _disposable: Disposable; + + private constructor( + private readonly _accountAccessSecretStorage: IAccountAccessSecretStorage, + disposables: Disposable[] = [] ) { - this._accountAccessSecretStorage = new AccountAccessSecretStorage(this._secretStorage, this._cloudName, this._clientId, this._authority); - this._accountAccessSecretStorage.onDidChange(() => this.update()); + this._disposable = Disposable.from( + ...disposables, + this._onDidAccountAccessChangeEmitter, + this._accountAccessSecretStorage.onDidChange(() => this.update()) + ); } - initialize() { - return this.update(); + static async create( + secretStorage: SecretStorage, + cloudName: string, + logger: LogOutputChannel, + migrations: { clientId: string; authority: string }[] | undefined, + ): Promise { + const storage = await AccountAccessSecretStorage.create(secretStorage, cloudName, logger, migrations); + const access = new ScopedAccountAccess(storage, [storage]); + await access.initialize(); + return access; + } + + dispose() { + this._disposable.dispose(); + } + + private async initialize(): Promise { + await this.update(); } isAllowedAccess(account: AccountInfo): boolean { @@ -60,19 +77,26 @@ export class ScopedAccountAccess implements IAccountAccess { } } -export class AccountAccessSecretStorage { +interface IAccountAccessSecretStorage { + get(): Promise; + store(value: string[]): Thenable; + delete(): Thenable; + onDidChange: Event; +} + +class AccountAccessSecretStorage implements IAccountAccessSecretStorage, Disposable { private _disposable: Disposable; - private readonly _onDidChangeEmitter = new EventEmitter; + private readonly _onDidChangeEmitter = new EventEmitter(); readonly onDidChange: Event = this._onDidChangeEmitter.event; - private readonly _key = `accounts-${this._cloudName}-${this._clientId}-${this._authority}`; + private readonly _key = `accounts-${this._cloudName}`; - constructor( + private constructor( private readonly _secretStorage: SecretStorage, private readonly _cloudName: string, - private readonly _clientId: string, - private readonly _authority: string + private readonly _logger: LogOutputChannel, + private readonly _migrations?: { clientId: string; authority: string }[], ) { this._disposable = Disposable.from( this._onDidChangeEmitter, @@ -84,6 +108,48 @@ export class AccountAccessSecretStorage { ); } + static async create( + secretStorage: SecretStorage, + cloudName: string, + logger: LogOutputChannel, + migrations?: { clientId: string; authority: string }[], + ): Promise { + const storage = new AccountAccessSecretStorage(secretStorage, cloudName, logger, migrations); + await storage.initialize(); + return storage; + } + + /** + * TODO: Remove this method after a release with the migration + */ + private async initialize(): Promise { + if (!this._migrations) { + return; + } + const current = await this.get(); + // If the secret storage already has the new key, we have already run the migration + if (current) { + return; + } + try { + const allValues = new Set(); + for (const { clientId, authority } of this._migrations) { + const oldKey = `accounts-${this._cloudName}-${clientId}-${authority}`; + const value = await this._secretStorage.get(oldKey); + if (value) { + const parsed = JSON.parse(value) as string[]; + parsed.forEach(v => allValues.add(v)); + } + } + if (allValues.size > 0) { + await this.store(Array.from(allValues)); + } + } catch (e) { + // Migration is best effort + this._logger.error(`Failed to migrate account access secret storage: ${e}`); + } + } + async get(): Promise { const value = await this._secretStorage.get(this._key); if (!value) { diff --git a/extensions/microsoft-authentication/src/common/cachePlugin.ts b/extensions/microsoft-authentication/src/common/cachePlugin.ts index 91b4f0ee6a8e..b87fdb78d9b5 100644 --- a/extensions/microsoft-authentication/src/common/cachePlugin.ts +++ b/extensions/microsoft-authentication/src/common/cachePlugin.ts @@ -6,7 +6,7 @@ import { ICachePlugin, TokenCacheContext } from '@azure/msal-node'; import { Disposable, EventEmitter, SecretStorage } from 'vscode'; -export class SecretStorageCachePlugin implements ICachePlugin { +export class SecretStorageCachePlugin implements ICachePlugin, Disposable { private readonly _onDidChange: EventEmitter = new EventEmitter(); readonly onDidChange = this._onDidChange.event; diff --git a/extensions/microsoft-authentication/src/common/publicClientCache.ts b/extensions/microsoft-authentication/src/common/publicClientCache.ts index 925a4d1a88c3..acc8ba0d3077 100644 --- a/extensions/microsoft-authentication/src/common/publicClientCache.ts +++ b/extensions/microsoft-authentication/src/common/publicClientCache.ts @@ -2,23 +2,22 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import type { AccountInfo, AuthenticationResult, InteractiveRequest, SilentFlowRequest } from '@azure/msal-node'; +import type { AccountInfo, AuthenticationResult, InteractiveRequest, RefreshTokenRequest, SilentFlowRequest } from '@azure/msal-node'; import type { Disposable, Event } from 'vscode'; -export interface ICachedPublicClientApplication extends Disposable { - initialize(): Promise; +export interface ICachedPublicClientApplication { onDidAccountsChange: Event<{ added: AccountInfo[]; changed: AccountInfo[]; deleted: AccountInfo[] }>; onDidRemoveLastAccount: Event; acquireTokenSilent(request: SilentFlowRequest): Promise; acquireTokenInteractive(request: InteractiveRequest): Promise; + acquireTokenByRefreshToken(request: RefreshTokenRequest): Promise; removeAccount(account: AccountInfo): Promise; accounts: AccountInfo[]; clientId: string; - authority: string; } export interface ICachedPublicClientApplicationManager { onDidAccountsChange: Event<{ added: AccountInfo[]; changed: AccountInfo[]; deleted: AccountInfo[] }>; - getOrCreate(clientId: string, authority: string): Promise; + getOrCreate(clientId: string, refreshTokensToMigrate?: string[]): Promise; getAll(): ICachedPublicClientApplication[]; } diff --git a/extensions/microsoft-authentication/src/common/scopeData.ts b/extensions/microsoft-authentication/src/common/scopeData.ts index a43f2c431dd4..88a0aad68cc1 100644 --- a/extensions/microsoft-authentication/src/common/scopeData.ts +++ b/extensions/microsoft-authentication/src/common/scopeData.ts @@ -3,21 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { workspace } from 'vscode'; - -const DEFAULT_CLIENT_ID_V1 = 'aebc6443-996d-45c2-90f0-388ff96faa56'; -const DEFAULT_TENANT_V1 = 'organizations'; -const DEFAULT_CLIENT_ID_V2 = 'c27c220f-ce2f-4904-927d-333864217eeb'; -const DEFAULT_TENANT_V2 = 'common'; +const DEFAULT_CLIENT_ID = 'aebc6443-996d-45c2-90f0-388ff96faa56'; +const DEFAULT_TENANT = 'organizations'; const OIDC_SCOPES = ['openid', 'email', 'profile', 'offline_access']; const GRAPH_TACK_ON_SCOPE = 'User.Read'; export class ScopeData { - - private readonly _defaultClientId: string; - private readonly _defaultTenant: string; - /** * The full list of scopes including: * * the original scopes passed to the constructor @@ -42,47 +34,57 @@ export class ScopeData { readonly clientId: string; /** - * The tenant ID to use for the token request. This is the value of the `VSCODE_TENANT:...` scope if present, otherwise the default tenant ID. + * The tenant ID or `organizations`, `common`, `consumers` to use for the token request. This is the value of the `VSCODE_TENANT:...` scope if present, otherwise it's the default. */ readonly tenant: string; - constructor(readonly originalScopes: readonly string[] = []) { - if (workspace.getConfiguration('microsoft-authentication').get<'v1' | 'v2'>('clientIdVersion') === 'v2') { - this._defaultClientId = DEFAULT_CLIENT_ID_V2; - this._defaultTenant = DEFAULT_TENANT_V2; - } else { - this._defaultClientId = DEFAULT_CLIENT_ID_V1; - this._defaultTenant = DEFAULT_TENANT_V1; - } + /** + * The tenant ID to use for the token request. This will only ever be a GUID if one was specified via the `VSCODE_TENANT:...` scope, otherwise undefined. + */ + readonly tenantId: string | undefined; + constructor(readonly originalScopes: readonly string[] = []) { const modifiedScopes = [...originalScopes]; modifiedScopes.sort(); this.allScopes = modifiedScopes; this.scopeStr = modifiedScopes.join(' '); this.scopesToSend = this.getScopesToSend(modifiedScopes); this.clientId = this.getClientId(this.allScopes); - this.tenant = this.getTenantId(this.allScopes); + this.tenant = this.getTenant(this.allScopes); + this.tenantId = this.getTenantId(this.tenant); } - private getClientId(scopes: string[]) { + private getClientId(scopes: string[]): string { return scopes.reduce((prev, current) => { if (current.startsWith('VSCODE_CLIENT_ID:')) { return current.split('VSCODE_CLIENT_ID:')[1]; } return prev; - }, undefined) ?? this._defaultClientId; + }, undefined) ?? DEFAULT_CLIENT_ID; } - private getTenantId(scopes: string[]) { + private getTenant(scopes: string[]): string { return scopes.reduce((prev, current) => { if (current.startsWith('VSCODE_TENANT:')) { return current.split('VSCODE_TENANT:')[1]; } return prev; - }, undefined) ?? this._defaultTenant; + }, undefined) ?? DEFAULT_TENANT; + } + + private getTenantId(tenant: string): string | undefined { + switch (tenant) { + case 'organizations': + case 'common': + case 'consumers': + // These are not valid tenant IDs, so we return undefined + return undefined; + default: + return this.tenant; + } } - private getScopesToSend(scopes: string[]) { + private getScopesToSend(scopes: string[]): string[] { const scopesToSend = scopes.filter(s => !s.startsWith('VSCODE_')); const set = new Set(scopesToSend); diff --git a/extensions/microsoft-authentication/src/common/test/scopeData.test.ts b/extensions/microsoft-authentication/src/common/test/scopeData.test.ts index 9250d7cecbda..4c70e4fd07c8 100644 --- a/extensions/microsoft-authentication/src/common/test/scopeData.test.ts +++ b/extensions/microsoft-authentication/src/common/test/scopeData.test.ts @@ -56,4 +56,21 @@ suite('ScopeData', () => { const scopeData = new ScopeData(['custom_scope', 'VSCODE_TENANT:some_tenant']); assert.strictEqual(scopeData.tenant, 'some_tenant'); }); + + test('should have tenantId be undefined if no VSCODE_TENANT scope is present', () => { + const scopeData = new ScopeData(['custom_scope']); + assert.strictEqual(scopeData.tenantId, undefined); + }); + + test('should have tenantId be undefined if typical tenant values are present', () => { + for (const element of ['common', 'organizations', 'consumers']) { + const scopeData = new ScopeData(['custom_scope', `VSCODE_TENANT:${element}`]); + assert.strictEqual(scopeData.tenantId, undefined); + } + }); + + test('should have tenantId be the value of VSCODE_TENANT scope if set to a specific value', () => { + const scopeData = new ScopeData(['custom_scope', 'VSCODE_TENANT:some_guid']); + assert.strictEqual(scopeData.tenantId, 'some_guid'); + }); }); diff --git a/extensions/microsoft-authentication/src/extensionV2.ts b/extensions/microsoft-authentication/src/extensionV2.ts index 9610af37977c..978603ad1322 100644 --- a/extensions/microsoft-authentication/src/extensionV2.ts +++ b/extensions/microsoft-authentication/src/extensionV2.ts @@ -49,14 +49,13 @@ async function initMicrosoftSovereignCloudAuthProvider( return undefined; } - const authProvider = new MsalAuthProvider( + const authProvider = await MsalAuthProvider.create( context, new MicrosoftSovereignCloudAuthenticationTelemetryReporter(context.extension.packageJSON.aiKey), window.createOutputChannel(l10n.t('Microsoft Sovereign Cloud Authentication'), { log: true }), uriHandler, env ); - await authProvider.initialize(); const disposable = authentication.registerAuthenticationProvider( 'microsoft-sovereign-cloud', authProviderName, @@ -70,13 +69,12 @@ async function initMicrosoftSovereignCloudAuthProvider( export async function activate(context: ExtensionContext, mainTelemetryReporter: MicrosoftAuthenticationTelemetryReporter) { const uriHandler = new UriEventHandler(); context.subscriptions.push(uriHandler); - const authProvider = new MsalAuthProvider( + const authProvider = await MsalAuthProvider.create( context, mainTelemetryReporter, Logger, uriHandler ); - await authProvider.initialize(); context.subscriptions.push(authentication.registerAuthenticationProvider( 'microsoft', 'Microsoft', diff --git a/extensions/microsoft-authentication/src/node/authProvider.ts b/extensions/microsoft-authentication/src/node/authProvider.ts index 40000e086200..a26008fb7802 100644 --- a/extensions/microsoft-authentication/src/node/authProvider.ts +++ b/extensions/microsoft-authentication/src/node/authProvider.ts @@ -7,7 +7,7 @@ import { AuthenticationGetSessionOptions, AuthenticationProvider, Authentication import { Environment } from '@azure/ms-rest-azure-env'; import { CachedPublicClientApplicationManager } from './publicClientCache'; import { UriEventHandler } from '../UriEventHandler'; -import { ICachedPublicClientApplication } from '../common/publicClientCache'; +import { ICachedPublicClientApplication, ICachedPublicClientApplicationManager } from '../common/publicClientCache'; import { MicrosoftAccountType, MicrosoftAuthenticationTelemetryReporter } from '../common/telemetryReporter'; import { ScopeData } from '../common/scopeData'; import { EventBufferer } from '../common/event'; @@ -22,7 +22,6 @@ const MSA_PASSTHRU_TID = 'f8cdef31-a31e-4b4a-93e4-5f571e91255a'; export class MsalAuthProvider implements AuthenticationProvider { private readonly _disposables: { dispose(): void }[]; - private readonly _publicClientManager: CachedPublicClientApplicationManager; private readonly _eventBufferer = new EventBufferer(); /** @@ -43,20 +42,15 @@ export class MsalAuthProvider implements AuthenticationProvider { */ onDidChangeSessions = this._onDidChangeSessionsEmitter.event; - constructor( + private constructor( private readonly _context: ExtensionContext, private readonly _telemetryReporter: MicrosoftAuthenticationTelemetryReporter, private readonly _logger: LogOutputChannel, private readonly _uriHandler: UriEventHandler, + private readonly _publicClientManager: ICachedPublicClientApplicationManager, private readonly _env: Environment = Environment.AzureCloud ) { this._disposables = _context.subscriptions; - this._publicClientManager = new CachedPublicClientApplicationManager( - _context.globalState, - _context.secrets, - this._logger, - this._env.name - ); const accountChangeEvent = this._eventBufferer.wrapEvent( this._publicClientManager.onDidAccountsChange, (last, newEvent) => { @@ -81,11 +75,24 @@ export class MsalAuthProvider implements AuthenticationProvider { )(e => this._handleAccountChange(e)); this._disposables.push( this._onDidChangeSessionsEmitter, - this._publicClientManager, accountChangeEvent ); } + static async create( + context: ExtensionContext, + telemetryReporter: MicrosoftAuthenticationTelemetryReporter, + logger: LogOutputChannel, + uriHandler: UriEventHandler, + env: Environment = Environment.AzureCloud + ): Promise { + const publicClientManager = await CachedPublicClientApplicationManager.create(context.secrets, logger, env.name); + context.subscriptions.push(publicClientManager); + const authProvider = new MsalAuthProvider(context, telemetryReporter, logger, uriHandler, publicClientManager, env); + await authProvider.initialize(); + return authProvider; + } + /** * Migrate sessions from the old secret storage to MSAL. * TODO: MSAL Migration. Remove this when we remove the old flow. @@ -109,14 +116,12 @@ export class MsalAuthProvider implements AuthenticationProvider { clientTenantMap.get(key)!.refreshTokens.push(session.refreshToken); } - for (const { clientId, tenant, refreshTokens } of clientTenantMap.values()) { - await this.getOrCreatePublicClientApplication(clientId, tenant, refreshTokens); + for (const { clientId, refreshTokens } of clientTenantMap.values()) { + await this._publicClientManager.getOrCreate(clientId, refreshTokens); } } - async initialize(): Promise { - await this._eventBufferer.bufferEventsAsync(() => this._publicClientManager.initialize()); - + private async initialize(): Promise { if (!this._context.globalState.get('msalMigration', false)) { await this._migrateSessions(); } @@ -173,8 +178,8 @@ export class MsalAuthProvider implements AuthenticationProvider { return allSessions; } - const cachedPca = await this.getOrCreatePublicClientApplication(scopeData.clientId, scopeData.tenant); - const sessions = await this.getAllSessionsForPca(cachedPca, scopeData.originalScopes, scopeData.scopesToSend, options?.account); + const cachedPca = await this._publicClientManager.getOrCreate(scopeData.clientId); + const sessions = await this.getAllSessionsForPca(cachedPca, scopeData, options?.account); this._logger.info(`[getSessions] [${scopeData.scopeStr}] returned ${sessions.length} session(s)`); return sessions; @@ -185,7 +190,7 @@ export class MsalAuthProvider implements AuthenticationProvider { // Do NOT use `scopes` beyond this place in the code. Use `scopeData` instead. this._logger.info('[createSession]', `[${scopeData.scopeStr}]`, 'starting'); - const cachedPca = await this.getOrCreatePublicClientApplication(scopeData.clientId, scopeData.tenant); + const cachedPca = await this._publicClientManager.getOrCreate(scopeData.clientId); // Used for showing a friendlier message to the user when the explicitly cancel a flow. let userCancelled: boolean | undefined; @@ -211,6 +216,7 @@ export class MsalAuthProvider implements AuthenticationProvider { : ExtensionHost.WebWorker, }); + const authority = new URL(scopeData.tenant, this._env.activeDirectoryEndpointUrl).toString(); let lastError: Error | undefined; for (const flow of flows) { if (flow !== flows[0]) { @@ -223,6 +229,7 @@ export class MsalAuthProvider implements AuthenticationProvider { try { const result = await flow.trigger({ cachedPca, + authority, scopes: scopeData.scopesToSend, loginHint: options.account?.label, windowHandle: window.nativeHandle ? Buffer.from(window.nativeHandle) : undefined, @@ -260,7 +267,7 @@ export class MsalAuthProvider implements AuthenticationProvider { if (account.homeAccountId === sessionId) { this._telemetryReporter.sendLogoutEvent(); promises.push(cachedPca.removeAccount(account)); - this._logger.info(`[removeSession] [${sessionId}] [${cachedPca.clientId}] [${cachedPca.authority}] removing session...`); + this._logger.info(`[removeSession] [${sessionId}] [${cachedPca.clientId}] removing session...`); } } } @@ -281,26 +288,69 @@ export class MsalAuthProvider implements AuthenticationProvider { //#endregion - private async getOrCreatePublicClientApplication(clientId: string, tenant: string, refreshTokensToMigrate?: string[]): Promise { - const authority = new URL(tenant, this._env.activeDirectoryEndpointUrl).toString(); - return await this._publicClientManager.getOrCreate(clientId, authority, refreshTokensToMigrate); - } - private async getAllSessionsForPca( cachedPca: ICachedPublicClientApplication, - originalScopes: readonly string[], - scopesToSend: string[], + scopeData: ScopeData, accountFilter?: AuthenticationSessionAccountInformation ): Promise { - const accounts = accountFilter + let filteredAccounts = accountFilter ? cachedPca.accounts.filter(a => a.homeAccountId === accountFilter.id) : cachedPca.accounts; + + // Group accounts by homeAccountId + const accountGroups = new Map(); + for (const account of filteredAccounts) { + const existing = accountGroups.get(account.homeAccountId) || []; + existing.push(account); + accountGroups.set(account.homeAccountId, existing); + } + + // Filter to one account per homeAccountId + filteredAccounts = Array.from(accountGroups.values()).map(accounts => { + if (accounts.length === 1) { + return accounts[0]; + } + + // If we have a specific tenant to target, prefer that one + if (scopeData.tenantId) { + const matchingTenant = accounts.find(a => a.tenantId === scopeData.tenantId); + if (matchingTenant) { + return matchingTenant; + } + } + + // Otherwise prefer the home tenant + return accounts.find(a => a.tenantId === a.idTokenClaims?.tid) || accounts[0]; + }); + + const authority = new URL(scopeData.tenant, this._env.activeDirectoryEndpointUrl).toString(); const sessions: AuthenticationSession[] = []; return this._eventBufferer.bufferEventsAsync(async () => { - for (const account of accounts) { + for (const account of filteredAccounts) { try { - const result = await cachedPca.acquireTokenSilent({ account, scopes: scopesToSend, redirectUri }); - sessions.push(this.sessionFromAuthenticationResult(result, originalScopes)); + let forceRefresh: true | undefined; + if (scopeData.tenantId) { + // If the tenants do not match, then we need to skip the cache + // to get a new token for the new tenant + if (account.tenantId !== scopeData.tenantId) { + forceRefresh = true; + } + } else { + // If we are requesting the home tenant and we don't yet have + // a token for the home tenant, we need to skip the cache + // to get a new token for the home tenant + if (account.tenantId !== account.idTokenClaims?.tid) { + forceRefresh = true; + } + } + const result = await cachedPca.acquireTokenSilent({ + account, + authority, + scopes: scopeData.scopesToSend, + redirectUri, + forceRefresh + }); + sessions.push(this.sessionFromAuthenticationResult(result, scopeData.originalScopes)); } catch (e) { // If we can't get a token silently, the account is probably in a bad state so we should skip it // MSAL will log this already, so we don't need to log it again diff --git a/extensions/microsoft-authentication/src/node/cachedPublicClientApplication.ts b/extensions/microsoft-authentication/src/node/cachedPublicClientApplication.ts index 8d081f1a825b..f0f4eb7b9bcd 100644 --- a/extensions/microsoft-authentication/src/node/cachedPublicClientApplication.ts +++ b/extensions/microsoft-authentication/src/node/cachedPublicClientApplication.ts @@ -3,14 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { PublicClientApplication, AccountInfo, Configuration, SilentFlowRequest, AuthenticationResult, InteractiveRequest, LogLevel, RefreshTokenRequest } from '@azure/msal-node'; +import { PublicClientApplication, AccountInfo, SilentFlowRequest, AuthenticationResult, InteractiveRequest, LogLevel, RefreshTokenRequest } from '@azure/msal-node'; import { NativeBrokerPlugin } from '@azure/msal-node-extensions'; -import { Disposable, Memento, SecretStorage, LogOutputChannel, window, ProgressLocation, l10n, EventEmitter } from 'vscode'; +import { Disposable, SecretStorage, LogOutputChannel, window, ProgressLocation, l10n, EventEmitter } from 'vscode'; import { raceCancellationAndTimeoutError } from '../common/async'; import { SecretStorageCachePlugin } from '../common/cachePlugin'; import { MsalLoggerOptions } from '../common/loggerOptions'; import { ICachedPublicClientApplication } from '../common/publicClientCache'; -import { ScopedAccountAccess } from '../common/accountAccess'; +import { IAccountAccess } from '../common/accountAccess'; export class CachedPublicClientApplication implements ICachedPublicClientApplication { // Core properties @@ -23,11 +23,10 @@ export class CachedPublicClientApplication implements ICachedPublicClientApplica private readonly _secretStorageCachePlugin = new SecretStorageCachePlugin( this._secretStorage, // Include the prefix as a differentiator to other secrets - `pca:${JSON.stringify({ clientId: this._clientId, authority: this._authority })}` + `pca:${this._clientId}` ); // Broker properties - private readonly _accountAccess = new ScopedAccountAccess(this._secretStorage, this._cloudName, this._clientId, this._authority); private readonly _isBrokerAvailable: boolean; //#region Events @@ -40,24 +39,20 @@ export class CachedPublicClientApplication implements ICachedPublicClientApplica //#endregion - constructor( + private constructor( private readonly _clientId: string, - private readonly _authority: string, - private readonly _cloudName: string, - private readonly _globalMemento: Memento, private readonly _secretStorage: SecretStorage, - private readonly _logger: LogOutputChannel + private readonly _accountAccess: IAccountAccess, + private readonly _logger: LogOutputChannel, ) { - // TODO:@TylerLeonhardt clean up old use of memento. Remove this in an iteration - this._globalMemento.update(`lastRemoval:${this._clientId}:${this._authority}`, undefined); const loggerOptions = new MsalLoggerOptions(_logger); const nativeBrokerPlugin = new NativeBrokerPlugin(); - this._isBrokerAvailable = nativeBrokerPlugin.isBrokerAvailable ?? false; + this._isBrokerAvailable = nativeBrokerPlugin.isBrokerAvailable; this._pca = new PublicClientApplication({ - auth: { clientId: _clientId, authority: _authority }, + auth: { clientId: _clientId }, system: { loggerOptions: { - correlationId: `${_clientId}] [${_authority}`, + correlationId: _clientId, loggerCallback: (level, message, containsPii) => loggerOptions.loggerCallback(level, message, containsPii), logLevel: LogLevel.Trace } @@ -68,18 +63,26 @@ export class CachedPublicClientApplication implements ICachedPublicClientApplica this._disposable = Disposable.from( this._registerOnSecretStorageChanged(), this._onDidAccountsChangeEmitter, - this._onDidRemoveLastAccountEmitter + this._onDidRemoveLastAccountEmitter, + this._secretStorageCachePlugin ); } get accounts(): AccountInfo[] { return this._accounts; } get clientId(): string { return this._clientId; } - get authority(): string { return this._authority; } - async initialize(): Promise { - if (this._isBrokerAvailable) { - await this._accountAccess.initialize(); - } + static async create( + clientId: string, + secretStorage: SecretStorage, + accountAccess: IAccountAccess, + logger: LogOutputChannel + ): Promise { + const app = new CachedPublicClientApplication(clientId, secretStorage, accountAccess, logger); + await app.initialize(); + return app; + } + + private async initialize(): Promise { await this._sequencer.queue(() => this._update()); } @@ -88,9 +91,9 @@ export class CachedPublicClientApplication implements ICachedPublicClientApplica } async acquireTokenSilent(request: SilentFlowRequest): Promise { - this._logger.debug(`[acquireTokenSilent] [${this._clientId}] [${this._authority}] [${request.scopes.join(' ')}] [${request.account.username}] starting...`); + this._logger.debug(`[acquireTokenSilent] [${this._clientId}] [${request.authority}] [${request.scopes.join(' ')}] [${request.account.username}] starting...`); let result = await this._sequencer.queue(() => this._pca.acquireTokenSilent(request)); - this._logger.debug(`[acquireTokenSilent] [${this._clientId}] [${this._authority}] [${request.scopes.join(' ')}] [${request.account.username}] got result`); + this._logger.debug(`[acquireTokenSilent] [${this._clientId}] [${request.authority}] [${request.scopes.join(' ')}] [${request.account.username}] got result`); // Check expiration of id token and if it's 5min before expiration, force a refresh. // this is what MSAL does for access tokens already so we're just adding it for id tokens since we care about those. // NOTE: Once we stop depending on id tokens for some things we can remove all of this. @@ -101,13 +104,13 @@ export class CachedPublicClientApplication implements ICachedPublicClientApplica * 1000 // convert to milliseconds ); if (fiveMinutesBefore < new Date()) { - this._logger.debug(`[acquireTokenSilent] [${this._clientId}] [${this._authority}] [${request.scopes.join(' ')}] [${request.account.username}] id token is expired or about to expire. Forcing refresh...`); + this._logger.debug(`[acquireTokenSilent] [${this._clientId}] [${request.authority}] [${request.scopes.join(' ')}] [${request.account.username}] id token is expired or about to expire. Forcing refresh...`); const newRequest = this._isBrokerAvailable // HACK: Broker doesn't support forceRefresh so we need to pass in claims which will force a refresh ? { ...request, claims: '{ "id_token": {}}' } : { ...request, forceRefresh: true }; result = await this._sequencer.queue(() => this._pca.acquireTokenSilent(newRequest)); - this._logger.debug(`[acquireTokenSilent] [${this._clientId}] [${this._authority}] [${request.scopes.join(' ')}] [${request.account.username}] got forced result`); + this._logger.debug(`[acquireTokenSilent] [${this._clientId}] [${request.authority}] [${request.scopes.join(' ')}] [${request.account.username}] got forced result`); } const newIdTokenExpirationInSecs = (result.idTokenClaims as { exp?: number }).exp; if (newIdTokenExpirationInSecs) { @@ -116,15 +119,15 @@ export class CachedPublicClientApplication implements ICachedPublicClientApplica * 1000 // convert to milliseconds ); if (fiveMinutesBefore < new Date()) { - this._logger.error(`[acquireTokenSilent] [${this._clientId}] [${this._authority}] [${request.scopes.join(' ')}] [${request.account.username}] id token is still expired.`); + this._logger.error(`[acquireTokenSilent] [${this._clientId}] [${request.authority}] [${request.scopes.join(' ')}] [${request.account.username}] id token is still expired.`); // HACK: Only for the Broker we try one more time with different claims to force a refresh. Why? We've seen the Broker caching tokens by the claims requested, thus // there has been a situation where both tokens are expired. if (this._isBrokerAvailable) { - this._logger.error(`[acquireTokenSilent] [${this._clientId}] [${this._authority}] [${request.scopes.join(' ')}] [${request.account.username}] forcing refresh with different claims...`); + this._logger.error(`[acquireTokenSilent] [${this._clientId}] [${request.authority}] [${request.scopes.join(' ')}] [${request.account.username}] forcing refresh with different claims...`); const newRequest = { ...request, claims: '{ "access_token": {}}' }; result = await this._sequencer.queue(() => this._pca.acquireTokenSilent(newRequest)); - this._logger.debug(`[acquireTokenSilent] [${this._clientId}] [${this._authority}] [${request.scopes.join(' ')}] [${request.account.username}] got forced result with different claims`); + this._logger.debug(`[acquireTokenSilent] [${this._clientId}] [${request.authority}] [${request.scopes.join(' ')}] [${request.account.username}] got forced result with different claims`); const newIdTokenExpirationInSecs = (result.idTokenClaims as { exp?: number }).exp; if (newIdTokenExpirationInSecs) { const fiveMinutesBefore = new Date( @@ -132,7 +135,7 @@ export class CachedPublicClientApplication implements ICachedPublicClientApplica * 1000 // convert to milliseconds ); if (fiveMinutesBefore < new Date()) { - this._logger.error(`[acquireTokenSilent] [${this._clientId}] [${this._authority}] [${request.scopes.join(' ')}] [${request.account.username}] id token is still expired.`); + this._logger.error(`[acquireTokenSilent] [${this._clientId}] [${request.authority}] [${request.scopes.join(' ')}] [${request.account.username}] id token is still expired.`); } } } @@ -140,15 +143,17 @@ export class CachedPublicClientApplication implements ICachedPublicClientApplica } } - if (result.account && !result.fromCache && this._verifyIfUsingBroker(result)) { - this._logger.debug(`[acquireTokenSilent] [${this._clientId}] [${this._authority}] [${request.scopes.join(' ')}] [${request.account.username}] firing event due to change`); + if (!result.account) { + this._logger.error(`[acquireTokenSilent] [${this._clientId}] [${request.authority}] [${request.scopes.join(' ')}] [${request.account.username}] no account found in result`); + } else if (!result.fromCache && this._verifyIfUsingBroker(result)) { + this._logger.debug(`[acquireTokenSilent] [${this._clientId}] [${request.authority}] [${request.scopes.join(' ')}] [${request.account.username}] firing event due to change`); this._onDidAccountsChangeEmitter.fire({ added: [], changed: [result.account], deleted: [] }); } return result; } async acquireTokenInteractive(request: InteractiveRequest): Promise { - this._logger.debug(`[acquireTokenInteractive] [${this._clientId}] [${this._authority}] [${request.scopes?.join(' ')}] loopbackClientOverride: ${request.loopbackClient ? 'true' : 'false'}`); + this._logger.debug(`[acquireTokenInteractive] [${this._clientId}] [${request.authority}] [${request.scopes?.join(' ')}] loopbackClientOverride: ${request.loopbackClient ? 'true' : 'false'}`); return await window.withProgress( { location: ProgressLocation.Notification, @@ -180,9 +185,17 @@ export class CachedPublicClientApplication implements ICachedPublicClientApplica * @param request a {@link RefreshTokenRequest} object that contains the refresh token and other parameters. * @returns an {@link AuthenticationResult} object that contains the result of the token acquisition operation. */ - async acquireTokenByRefreshToken(request: RefreshTokenRequest) { - this._logger.debug(`[acquireTokenByRefreshToken] [${this._clientId}] [${this._authority}] [${request.scopes.join(' ')}]`); - const result = await this._sequencer.queue(() => this._pca.acquireTokenByRefreshToken(request)); + async acquireTokenByRefreshToken(request: RefreshTokenRequest): Promise { + this._logger.debug(`[acquireTokenByRefreshToken] [${this._clientId}] [${request.authority}] [${request.scopes.join(' ')}]`); + const result = await this._sequencer.queue(async () => { + const result = await this._pca.acquireTokenByRefreshToken(request); + // Force an update so that the account cache is updated. + // TODO:@TylerLeonhardt The problem is, we use the sequencer for + // change events but we _don't_ use it for the accounts cache. + // We should probably use it for the accounts cache as well. + await this._update(); + return result; + }); if (result) { // this._setupRefresh(result); if (this._isBrokerAvailable && result.account) { @@ -213,7 +226,14 @@ export class CachedPublicClientApplication implements ICachedPublicClientApplica if (!result.fromNativeBroker) { return true; } - const key = result.account!.homeAccountId; + // The nativeAccountId is what the broker uses to differenciate all + // types of accounts. Even if the "account" is a duplicate of another because + // it's actaully a guest account in another tenant. + let key = result.account!.nativeAccountId; + if (!key) { + this._logger.error(`[verifyIfUsingBroker] [${this._clientId}] [${result.account!.username}] no nativeAccountId found. Using homeAccountId instead.`); + key = result.account!.homeAccountId; + } const lastSeen = this._lastSeen.get(key); const lastTimeAuthed = result.account!.idTokenClaims!.iat!; if (!lastSeen) { @@ -229,7 +249,7 @@ export class CachedPublicClientApplication implements ICachedPublicClientApplica private async _update() { const before = this._accounts; - this._logger.debug(`[update] [${this._clientId}] [${this._authority}] CachedPublicClientApplication update before: ${before.length}`); + this._logger.debug(`[update] [${this._clientId}] CachedPublicClientApplication update before: ${before.length}`); // Clear in-memory cache so we know we're getting account data from the SecretStorage this._pca.clearCache(); let after = await this._pca.getAllAccounts(); @@ -237,7 +257,7 @@ export class CachedPublicClientApplication implements ICachedPublicClientApplica after = after.filter(a => this._accountAccess.isAllowedAccess(a)); } this._accounts = after; - this._logger.debug(`[update] [${this._clientId}] [${this._authority}] CachedPublicClientApplication update after: ${after.length}`); + this._logger.debug(`[update] [${this._clientId}] CachedPublicClientApplication update after: ${after.length}`); const beforeSet = new Set(before.map(b => b.homeAccountId)); const afterSet = new Set(after.map(a => a.homeAccountId)); @@ -246,13 +266,13 @@ export class CachedPublicClientApplication implements ICachedPublicClientApplica const deleted = before.filter(b => !afterSet.has(b.homeAccountId)); if (added.length > 0 || deleted.length > 0) { this._onDidAccountsChangeEmitter.fire({ added, changed: [], deleted }); - this._logger.debug(`[update] [${this._clientId}] [${this._authority}] CachedPublicClientApplication accounts changed. added: ${added.length}, deleted: ${deleted.length}`); + this._logger.debug(`[update] [${this._clientId}] CachedPublicClientApplication accounts changed. added: ${added.length}, deleted: ${deleted.length}`); if (!after.length) { - this._logger.debug(`[update] [${this._clientId}] [${this._authority}] CachedPublicClientApplication final account deleted. Firing event.`); + this._logger.debug(`[update] [${this._clientId}] CachedPublicClientApplication final account deleted. Firing event.`); this._onDidRemoveLastAccountEmitter.fire(); } } - this._logger.debug(`[update] [${this._clientId}] [${this._authority}] CachedPublicClientApplication update complete`); + this._logger.debug(`[update] [${this._clientId}] CachedPublicClientApplication update complete`); } } diff --git a/extensions/microsoft-authentication/src/node/flows.ts b/extensions/microsoft-authentication/src/node/flows.ts index c6f40a943f61..ac678d8313dc 100644 --- a/extensions/microsoft-authentication/src/node/flows.ts +++ b/extensions/microsoft-authentication/src/node/flows.ts @@ -25,6 +25,7 @@ interface IMsalFlowOptions { interface IMsalFlowTriggerOptions { cachedPca: ICachedPublicClientApplication; + authority: string; scopes: string[]; loginHint?: string; windowHandle?: Buffer; @@ -45,11 +46,12 @@ class DefaultLoopbackFlow implements IMsalFlow { supportsWebWorkerExtensionHost: false }; - async trigger({ cachedPca, scopes, loginHint, windowHandle, logger }: IMsalFlowTriggerOptions): Promise { + async trigger({ cachedPca, authority, scopes, loginHint, windowHandle, logger }: IMsalFlowTriggerOptions): Promise { logger.info('Trying default msal flow...'); return await cachedPca.acquireTokenInteractive({ openBrowser: async (url: string) => { await env.openExternal(Uri.parse(url)); }, scopes, + authority, successTemplate: loopbackTemplate, errorTemplate: loopbackTemplate, loginHint, @@ -66,12 +68,13 @@ class UrlHandlerFlow implements IMsalFlow { supportsWebWorkerExtensionHost: false }; - async trigger({ cachedPca, scopes, loginHint, windowHandle, logger, uriHandler }: IMsalFlowTriggerOptions): Promise { + async trigger({ cachedPca, authority, scopes, loginHint, windowHandle, logger, uriHandler }: IMsalFlowTriggerOptions): Promise { logger.info('Trying protocol handler flow...'); const loopbackClient = new UriHandlerLoopbackClient(uriHandler, redirectUri, logger); return await cachedPca.acquireTokenInteractive({ openBrowser: (url: string) => loopbackClient.openBrowser(url), scopes, + authority, loopbackClient, loginHint, prompt: loginHint ? undefined : 'select_account', diff --git a/extensions/microsoft-authentication/src/node/publicClientCache.ts b/extensions/microsoft-authentication/src/node/publicClientCache.ts index f4f19ff8df53..16ccb80321fd 100644 --- a/extensions/microsoft-authentication/src/node/publicClientCache.ts +++ b/extensions/microsoft-authentication/src/node/publicClientCache.ts @@ -7,6 +7,7 @@ import { AccountInfo } from '@azure/msal-node'; import { SecretStorage, LogOutputChannel, Disposable, EventEmitter, Memento, Event } from 'vscode'; import { ICachedPublicClientApplication, ICachedPublicClientApplicationManager } from '../common/publicClientCache'; import { CachedPublicClientApplication } from './cachedPublicClientApplication'; +import { IAccountAccess, ScopedAccountAccess } from '../common/accountAccess'; export interface IPublicClientApplicationInfo { clientId: string; @@ -14,56 +15,68 @@ export interface IPublicClientApplicationInfo { } export class CachedPublicClientApplicationManager implements ICachedPublicClientApplicationManager { - // The key is the clientId and authority JSON stringified - private readonly _pcas = new Map(); + // The key is the clientId + private readonly _pcas = new Map(); private readonly _pcaDisposables = new Map(); private _disposable: Disposable; - private _pcasSecretStorage: PublicClientApplicationsSecretStorage; private readonly _onDidAccountsChangeEmitter = new EventEmitter<{ added: AccountInfo[]; changed: AccountInfo[]; deleted: AccountInfo[] }>(); readonly onDidAccountsChange = this._onDidAccountsChangeEmitter.event; - constructor( - private readonly _globalMemento: Memento, + private constructor( + private readonly _pcasSecretStorage: IPublicClientApplicationSecretStorage, + private readonly _accountAccess: IAccountAccess, private readonly _secretStorage: SecretStorage, private readonly _logger: LogOutputChannel, - private readonly _cloudName: string + disposables: Disposable[] ) { - this._pcasSecretStorage = new PublicClientApplicationsSecretStorage(_secretStorage, _cloudName); this._disposable = Disposable.from( - this._pcasSecretStorage, + ...disposables, this._registerSecretStorageHandler(), this._onDidAccountsChangeEmitter ); } + static async create( + secretStorage: SecretStorage, + logger: LogOutputChannel, + cloudName: string + ): Promise { + const pcasSecretStorage = await PublicClientApplicationsSecretStorage.create(secretStorage, cloudName); + // TODO: Remove the migrations in a version + const migrations = await pcasSecretStorage.getOldValue(); + const accountAccess = await ScopedAccountAccess.create(secretStorage, cloudName, logger, migrations); + const manager = new CachedPublicClientApplicationManager(pcasSecretStorage, accountAccess, secretStorage, logger, [pcasSecretStorage, accountAccess]); + await manager.initialize(); + return manager; + } + private _registerSecretStorageHandler() { return this._pcasSecretStorage.onDidChange(() => this._handleSecretStorageChange()); } - async initialize() { + private async initialize() { this._logger.debug('[initialize] Initializing PublicClientApplicationManager'); - let keys: string[] | undefined; + let clientIds: string[] | undefined; try { - keys = await this._pcasSecretStorage.get(); + clientIds = await this._pcasSecretStorage.get(); } catch (e) { // data is corrupted this._logger.error('[initialize] Error initializing PublicClientApplicationManager:', e); await this._pcasSecretStorage.delete(); } - if (!keys) { + if (!clientIds) { return; } const promises = new Array>(); - for (const key of keys) { + for (const clientId of clientIds) { try { - const { clientId, authority } = JSON.parse(key) as IPublicClientApplicationInfo; // Load the PCA in memory - promises.push(this._doCreatePublicClientApplication(clientId, authority, key)); + promises.push(this._doCreatePublicClientApplication(clientId)); } catch (e) { - this._logger.error('[initialize] Error intitializing PCA:', key); + this._logger.error('[initialize] Error intitializing PCA:', clientId); } } @@ -75,11 +88,11 @@ export class CachedPublicClientApplicationManager implements ICachedPublicClient } else { if (!result.value.accounts.length) { pcasChanged = true; - const pcaKey = JSON.stringify({ clientId: result.value.clientId, authority: result.value.authority }); - this._pcaDisposables.get(pcaKey)?.dispose(); - this._pcaDisposables.delete(pcaKey); - this._pcas.delete(pcaKey); - this._logger.debug(`[initialize] [${result.value.clientId}] [${result.value.authority}] PCA disposed because it's empty.`); + const clientId = result.value.clientId; + this._pcaDisposables.get(clientId)?.dispose(); + this._pcaDisposables.delete(clientId); + this._pcas.delete(clientId); + this._logger.debug(`[initialize] [${clientId}] PCA disposed because it's empty.`); } } } @@ -94,43 +107,39 @@ export class CachedPublicClientApplicationManager implements ICachedPublicClient Disposable.from(...this._pcaDisposables.values()).dispose(); } - async getOrCreate(clientId: string, authority: string, refreshTokensToMigrate?: string[]): Promise { - // Use the clientId and authority as the key - const pcasKey = JSON.stringify({ clientId, authority }); - let pca = this._pcas.get(pcasKey); + async getOrCreate(clientId: string, refreshTokensToMigrate?: string[]): Promise { + let pca = this._pcas.get(clientId); if (pca) { - this._logger.debug(`[getOrCreate] [${clientId}] [${authority}] PublicClientApplicationManager cache hit`); + this._logger.debug(`[getOrCreate] [${clientId}] PublicClientApplicationManager cache hit`); } else { - this._logger.debug(`[getOrCreate] [${clientId}] [${authority}] PublicClientApplicationManager cache miss, creating new PCA...`); - pca = await this._doCreatePublicClientApplication(clientId, authority, pcasKey); + this._logger.debug(`[getOrCreate] [${clientId}] PublicClientApplicationManager cache miss, creating new PCA...`); + pca = await this._doCreatePublicClientApplication(clientId); await this._storePublicClientApplications(); - this._logger.debug(`[getOrCreate] [${clientId}] [${authority}] PCA created.`); + this._logger.debug(`[getOrCreate] [${clientId}] PCA created.`); } // TODO: MSAL Migration. Remove this when we remove the old flow. if (refreshTokensToMigrate?.length) { - this._logger.debug(`[getOrCreate] [${clientId}] [${authority}] Migrating refresh tokens to PCA...`); + this._logger.debug(`[getOrCreate] [${clientId}] Migrating refresh tokens to PCA...`); for (const refreshToken of refreshTokensToMigrate) { try { // Use the refresh token to acquire a result. This will cache the refresh token for future operations. // The scopes don't matter here since we can create any token from the refresh token. const result = await pca.acquireTokenByRefreshToken({ refreshToken, forceCache: true, scopes: [] }); if (result?.account) { - this._logger.debug(`[getOrCreate] [${clientId}] [${authority}] Refresh token migrated to PCA.`); + this._logger.debug(`[getOrCreate] [${clientId}] Refresh token migrated to PCA.`); } } catch (e) { - this._logger.error(`[getOrCreate] [${clientId}] [${authority}] Error migrating refresh token:`, e); + this._logger.error(`[getOrCreate] [${clientId}] Error migrating refresh token:`, e); } } - // reinitialize the PCA so the account is properly cached - await pca.initialize(); } return pca; } - private async _doCreatePublicClientApplication(clientId: string, authority: string, pcasKey: string) { - const pca = new CachedPublicClientApplication(clientId, authority, this._cloudName, this._globalMemento, this._secretStorage, this._logger); - this._pcas.set(pcasKey, pca); + private async _doCreatePublicClientApplication(clientId: string): Promise { + const pca = await CachedPublicClientApplication.create(clientId, this._secretStorage, this._accountAccess, this._logger); + this._pcas.set(clientId, pca); const disposable = Disposable.from( pca, pca.onDidAccountsChange(e => this._onDidAccountsChangeEmitter.fire(e)), @@ -138,15 +147,17 @@ export class CachedPublicClientApplicationManager implements ICachedPublicClient // The PCA has no more accounts, so we can dispose it so we're not keeping it // around forever. disposable.dispose(); - this._pcaDisposables.delete(pcasKey); - this._pcas.delete(pcasKey); - this._logger.debug(`[_doCreatePublicClientApplication] [${clientId}] [${authority}] PCA disposed. Firing off storing of PCAs...`); + this._pcaDisposables.delete(clientId); + this._pcas.delete(clientId); + this._logger.debug(`[_doCreatePublicClientApplication] [${clientId}] PCA disposed. Firing off storing of PCAs...`); void this._storePublicClientApplications(); }) ); - this._pcaDisposables.set(pcasKey, disposable); - // Intialize the PCA after the `onDidAccountsChange` is set so we get initial state. - await pca.initialize(); + this._pcaDisposables.set(clientId, disposable); + // Fire for the initial state and only if accounts exist + if (pca.accounts.length > 0) { + this._onDidAccountsChangeEmitter.fire({ added: pca.accounts, changed: [], deleted: [] }); + } return pca; } @@ -183,15 +194,14 @@ export class CachedPublicClientApplicationManager implements ICachedPublicClient } // Handle the new ones - for (const newPca of pcaKeysFromStorage) { + for (const clientId of pcaKeysFromStorage) { try { - const { clientId, authority } = JSON.parse(newPca); - this._logger.debug(`[_handleSecretStorageChange] [${clientId}] [${authority}] Creating new PCA that was created in another window...`); - await this._doCreatePublicClientApplication(clientId, authority, newPca); - this._logger.debug(`[_handleSecretStorageChange] [${clientId}] [${authority}] PCA created.`); + this._logger.debug(`[_handleSecretStorageChange] [${clientId}] Creating new PCA that was created in another window...`); + await this._doCreatePublicClientApplication(clientId); + this._logger.debug(`[_handleSecretStorageChange] [${clientId}] PCA created.`); } catch (_e) { // This really shouldn't happen, but should we do something about this? - this._logger.error(`Failed to parse new PublicClientApplication: ${newPca}`); + this._logger.error(`Failed to create new PublicClientApplication: ${clientId}`); continue; } } @@ -204,15 +214,24 @@ export class CachedPublicClientApplicationManager implements ICachedPublicClient } } -class PublicClientApplicationsSecretStorage { +interface IPublicClientApplicationSecretStorage { + get(): Promise; + getOldValue(): Promise<{ clientId: string; authority: string }[] | undefined>; + store(value: string[]): Thenable; + delete(): Thenable; + onDidChange: Event; +} + +class PublicClientApplicationsSecretStorage implements IPublicClientApplicationSecretStorage, Disposable { private _disposable: Disposable; private readonly _onDidChangeEmitter = new EventEmitter; readonly onDidChange: Event = this._onDidChangeEmitter.event; - private readonly _key = `publicClientApplications-${this._cloudName}`; + private readonly _oldKey = `publicClientApplications-${this._cloudName}`; + private readonly _key = `publicClients-${this._cloudName}`; - constructor(private readonly _secretStorage: SecretStorage, private readonly _cloudName: string) { + private constructor(private readonly _secretStorage: SecretStorage, private readonly _cloudName: string) { this._disposable = Disposable.from( this._onDidChangeEmitter, this._secretStorage.onDidChange(e => { @@ -223,6 +242,30 @@ class PublicClientApplicationsSecretStorage { ); } + static async create(secretStorage: SecretStorage, cloudName: string): Promise { + const storage = new PublicClientApplicationsSecretStorage(secretStorage, cloudName); + await storage.initialize(); + return storage; + } + + /** + * Runs the migration. + * TODO: Remove this after a version. + */ + private async initialize() { + const oldValue = await this.getOldValue(); + if (!oldValue) { + return; + } + const newValue = await this.get() ?? []; + for (const { clientId } of oldValue) { + if (!newValue.includes(clientId)) { + newValue.push(clientId); + } + } + await this.store(newValue); + } + async get(): Promise { const value = await this._secretStorage.get(this._key); if (!value) { @@ -231,6 +274,25 @@ class PublicClientApplicationsSecretStorage { return JSON.parse(value); } + /** + * Old representation of data that included the authority. This should be removed in a version or 2. + * @returns An array of objects with clientId and authority + */ + async getOldValue(): Promise<{ clientId: string; authority: string }[] | undefined> { + const value = await this._secretStorage.get(this._oldKey); + if (!value) { + return undefined; + } + const result: { clientId: string; authority: string }[] = []; + for (const stringifiedObj of JSON.parse(value)) { + const obj = JSON.parse(stringifiedObj); + if (obj.clientId && obj.authority) { + result.push(obj); + } + } + return result; + } + store(value: string[]): Thenable { return this._secretStorage.store(this._key, JSON.stringify(value)); } diff --git a/extensions/notebook-renderers/package-lock.json b/extensions/notebook-renderers/package-lock.json index 8dbc5f5ad4c2..ad0178475cd6 100644 --- a/extensions/notebook-renderers/package-lock.json +++ b/extensions/notebook-renderers/package-lock.json @@ -9,28 +9,215 @@ "version": "1.0.0", "license": "MIT", "devDependencies": { - "@types/jsdom": "^21.1.0", - "@types/vscode-notebook-renderer": "^1.60.0", - "jsdom": "^21.1.1" + "@types/jsdom": "^27.0.0", + "@types/vscode-notebook-renderer": "^1.72.4", + "jsdom": "^27.4.0" }, "engines": { "vscode": "^1.57.0" } }, - "node_modules/@tootallnate/once": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-2.0.0.tgz", - "integrity": "sha512-XCuKFP5PS55gnMVu3dty8KPatLqUoy/ZYzDzAGCQ8JNFCkLXzmI7vNHCR+XpbZaMWQK/vQubr7PkYq8g470J/A==", + "node_modules/@acemir/cssom": { + "version": "0.9.30", + "resolved": "https://registry.npmjs.org/@acemir/cssom/-/cssom-0.9.30.tgz", + "integrity": "sha512-9CnlMCI0LmCIq0olalQqdWrJHPzm0/tw3gzOA9zJSgvFX7Xau3D24mAGa4BtwxwY69nsuJW6kQqqCzf/mEcQgg==", "dev": true, + "license": "MIT" + }, + "node_modules/@asamuzakjp/css-color": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/@asamuzakjp/css-color/-/css-color-4.1.1.tgz", + "integrity": "sha512-B0Hv6G3gWGMn0xKJ0txEi/jM5iFpT3MfDxmhZFb4W047GvytCf1DHQ1D69W3zHI4yWe2aTZAA0JnbMZ7Xc8DuQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@csstools/css-calc": "^2.1.4", + "@csstools/css-color-parser": "^3.1.0", + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4", + "lru-cache": "^11.2.4" + } + }, + "node_modules/@asamuzakjp/dom-selector": { + "version": "6.7.6", + "resolved": "https://registry.npmjs.org/@asamuzakjp/dom-selector/-/dom-selector-6.7.6.tgz", + "integrity": "sha512-hBaJER6A9MpdG3WgdlOolHmbOYvSk46y7IQN/1+iqiCuUu6iWdQrs9DGKF8ocqsEqWujWf/V7b7vaDgiUmIvUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@asamuzakjp/nwsapi": "^2.3.9", + "bidi-js": "^1.0.3", + "css-tree": "^3.1.0", + "is-potential-custom-element-name": "^1.0.1", + "lru-cache": "^11.2.4" + } + }, + "node_modules/@asamuzakjp/nwsapi": { + "version": "2.3.9", + "resolved": "https://registry.npmjs.org/@asamuzakjp/nwsapi/-/nwsapi-2.3.9.tgz", + "integrity": "sha512-n8GuYSrI9bF7FFZ/SjhwevlHc8xaVlb/7HmHelnc/PZXBD2ZR49NnN9sMMuDdEGPeeRQ5d0hqlSlEpgCX3Wl0Q==", + "dev": true, + "license": "MIT" + }, + "node_modules/@csstools/color-helpers": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/@csstools/color-helpers/-/color-helpers-5.1.0.tgz", + "integrity": "sha512-S11EXWJyy0Mz5SYvRmY8nJYTFFd1LCNV+7cXyAgQtOOuzb4EsgfqDufL+9esx72/eLhsRdGZwaldu/h+E4t4BA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/css-calc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@csstools/css-calc/-/css-calc-2.1.4.tgz", + "integrity": "sha512-3N8oaj+0juUw/1H3YwmDDJXCgTB1gKU6Hc/bB502u9zR0q2vd786XJH9QfrKIEgFlZmhZiq6epXl4rHqhzsIgQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-color-parser": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@csstools/css-color-parser/-/css-color-parser-3.1.0.tgz", + "integrity": "sha512-nbtKwh3a6xNVIp/VRuXV64yTKnb1IjTAEEh3irzS+HkKjAOYLTGNb9pmVNntZ8iVBHcWDA2Dof0QtPgFI1BaTA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "dependencies": { + "@csstools/color-helpers": "^5.1.0", + "@csstools/css-calc": "^2.1.4" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-parser-algorithms": "^3.0.5", + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-parser-algorithms": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@csstools/css-parser-algorithms/-/css-parser-algorithms-3.0.5.tgz", + "integrity": "sha512-DaDeUkXZKjdGhgYaHNJTV9pV7Y9B3b644jCLs9Upc3VeNGg6LWARAT6O+Q+/COo+2gg/bM5rhpMAtf70WqfBdQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", "engines": { - "node": ">= 10" + "node": ">=18" + }, + "peerDependencies": { + "@csstools/css-tokenizer": "^3.0.4" + } + }, + "node_modules/@csstools/css-syntax-patches-for-csstree": { + "version": "1.0.22", + "resolved": "https://registry.npmjs.org/@csstools/css-syntax-patches-for-csstree/-/css-syntax-patches-for-csstree-1.0.22.tgz", + "integrity": "sha512-qBcx6zYlhleiFfdtzkRgwNC7VVoAwfK76Vmsw5t+PbvtdknO9StgRk7ROvq9so1iqbdW4uLIDAsXRsTfUrIoOw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT-0", + "engines": { + "node": ">=18" + } + }, + "node_modules/@csstools/css-tokenizer": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@csstools/css-tokenizer/-/css-tokenizer-3.0.4.tgz", + "integrity": "sha512-Vd/9EVDiu6PPJt9yAh6roZP6El1xHrdvIVGjyBsHR0RYwNHgL7FJPyIIW4fANJNG6FtyZfvlRPpFI4ZM/lubvw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/csstools" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/csstools" + } + ], + "license": "MIT", + "engines": { + "node": ">=18" + } + }, + "node_modules/@exodus/bytes": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@exodus/bytes/-/bytes-1.6.0.tgz", + "integrity": "sha512-y32mI9627q5LR/L8fLc4YyDRJQOi+jK0D9okzLilAdiU3F9we3zC7Y7CFrR/8vAvUyv7FgBAYcNHtvbmhKCFcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" + }, + "peerDependencies": { + "@exodus/crypto": "^1.0.0-rc.4" + }, + "peerDependenciesMeta": { + "@exodus/crypto": { + "optional": true + } } }, "node_modules/@types/jsdom": { - "version": "21.1.0", - "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-21.1.0.tgz", - "integrity": "sha512-leWreJOdnuIxq9Y70tBVm/bvTuh31DSlF/r4l7Cfi4uhVQqLHD0Q4v301GMisEMwwbMgF7ZKxuZ+Jbd4NcdmRw==", + "version": "27.0.0", + "resolved": "https://registry.npmjs.org/@types/jsdom/-/jsdom-27.0.0.tgz", + "integrity": "sha512-NZyFl/PViwKzdEkQg96gtnB8wm+1ljhdDay9ahn4hgb+SfVtPCbm3TlmDUFXTA+MGN3CijicnMhG18SI5H3rFw==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*", "@types/tough-cookie": "*", @@ -50,112 +237,83 @@ "dev": true }, "node_modules/@types/vscode-notebook-renderer": { - "version": "1.60.0", - "resolved": "https://registry.npmjs.org/@types/vscode-notebook-renderer/-/vscode-notebook-renderer-1.60.0.tgz", - "integrity": "sha512-u7TD2uuEZTVuitx0iijOJdKI0JLiQP6PsSBSRy2XmHXUOXcp5p1S56NrjOEDoF+PIHd3NL3eO6KTRSf5nukDqQ==", - "dev": true - }, - "node_modules/abab": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.6.tgz", - "integrity": "sha512-j2afSsaIENvHZN2B8GOpF566vZ5WVk5opAiMTvWgaQT8DkbOqsTfvNAvHoRGU2zzP8cPoqys+xHTRDWW8L+/BA==", - "deprecated": "Use your platform's native atob() and btoa() methods instead", - "dev": true - }, - "node_modules/acorn": { - "version": "8.8.2", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.8.2.tgz", - "integrity": "sha512-xjIYgE8HBrkpd/sJqOGNspf8uHG+NOHGOw6a/Urj8taM2EXfdNAH2oFcPeIFfsv3+kz/mJrS5VuMqbNLjCa2vw==", + "version": "1.72.4", + "resolved": "https://registry.npmjs.org/@types/vscode-notebook-renderer/-/vscode-notebook-renderer-1.72.4.tgz", + "integrity": "sha512-bdKO41c6Dc24pH/O/eM/jqfCwGH4zc76o/g/6Gt1y/eg/bvvqP2/VpbV+Sa5Te2sZekFRcbYnSSFTKo3wcVGUg==", "dev": true, - "bin": { - "acorn": "bin/acorn" - }, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/acorn-globals": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-7.0.1.tgz", - "integrity": "sha512-umOSDSDrfHbTNPuNpC2NSnnA3LUrqpevPb4T9jRx4MagXNS0rs+gwiTcAvqCRmsD6utzsrzNt+ebm00SNWiC3Q==", - "dev": true, - "dependencies": { - "acorn": "^8.1.0", - "acorn-walk": "^8.0.2" - } + "license": "MIT" }, - "node_modules/acorn-walk": { - "version": "8.2.0", - "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-8.2.0.tgz", - "integrity": "sha512-k+iyHEuPgSw6SbuDpGQM+06HQUa04DZ3o+F6CSzXMvvI5KMvnaEqXe+YVe555R9nn6GPt404fos4wcgpw12SDA==", + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.4.0" + "node": ">= 14" } }, - "node_modules/agent-base": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", - "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", + "node_modules/bidi-js": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/bidi-js/-/bidi-js-1.0.3.tgz", + "integrity": "sha512-RKshQI1R3YQ+n9YJz2QQ147P66ELpa1FQEg20Dk8oW9t2KgLbpDLLp9aGZ7y8WHSshDknG0bknqGw5/tyCs5tw==", "dev": true, + "license": "MIT", "dependencies": { - "debug": "4" - }, - "engines": { - "node": ">= 6.0.0" + "require-from-string": "^2.0.2" } }, - "node_modules/asynckit": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", - "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", - "dev": true - }, - "node_modules/combined-stream": { - "version": "1.0.8", - "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", - "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "node_modules/css-tree": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/css-tree/-/css-tree-3.1.0.tgz", + "integrity": "sha512-0eW44TGN5SQXU1mWSkKwFstI/22X2bG1nYzZTYMAWjylYURhse752YgbE4Cx46AC+bAvI+/dYTPRk1LqSUnu6w==", "dev": true, + "license": "MIT", "dependencies": { - "delayed-stream": "~1.0.0" + "mdn-data": "2.12.2", + "source-map-js": "^1.0.1" }, "engines": { - "node": ">= 0.8" + "node": "^10 || ^12.20.0 || ^14.13.0 || >=15.0.0" } }, "node_modules/cssstyle": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-3.0.0.tgz", - "integrity": "sha512-N4u2ABATi3Qplzf0hWbVCdjenim8F3ojEXpBDF5hBpjzW182MjNGLqfmQ0SkSPeQ+V86ZXgeH8aXj6kayd4jgg==", + "version": "5.3.5", + "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-5.3.5.tgz", + "integrity": "sha512-GlsEptulso7Jg0VaOZ8BXQi3AkYM5BOJKEO/rjMidSCq70FkIC5y0eawrCXeYzxgt3OCf4Ls+eoxN+/05vN0Ag==", "dev": true, + "license": "MIT", "dependencies": { - "rrweb-cssom": "^0.6.0" + "@asamuzakjp/css-color": "^4.1.1", + "@csstools/css-syntax-patches-for-csstree": "^1.0.21", + "css-tree": "^3.1.0" }, "engines": { - "node": ">=14" + "node": ">=20" } }, "node_modules/data-urls": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-4.0.0.tgz", - "integrity": "sha512-/mMTei/JXPqvFqQtfyTowxmJVwr2PVAeCcDxyFf6LhoOu/09TX2OX3kb2wzi4DMXcfj4OItwDOnhl5oziPnT6g==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-6.0.0.tgz", + "integrity": "sha512-BnBS08aLUM+DKamupXs3w2tJJoqU+AkaE/+6vQxi/G/DPmIZFJJp9Dkb1kM03AZx8ADehDUZgsNxju3mPXZYIA==", "dev": true, + "license": "MIT", "dependencies": { - "abab": "^2.0.6", - "whatwg-mimetype": "^3.0.0", - "whatwg-url": "^12.0.0" + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^15.0.0" }, "engines": { - "node": ">=14" + "node": ">=20" } }, "node_modules/debug": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", - "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, + "license": "MIT", "dependencies": { - "ms": "2.1.2" + "ms": "^2.1.3" }, "engines": { "node": ">=6.0" @@ -167,38 +325,11 @@ } }, "node_modules/decimal.js": { - "version": "10.4.3", - "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.4.3.tgz", - "integrity": "sha512-VBBaLc1MgL5XpzgIP7ny5Z6Nx3UrRkIViUkPUdtl9aya5amy3De1gsUUSB1g3+3sExYNjCAsAznmukyxCb1GRA==", - "dev": true - }, - "node_modules/deep-is": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", - "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", - "dev": true - }, - "node_modules/delayed-stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", - "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", - "dev": true, - "engines": { - "node": ">=0.4.0" - } - }, - "node_modules/domexception": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/domexception/-/domexception-4.0.0.tgz", - "integrity": "sha512-A2is4PLG+eeSfoTMA95/s4pvAoSo2mKtiM5jlHkAVewmiO8ISFTFKZjH7UAM1Atli/OT/7JHOrJRJiMKUZKYBw==", - "deprecated": "Use your platform's native DOMException instead", + "version": "10.6.0", + "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.6.0.tgz", + "integrity": "sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==", "dev": true, - "dependencies": { - "webidl-conversions": "^7.0.0" - }, - "engines": { - "node": ">=12" - } + "license": "MIT" }, "node_modules/entities": { "version": "4.4.0", @@ -212,128 +343,45 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/escodegen": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-2.0.0.tgz", - "integrity": "sha512-mmHKys/C8BFUGI+MAWNcSYoORYLMdPzjrknd2Vc+bUsjN5bXcr8EhrNB+UTqfL1y3I9c4fw2ihgtMPQLBRiQxw==", - "dev": true, - "dependencies": { - "esprima": "^4.0.1", - "estraverse": "^5.2.0", - "esutils": "^2.0.2", - "optionator": "^0.8.1" - }, - "bin": { - "escodegen": "bin/escodegen.js", - "esgenerate": "bin/esgenerate.js" - }, - "engines": { - "node": ">=6.0" - }, - "optionalDependencies": { - "source-map": "~0.6.1" - } - }, - "node_modules/esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true, - "bin": { - "esparse": "bin/esparse.js", - "esvalidate": "bin/esvalidate.js" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/estraverse": { - "version": "5.3.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", - "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, - "node_modules/esutils": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", - "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/fast-levenshtein": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", - "dev": true - }, - "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", - "dev": true, - "dependencies": { - "asynckit": "^0.4.0", - "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/html-encoding-sniffer": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-3.0.0.tgz", - "integrity": "sha512-oWv4T4yJ52iKrufjnyZPkrN0CH3QnrUqdB6In1g5Fe1mia8GmF36gnfNySxoZtxD5+NmYw1EElVXiBk93UeskA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-6.0.0.tgz", + "integrity": "sha512-CV9TW3Y3f8/wT0BRFc1/KAVQ3TUHiXmaAb6VW9vtiMFf7SLoMd1PdAc4W3KFOFETBJUb90KatHqlsZMWV+R9Gg==", "dev": true, + "license": "MIT", "dependencies": { - "whatwg-encoding": "^2.0.0" + "@exodus/bytes": "^1.6.0" }, "engines": { - "node": ">=12" + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" } }, "node_modules/http-proxy-agent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-5.0.0.tgz", - "integrity": "sha512-n2hY8YdoRE1i7r6M0w9DIw5GgZN0G25P8zLCRQ8rjXtTU3vsNFBI/vWK/UIeE6g5MUUz6avwAPXmL6Fy9D/90w==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", "dev": true, + "license": "MIT", "dependencies": { - "@tootallnate/once": "2", - "agent-base": "6", - "debug": "4" + "agent-base": "^7.1.0", + "debug": "^4.3.4" }, "engines": { - "node": ">= 6" + "node": ">= 14" } }, "node_modules/https-proxy-agent": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz", - "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", "dev": true, + "license": "MIT", "dependencies": { - "agent-base": "6", + "agent-base": "^7.1.2", "debug": "4" }, "engines": { - "node": ">= 6" - } - }, - "node_modules/iconv-lite": { - "version": "0.6.3", - "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", - "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", - "dev": true, - "dependencies": { - "safer-buffer": ">= 2.1.2 < 3.0.0" - }, - "engines": { - "node": ">=0.10.0" + "node": ">= 14" } }, "node_modules/is-potential-custom-element-name": { @@ -343,43 +391,38 @@ "dev": true }, "node_modules/jsdom": { - "version": "21.1.1", - "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-21.1.1.tgz", - "integrity": "sha512-Jjgdmw48RKcdAIQyUD1UdBh2ecH7VqwaXPN3ehoZN6MqgVbMn+lRm1aAT1AsdJRAJpwfa4IpwgzySn61h2qu3w==", + "version": "27.4.0", + "resolved": "https://registry.npmjs.org/jsdom/-/jsdom-27.4.0.tgz", + "integrity": "sha512-mjzqwWRD9Y1J1KUi7W97Gja1bwOOM5Ug0EZ6UDK3xS7j7mndrkwozHtSblfomlzyB4NepioNt+B2sOSzczVgtQ==", "dev": true, + "license": "MIT", "dependencies": { - "abab": "^2.0.6", - "acorn": "^8.8.2", - "acorn-globals": "^7.0.0", - "cssstyle": "^3.0.0", - "data-urls": "^4.0.0", - "decimal.js": "^10.4.3", - "domexception": "^4.0.0", - "escodegen": "^2.0.0", - "form-data": "^4.0.0", - "html-encoding-sniffer": "^3.0.0", - "http-proxy-agent": "^5.0.0", - "https-proxy-agent": "^5.0.1", + "@acemir/cssom": "^0.9.28", + "@asamuzakjp/dom-selector": "^6.7.6", + "@exodus/bytes": "^1.6.0", + "cssstyle": "^5.3.4", + "data-urls": "^6.0.0", + "decimal.js": "^10.6.0", + "html-encoding-sniffer": "^6.0.0", + "http-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.6", "is-potential-custom-element-name": "^1.0.1", - "nwsapi": "^2.2.2", - "parse5": "^7.1.2", - "rrweb-cssom": "^0.6.0", + "parse5": "^8.0.0", "saxes": "^6.0.0", "symbol-tree": "^3.2.4", - "tough-cookie": "^4.1.2", - "w3c-xmlserializer": "^4.0.0", - "webidl-conversions": "^7.0.0", - "whatwg-encoding": "^2.0.0", - "whatwg-mimetype": "^3.0.0", - "whatwg-url": "^12.0.1", - "ws": "^8.13.0", - "xml-name-validator": "^4.0.0" + "tough-cookie": "^6.0.0", + "w3c-xmlserializer": "^5.0.0", + "webidl-conversions": "^8.0.0", + "whatwg-mimetype": "^4.0.0", + "whatwg-url": "^15.1.0", + "ws": "^8.18.3", + "xml-name-validator": "^5.0.0" }, "engines": { - "node": ">=14" + "node": "^20.19.0 || ^22.12.0 || >=24.0.0" }, "peerDependencies": { - "canvas": "^2.5.0" + "canvas": "^3.0.0" }, "peerDependenciesMeta": { "canvas": { @@ -387,68 +430,55 @@ } } }, - "node_modules/levn": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/levn/-/levn-0.3.0.tgz", - "integrity": "sha512-0OO4y2iOHix2W6ujICbKIaEQXvFQHue65vUG3pb5EUomzPI90z9hsA1VsO/dbIIpC53J8gxM9Q4Oho0jrCM/yA==", + "node_modules/jsdom/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", "dev": true, - "dependencies": { - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2" - }, + "license": "BSD-2-Clause", "engines": { - "node": ">= 0.8.0" + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/mime-db": { - "version": "1.52.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", - "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "node_modules/jsdom/node_modules/parse5": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-8.0.0.tgz", + "integrity": "sha512-9m4m5GSgXjL4AjumKzq1Fgfp3Z8rsvjRNbnkVwfu2ImRqE5D0LnY2QfDen18FSY9C573YU5XxSapdHZTZ2WolA==", "dev": true, - "engines": { - "node": ">= 0.6" + "license": "MIT", + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" } }, - "node_modules/mime-types": { - "version": "2.1.35", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", - "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "node_modules/lru-cache": { + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", "dev": true, - "dependencies": { - "mime-db": "1.52.0" - }, + "license": "BlueOak-1.0.0", "engines": { - "node": ">= 0.6" + "node": "20 || >=22" } }, - "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, - "node_modules/nwsapi": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/nwsapi/-/nwsapi-2.2.2.tgz", - "integrity": "sha512-90yv+6538zuvUMnN+zCr8LuV6bPFdq50304114vJYJ8RDyK8D5O9Phpbd6SZWgI7PwzmmfN1upeOJlvybDSgCw==", - "dev": true + "node_modules/mdn-data": { + "version": "2.12.2", + "resolved": "https://registry.npmjs.org/mdn-data/-/mdn-data-2.12.2.tgz", + "integrity": "sha512-IEn+pegP1aManZuckezWCO+XZQDplx1366JoVhTpMpBB1sPey/SbveZQUosKiKiGYjg1wH4pMlNgXbCiYgihQA==", + "dev": true, + "license": "CC0-1.0" }, - "node_modules/optionator": { - "version": "0.8.3", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.8.3.tgz", - "integrity": "sha512-+IW9pACdk3XWmmTXG8m3upGUJst5XRGzxMRjXzAuJ1XnIFNvfhjjIuYkDvysnPQ7qzqVzLt78BCruntqRhWQbA==", + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true, - "dependencies": { - "deep-is": "~0.1.3", - "fast-levenshtein": "~2.0.6", - "levn": "~0.3.0", - "prelude-ls": "~1.1.2", - "type-check": "~0.3.2", - "word-wrap": "~1.2.3" - }, - "engines": { - "node": ">= 0.8.0" - } + "license": "MIT" }, "node_modules/parse5": { "version": "7.1.2", @@ -462,54 +492,26 @@ "url": "https://github.com/inikulin/parse5?sponsor=1" } }, - "node_modules/prelude-ls": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.1.2.tgz", - "integrity": "sha512-ESF23V4SKG6lVSGZgYNpbsiaAkdab6ZgOxe52p7+Kid3W3u3bxR4Vfd/o21dmN7jSt0IwgZ4v5MUd26FEtXE9w==", + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, + "license": "MIT", "engines": { - "node": ">= 0.8.0" + "node": ">=6" } }, - "node_modules/psl": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/psl/-/psl-1.9.0.tgz", - "integrity": "sha512-E/ZsdU4HLs/68gYzgGTkMicWTLPdAftJLfJFlLUAAKZGkStNU72sZjT66SnMDVOfOWY/YAoiD7Jxa9iHvngcag==", - "dev": true - }, - "node_modules/punycode": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.0.tgz", - "integrity": "sha512-rRV+zQD8tVFys26lAGR9WUuS4iUAngJScM+ZRSKtvl5tKeZ2t5bvdNFdNHBW9FWR4guGHlgmsZ1G7BSm2wTbuA==", + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "dev": true, + "license": "MIT", "engines": { - "node": ">=6" + "node": ">=0.10.0" } }, - "node_modules/querystringify": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/querystringify/-/querystringify-2.2.0.tgz", - "integrity": "sha512-FIqgj2EUvTa7R50u0rGsyTftzjYmv/a3hO345bZNrqabNqjtgiDMgmo4mkUjd+nzU5oF3dClKqFIPUKybUyqoQ==", - "dev": true - }, - "node_modules/requires-port": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/requires-port/-/requires-port-1.0.0.tgz", - "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", - "dev": true - }, - "node_modules/rrweb-cssom": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/rrweb-cssom/-/rrweb-cssom-0.6.0.tgz", - "integrity": "sha512-APM0Gt1KoXBz0iIkkdB/kfvGOwC4UuJFeG/c+yV7wSc7q96cG/kJ0HiYCnzivD9SB53cLV1MlHFNfOuPaadYSw==", - "dev": true - }, - "node_modules/safer-buffer": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", - "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", - "dev": true - }, "node_modules/saxes": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/saxes/-/saxes-6.0.0.tgz", @@ -522,12 +524,12 @@ "node": ">=v12.22.7" } }, - "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "node_modules/source-map-js": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true, - "optional": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -538,133 +540,105 @@ "integrity": "sha512-9QNk5KwDF+Bvz+PyObkmSYjI5ksVUYtjW7AU22r2NKcfLJcXp96hkDWU3+XndOsUb+AQ9QhfzfCT2O+CNWT5Tw==", "dev": true }, - "node_modules/tough-cookie": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-4.1.3.tgz", - "integrity": "sha512-aX/y5pVRkfRnfmuX+OdbSdXvPe6ieKX/G2s7e98f4poJHnqH3281gDPm/metm6E/WRamfx7WC4HUqkWHfQHprw==", + "node_modules/tldts": { + "version": "7.0.19", + "resolved": "https://registry.npmjs.org/tldts/-/tldts-7.0.19.tgz", + "integrity": "sha512-8PWx8tvC4jDB39BQw1m4x8y5MH1BcQ5xHeL2n7UVFulMPH/3Q0uiamahFJ3lXA0zO2SUyRXuVVbWSDmstlt9YA==", "dev": true, + "license": "MIT", "dependencies": { - "psl": "^1.1.33", - "punycode": "^2.1.1", - "universalify": "^0.2.0", - "url-parse": "^1.5.3" + "tldts-core": "^7.0.19" }, - "engines": { - "node": ">=6" + "bin": { + "tldts": "bin/cli.js" } }, - "node_modules/tr46": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/tr46/-/tr46-4.1.1.tgz", - "integrity": "sha512-2lv/66T7e5yNyhAAC4NaKe5nVavzuGJQVVtRYLyQ2OI8tsJ61PMLlelehb0wi2Hx6+hT/OJUWZcw8MjlSRnxvw==", + "node_modules/tldts-core": { + "version": "7.0.19", + "resolved": "https://registry.npmjs.org/tldts-core/-/tldts-core-7.0.19.tgz", + "integrity": "sha512-lJX2dEWx0SGH4O6p+7FPwYmJ/bu1JbcGJ8RLaG9b7liIgZ85itUVEPbMtWRVrde/0fnDPEPHW10ZsKW3kVsE9A==", "dev": true, - "dependencies": { - "punycode": "^2.3.0" - }, - "engines": { - "node": ">=14" - } + "license": "MIT" }, - "node_modules/type-check": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.3.2.tgz", - "integrity": "sha512-ZCmOJdvOWDBYJlzAoFkC+Q0+bUyEOS1ltgp1MGU03fqHG+dbi9tBFU2Rd9QKiDZFAYrhPh2JUf7rZRIuHRKtOg==", + "node_modules/tough-cookie": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-6.0.0.tgz", + "integrity": "sha512-kXuRi1mtaKMrsLUxz3sQYvVl37B0Ns6MzfrtV5DvJceE9bPyspOqk9xxv7XbZWcfLWbFmm997vl83qUWVJA64w==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "prelude-ls": "~1.1.2" + "tldts": "^7.0.5" }, "engines": { - "node": ">= 0.8.0" - } - }, - "node_modules/universalify": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.2.0.tgz", - "integrity": "sha512-CJ1QgKmNg3CwvAv/kOFmtnEN05f0D/cn9QntgNOQlQF9dgvVTHj3t+8JPdjqawCHk7V/KA+fbUqzZ9XWhcqPUg==", - "dev": true, - "engines": { - "node": ">= 4.0.0" + "node": ">=16" } }, - "node_modules/url-parse": { - "version": "1.5.10", - "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", - "integrity": "sha512-WypcfiRhfeUP9vvF0j6rw0J3hrWrw6iZv3+22h6iRMJ/8z1Tj6XfLP4DsUix5MhMPnXpiHDoKyoZ/bdCkwBCiQ==", + "node_modules/tr46": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/tr46/-/tr46-6.0.0.tgz", + "integrity": "sha512-bLVMLPtstlZ4iMQHpFHTR7GAGj2jxi8Dg0s2h2MafAE4uSWF98FC/3MomU51iQAMf8/qDUbKWf5GxuvvVcXEhw==", "dev": true, + "license": "MIT", "dependencies": { - "querystringify": "^2.1.1", - "requires-port": "^1.0.0" + "punycode": "^2.3.1" + }, + "engines": { + "node": ">=20" } }, "node_modules/w3c-xmlserializer": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-4.0.0.tgz", - "integrity": "sha512-d+BFHzbiCx6zGfz0HyQ6Rg69w9k19nviJspaj4yNscGjrHu94sVP+aRm75yEbCh+r2/yR+7q6hux9LVtbuTGBw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", + "integrity": "sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==", "dev": true, + "license": "MIT", "dependencies": { - "xml-name-validator": "^4.0.0" + "xml-name-validator": "^5.0.0" }, "engines": { - "node": ">=14" + "node": ">=18" } }, "node_modules/webidl-conversions": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-7.0.0.tgz", - "integrity": "sha512-VwddBukDzu71offAQR975unBIGqfKZpM+8ZX6ySk8nYhVoo5CYaZyzt3YBvYtRtO+aoGlqxPg/B87NGVZ/fu6g==", - "dev": true, - "engines": { - "node": ">=12" - } - }, - "node_modules/whatwg-encoding": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", - "integrity": "sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-8.0.0.tgz", + "integrity": "sha512-n4W4YFyz5JzOfQeA8oN7dUYpR+MBP3PIUsn2jLjWXwK5ASUzt0Jc/A5sAUZoCYFJRGF0FBKJ+1JjN43rNdsQzA==", "dev": true, - "dependencies": { - "iconv-lite": "0.6.3" - }, + "license": "BSD-2-Clause", "engines": { - "node": ">=12" + "node": ">=20" } }, "node_modules/whatwg-mimetype": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-3.0.0.tgz", - "integrity": "sha512-nt+N2dzIutVRxARx1nghPKGv1xHikU7HKdfafKkLNLindmPU/ch3U31NOCGGA/dmPcmb1VlofO0vnKAcsm0o/Q==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", "dev": true, + "license": "MIT", "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/whatwg-url": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-12.0.1.tgz", - "integrity": "sha512-Ed/LrqB8EPlGxjS+TrsXcpUond1mhccS3pchLhzSgPCnTimUCKj3IZE75pAs5m6heB2U2TMerKFUXheyHY+VDQ==", + "version": "15.1.0", + "resolved": "https://registry.npmjs.org/whatwg-url/-/whatwg-url-15.1.0.tgz", + "integrity": "sha512-2ytDk0kiEj/yu90JOAp44PVPUkO9+jVhyf+SybKlRHSDlvOOZhdPIrr7xTH64l4WixO2cP+wQIcgujkGBPPz6g==", "dev": true, + "license": "MIT", "dependencies": { - "tr46": "^4.1.1", - "webidl-conversions": "^7.0.0" + "tr46": "^6.0.0", + "webidl-conversions": "^8.0.0" }, "engines": { - "node": ">=14" - } - }, - "node_modules/word-wrap": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.4.tgz", - "integrity": "sha512-2V81OA4ugVo5pRo46hAoD2ivUJx8jXmWXfUkY4KFNw0hEptvN0QfH3K4nHiwzGeKl5rFKedV48QVoqYavy4YpA==", - "dev": true, - "engines": { - "node": ">=0.10.0" + "node": ">=20" } }, "node_modules/ws": { - "version": "8.17.1", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", - "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "version": "8.18.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.3.tgz", + "integrity": "sha512-PEIGCY5tSlUt50cqyMXfCzX+oOPqN0vuGqWzbcJ2xvnkzkq46oOpz7dQaTDBdfICb4N14+GARUDw2XV2N4tvzg==", "dev": true, + "license": "MIT", "engines": { "node": ">=10.0.0" }, @@ -682,12 +656,13 @@ } }, "node_modules/xml-name-validator": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-4.0.0.tgz", - "integrity": "sha512-ICP2e+jsHvAj2E2lIHxa5tjXRlKDJo4IdvPvCXbXQGdzSfmSpNVyIKMvoZHjDY9DP0zV17iI85o90vRFXNccRw==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/xml-name-validator/-/xml-name-validator-5.0.0.tgz", + "integrity": "sha512-EvGK8EJ3DhaHfbRlETOWAS5pO9MZITeauHKJyb8wyajUfQUenkIg2MvLDTZ4T/TgIcm3HU0TFBgWWboAZ30UHg==", "dev": true, + "license": "Apache-2.0", "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/xmlchars": { diff --git a/extensions/notebook-renderers/package.json b/extensions/notebook-renderers/package.json index d6ece35af112..6420afcea5c5 100644 --- a/extensions/notebook-renderers/package.json +++ b/extensions/notebook-renderers/package.json @@ -48,9 +48,9 @@ }, "dependencies": {}, "devDependencies": { - "@types/jsdom": "^21.1.0", - "@types/vscode-notebook-renderer": "^1.60.0", - "jsdom": "^21.1.1" + "@types/jsdom": "^27.0.0", + "@types/vscode-notebook-renderer": "^1.72.4", + "jsdom": "^27.4.0" }, "repository": { "type": "git", diff --git a/extensions/notebook-renderers/src/ansi.ts b/extensions/notebook-renderers/src/ansi.ts index ee26d37d1f6d..90abdb73b5f1 100644 --- a/extensions/notebook-renderers/src/ansi.ts +++ b/extensions/notebook-renderers/src/ansi.ts @@ -59,7 +59,7 @@ export function handleANSIOutput(text: string, linkOptions: LinkOptions): HTMLSp * Certain ranges that are matched here do not contain real graphics rendition sequences. For * the sake of having a simpler expression, they have been included anyway. */ - if (ansiSequence.match(/^(?:[34][0-8]|9[0-7]|10[0-7]|[0-9]|2[1-5,7-9]|[34]9|5[8,9]|1[0-9])(?:;[349][0-7]|10[0-7]|[013]|[245]|[34]9)?(?:;[012]?[0-9]?[0-9])*;?m$/)) { + if (ansiSequence.match(/^(?:[34][0-8]|9[0-7]|10[0-7]|[0-9]|2[1-5,7-9]|[34]9|5[8,9]|1[0-9])(?:;[349][0-7]|10[0-7]|[013]|[245]|[34]9)?(?:;[0-9]{1,3})*;?m$/)) { const styleCodes: number[] = ansiSequence.slice(0, -1) // Remove final 'm' character. .split(';') // Separate style codes. diff --git a/extensions/npm/.vscode/launch.json b/extensions/npm/.vscode/launch.json index 017c87624153..b5cc96144bc8 100644 --- a/extensions/npm/.vscode/launch.json +++ b/extensions/npm/.vscode/launch.json @@ -9,10 +9,7 @@ "args": [ "--extensionDevelopmentPath=${workspaceFolder}" ], - "stopOnEntry": false, "sourceMaps": true, - "outFiles": ["${workspaceFolder}/client/out/**/*.js"], - "preLaunchTask": "npm" } ] -} \ No newline at end of file +} diff --git a/extensions/npm/package-lock.json b/extensions/npm/package-lock.json index 4ee7ee7a0e34..bb44648b111c 100644 --- a/extensions/npm/package-lock.json +++ b/extensions/npm/package-lock.json @@ -9,44 +9,72 @@ "version": "1.0.1", "license": "MIT", "dependencies": { - "find-up": "^5.0.0", + "find-up": "^8.0.0", "find-yarn-workspace-root": "^2.0.0", - "jsonc-parser": "^3.2.0", - "minimatch": "^5.1.6", - "request-light": "^0.7.0", - "vscode-uri": "^3.0.8", - "which": "^4.0.0", - "which-pm": "^2.1.1" + "jsonc-parser": "^3.3.1", + "minimatch": "^10.1.1", + "request-light": "^0.8.0", + "vscode-uri": "^3.1.0", + "which": "^6.0.0", + "which-pm": "^3.0.1" }, "devDependencies": { - "@types/minimatch": "^5.1.2", - "@types/node": "20.x", - "@types/which": "^3.0.0" + "@types/minimatch": "^6.0.0", + "@types/node": "25.x", + "@types/which": "^3.0.4" }, "engines": { "vscode": "0.10.x" } }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, "node_modules/@types/minimatch": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-5.1.2.tgz", - "integrity": "sha512-K0VQKziLUWkVKiRVrx4a40iPaxTUefQmjtkQofBkYRcoaaL/8rhwDWww9qWbrgicNOgnpIsMxyNIUM4+n6dUIA==", - "dev": true + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-6.0.0.tgz", + "integrity": "sha512-zmPitbQ8+6zNutpwgcQuLcsEpn/Cj54Kbn7L5pX0Os5kdWplB7xPgEh/g+SWOB/qmows2gpuCaPyduq8ZZRnxA==", + "deprecated": "This is a stub types definition. minimatch provides its own type definitions, so you do not need this installed.", + "dev": true, + "license": "MIT", + "dependencies": { + "minimatch": "*" + } }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/@types/which": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/@types/which/-/which-3.0.0.tgz", - "integrity": "sha512-ASCxdbsrwNfSMXALlC3Decif9rwDMu+80KGp5zI2RLRotfMsTv7fHL8W8VDp24wymzDyIFudhUeSCugrgRFfHQ==", - "dev": true + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/which/-/which-3.0.4.tgz", + "integrity": "sha512-liyfuo/106JdlgSchJzXEQCVArk0CvevqPote8F8HgWgJ3dRCcTHgJIsLDuee0kxk/mhbInzIZk3QWSZJ8R+2w==", + "dev": true, + "license": "MIT" }, "node_modules/argparse": { "version": "1.0.9", @@ -56,19 +84,6 @@ "sprintf-js": "~1.0.2" } }, - "node_modules/balanced-match": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", - "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c= sha512-9Y0g0Q8rmSt+H33DfKv7FOc3v+iRI+o1lbzt8jGcIosYW37IIW/2XVYq5NPdmaD5NQ59Nk26Kl/vZbwW9Fr8vg==" - }, - "node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dependencies": { - "balanced-match": "^1.0.0" - } - }, "node_modules/braces": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", @@ -104,15 +119,16 @@ } }, "node_modules/find-up": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", - "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/find-up/-/find-up-8.0.0.tgz", + "integrity": "sha512-JGG8pvDi2C+JxidYdIwQDyS/CgcrIdh18cvgxcBge3wSHRQOrooMD3GlFBcmMJAN9M42SAZjDp5zv1dglJjwww==", + "license": "MIT", "dependencies": { - "locate-path": "^6.0.0", - "path-exists": "^4.0.0" + "locate-path": "^8.0.0", + "unicorn-magic": "^0.3.0" }, "engines": { - "node": ">=10" + "node": ">=20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -148,9 +164,10 @@ } }, "node_modules/js-yaml": { - "version": "3.14.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.0.tgz", - "integrity": "sha512-/4IbIeHcD9VMHFqDR/gQ7EdZdLimOvW2DdcxFjdyyZ9NsbS+ccrXqVWDtab/lRl5AlUqmpBx8EhPaWR+OtY17A==", + "version": "3.14.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.2.tgz", + "integrity": "sha512-PMSmkqxr106Xa156c2M265Z+FTrPl+oxd/rgOQy2tijQeK5TxQ43psO1ZCwhVOSdnn+RzkzlRz/eY4BgJBYVpg==", + "license": "MIT", "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" @@ -160,9 +177,10 @@ } }, "node_modules/jsonc-parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", - "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==" + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "license": "MIT" }, "node_modules/load-yaml-file": { "version": "0.2.0", @@ -179,14 +197,15 @@ } }, "node_modules/locate-path": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", - "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-8.0.0.tgz", + "integrity": "sha512-XT9ewWAC43tiAV7xDAPflMkG0qOPn2QjHqlgX8FOqmWa/rxnyYDulF9T0F7tRy1u+TVTmK/M//6VIOye+2zDXg==", + "license": "MIT", "dependencies": { - "p-locate": "^5.0.0" + "p-locate": "^6.0.0" }, "engines": { - "node": ">=10" + "node": ">=20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -206,52 +225,50 @@ } }, "node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^2.0.1" + "@isaacs/brace-expansion": "^5.0.0" }, "engines": { - "node": ">=10" + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/p-limit": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", - "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz", + "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==", + "license": "MIT", "dependencies": { - "yocto-queue": "^0.1.0" + "yocto-queue": "^1.0.0" }, "engines": { - "node": ">=10" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-locate": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", - "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz", + "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==", + "license": "MIT", "dependencies": { - "p-limit": "^3.0.2" + "p-limit": "^4.0.0" }, "engines": { - "node": ">=10" + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/path-exists": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", - "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "engines": { - "node": ">=8" - } - }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", @@ -273,9 +290,10 @@ } }, "node_modules/request-light": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/request-light/-/request-light-0.7.0.tgz", - "integrity": "sha512-lMbBMrDoxgsyO+yB3sDcrDuX85yYt7sS8BfQd11jtbW/z5ZWgLZRcEGLsLoYw7I0WSUGQBs8CC8ScIxkTX1+6Q==" + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/request-light/-/request-light-0.8.0.tgz", + "integrity": "sha512-bH6E4PMmsEXYrLX6Kr1vu+xI3HproB1vECAwaPSJeroLE1kpWE3HR27uB4icx+6YORu1ajqBJXxuedv8ZQg5Lw==", + "license": "MIT" }, "node_modules/sprintf-js": { "version": "1.0.3", @@ -302,20 +320,35 @@ } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" + }, + "node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/vscode-uri": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", - "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "license": "MIT" }, "node_modules/which": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/which/-/which-4.0.0.tgz", - "integrity": "sha512-GlaYyEb07DPxYCKhKzplCWBJtvxZcZMrL+4UkrTSJHHPyZU4mYYTv3qaOe77H7EODLSSopAUFAc6W8U4yqvscg==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.0.tgz", + "integrity": "sha512-f+gEpIKMR9faW/JgAgPK1D7mekkFoqbmiwvNzuhsHetni20QSgzg9Vhn0g2JSJkkfehQnqdUAx7/e15qS1lPxg==", + "license": "ISC", "dependencies": { "isexe": "^3.1.1" }, @@ -323,27 +356,28 @@ "node-which": "bin/which.js" }, "engines": { - "node": "^16.13.0 || >=18.0.0" + "node": "^20.17.0 || >=22.9.0" } }, "node_modules/which-pm": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/which-pm/-/which-pm-2.1.1.tgz", - "integrity": "sha512-xzzxNw2wMaoCWXiGE8IJ9wuPMU+EYhFksjHxrRT8kMT5SnocBPRg69YAMtyV4D12fP582RA+k3P8H9J5EMdIxQ==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/which-pm/-/which-pm-3.0.1.tgz", + "integrity": "sha512-v2JrMq0waAI4ju1xU5x3blsxBBMgdgZve580iYMN5frDaLGjbA24fok7wKCsya8KLVO19Ju4XDc5+zTZCJkQfg==", + "license": "MIT", "dependencies": { - "load-yaml-file": "^0.2.0", - "path-exists": "^4.0.0" + "load-yaml-file": "^0.2.0" }, "engines": { - "node": ">=8.15" + "node": ">=18.12" } }, "node_modules/yocto-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", - "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.2.2.tgz", + "integrity": "sha512-4LCcse/U2MHZ63HAJVE+v71o7yOdIe4cZ70Wpf8D/IyjDKYQLV5GD46B+hSTjJsvV5PztjvHoU580EftxjDZFQ==", + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=12.20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" diff --git a/extensions/npm/package.json b/extensions/npm/package.json index 847a48194659..e4d1b7aa7a7b 100644 --- a/extensions/npm/package.json +++ b/extensions/npm/package.json @@ -21,19 +21,19 @@ "watch": "npx gulp watch-extension:npm" }, "dependencies": { - "find-up": "^5.0.0", + "find-up": "^8.0.0", "find-yarn-workspace-root": "^2.0.0", - "jsonc-parser": "^3.2.0", - "minimatch": "^5.1.6", - "request-light": "^0.7.0", - "which": "^4.0.0", - "which-pm": "^2.1.1", - "vscode-uri": "^3.0.8" + "jsonc-parser": "^3.3.1", + "minimatch": "^10.1.1", + "request-light": "^0.8.0", + "which": "^6.0.0", + "which-pm": "^3.0.1", + "vscode-uri": "^3.1.0" }, "devDependencies": { - "@types/minimatch": "^5.1.2", - "@types/node": "20.x", - "@types/which": "^3.0.0" + "@types/minimatch": "^6.0.0", + "@types/node": "25.x", + "@types/which": "^3.0.4" }, "main": "./out/npmMain", "browser": "./dist/browser/npmBrowserMain", diff --git a/extensions/npm/src/npmMain.ts b/extensions/npm/src/npmMain.ts index dc1c2c241262..c37056fc4a65 100644 --- a/extensions/npm/src/npmMain.ts +++ b/extensions/npm/src/npmMain.ts @@ -38,7 +38,7 @@ export async function activate(context: vscode.ExtensionContext): Promise treeDataProvider = registerExplorer(context); context.subscriptions.push(vscode.workspace.onDidChangeConfiguration((e) => { - if (e.affectsConfiguration('npm.exclude') || e.affectsConfiguration('npm.autoDetect') || e.affectsConfiguration('npm.scriptExplorerExclude')) { + if (e.affectsConfiguration('npm.exclude') || e.affectsConfiguration('npm.autoDetect') || e.affectsConfiguration('npm.scriptExplorerExclude') || e.affectsConfiguration('npm.runSilent') || e.affectsConfiguration('npm.packageManager') || e.affectsConfiguration('npm.scriptRunner')) { invalidateTasksCache(); if (treeDataProvider) { treeDataProvider.refresh(); diff --git a/extensions/package-lock.json b/extensions/package-lock.json index 2fe3d1616983..f2978a2e5631 100644 --- a/extensions/package-lock.json +++ b/extensions/package-lock.json @@ -10,18 +10,18 @@ "hasInstallScript": true, "license": "MIT", "dependencies": { - "typescript": "^5.7.3" + "typescript": "^5.9.3" }, "devDependencies": { - "@parcel/watcher": "2.5.1", - "esbuild": "0.25.0", + "@parcel/watcher": "2.5.6", + "esbuild": "0.27.2", "vscode-grammar-updater": "^1.1.0" } }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.25.0.tgz", - "integrity": "sha512-O7vun9Sf8DFjH2UtqK8Ku3LkquL9SZL8OLY1T5NZkA34+wG3OQF7cl4Ql8vdNzM6fzBbYfLaiRLIOZ+2FOCgBQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.27.2.tgz", + "integrity": "sha512-GZMB+a0mOMZs4MpDbj8RJp4cw+w1WV5NYD6xzgvzUJ5Ek2jerwfO2eADyI6ExDSUED+1X8aMbegahsJi+8mgpw==", "cpu": [ "ppc64" ], @@ -36,9 +36,9 @@ } }, "node_modules/@esbuild/android-arm": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.25.0.tgz", - "integrity": "sha512-PTyWCYYiU0+1eJKmw21lWtC+d08JDZPQ5g+kFyxP0V+es6VPPSUhM6zk8iImp2jbV6GwjX4pap0JFbUQN65X1g==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.27.2.tgz", + "integrity": "sha512-DVNI8jlPa7Ujbr1yjU2PfUSRtAUZPG9I1RwW4F4xFB1Imiu2on0ADiI/c3td+KmDtVKNbi+nffGDQMfcIMkwIA==", "cpu": [ "arm" ], @@ -53,9 +53,9 @@ } }, "node_modules/@esbuild/android-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.25.0.tgz", - "integrity": "sha512-grvv8WncGjDSyUBjN9yHXNt+cq0snxXbDxy5pJtzMKGmmpPxeAmAhWxXI+01lU5rwZomDgD3kJwulEnhTRUd6g==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.27.2.tgz", + "integrity": "sha512-pvz8ZZ7ot/RBphf8fv60ljmaoydPU12VuXHImtAs0XhLLw+EXBi2BLe3OYSBslR4rryHvweW5gmkKFwTiFy6KA==", "cpu": [ "arm64" ], @@ -70,9 +70,9 @@ } }, "node_modules/@esbuild/android-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.25.0.tgz", - "integrity": "sha512-m/ix7SfKG5buCnxasr52+LI78SQ+wgdENi9CqyCXwjVR2X4Jkz+BpC3le3AoBPYTC9NHklwngVXvbJ9/Akhrfg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.27.2.tgz", + "integrity": "sha512-z8Ank4Byh4TJJOh4wpz8g2vDy75zFL0TlZlkUkEwYXuPSgX8yzep596n6mT7905kA9uHZsf/o2OJZubl2l3M7A==", "cpu": [ "x64" ], @@ -87,9 +87,9 @@ } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.25.0.tgz", - "integrity": "sha512-mVwdUb5SRkPayVadIOI78K7aAnPamoeFR2bT5nszFUZ9P8UpK4ratOdYbZZXYSqPKMHfS1wdHCJk1P1EZpRdvw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.27.2.tgz", + "integrity": "sha512-davCD2Zc80nzDVRwXTcQP/28fiJbcOwvdolL0sOiOsbwBa72kegmVU0Wrh1MYrbuCL98Omp5dVhQFWRKR2ZAlg==", "cpu": [ "arm64" ], @@ -104,9 +104,9 @@ } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.25.0.tgz", - "integrity": "sha512-DgDaYsPWFTS4S3nWpFcMn/33ZZwAAeAFKNHNa1QN0rI4pUjgqf0f7ONmXf6d22tqTY+H9FNdgeaAa+YIFUn2Rg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.27.2.tgz", + "integrity": "sha512-ZxtijOmlQCBWGwbVmwOF/UCzuGIbUkqB1faQRf5akQmxRJ1ujusWsb3CVfk/9iZKr2L5SMU5wPBi1UWbvL+VQA==", "cpu": [ "x64" ], @@ -121,9 +121,9 @@ } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.25.0.tgz", - "integrity": "sha512-VN4ocxy6dxefN1MepBx/iD1dH5K8qNtNe227I0mnTRjry8tj5MRk4zprLEdG8WPyAPb93/e4pSgi1SoHdgOa4w==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.27.2.tgz", + "integrity": "sha512-lS/9CN+rgqQ9czogxlMcBMGd+l8Q3Nj1MFQwBZJyoEKI50XGxwuzznYdwcav6lpOGv5BqaZXqvBSiB/kJ5op+g==", "cpu": [ "arm64" ], @@ -138,9 +138,9 @@ } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.25.0.tgz", - "integrity": "sha512-mrSgt7lCh07FY+hDD1TxiTyIHyttn6vnjesnPoVDNmDfOmggTLXRv8Id5fNZey1gl/V2dyVK1VXXqVsQIiAk+A==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.27.2.tgz", + "integrity": "sha512-tAfqtNYb4YgPnJlEFu4c212HYjQWSO/w/h/lQaBK7RbwGIkBOuNKQI9tqWzx7Wtp7bTPaGC6MJvWI608P3wXYA==", "cpu": [ "x64" ], @@ -155,9 +155,9 @@ } }, "node_modules/@esbuild/linux-arm": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.25.0.tgz", - "integrity": "sha512-vkB3IYj2IDo3g9xX7HqhPYxVkNQe8qTK55fraQyTzTX/fxaDtXiEnavv9geOsonh2Fd2RMB+i5cbhu2zMNWJwg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.27.2.tgz", + "integrity": "sha512-vWfq4GaIMP9AIe4yj1ZUW18RDhx6EPQKjwe7n8BbIecFtCQG4CfHGaHuh7fdfq+y3LIA2vGS/o9ZBGVxIDi9hw==", "cpu": [ "arm" ], @@ -172,9 +172,9 @@ } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.25.0.tgz", - "integrity": "sha512-9QAQjTWNDM/Vk2bgBl17yWuZxZNQIF0OUUuPZRKoDtqF2k4EtYbpyiG5/Dk7nqeK6kIJWPYldkOcBqjXjrUlmg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.27.2.tgz", + "integrity": "sha512-hYxN8pr66NsCCiRFkHUAsxylNOcAQaxSSkHMMjcpx0si13t1LHFphxJZUiGwojB1a/Hd5OiPIqDdXONia6bhTw==", "cpu": [ "arm64" ], @@ -189,9 +189,9 @@ } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.25.0.tgz", - "integrity": "sha512-43ET5bHbphBegyeqLb7I1eYn2P/JYGNmzzdidq/w0T8E2SsYL1U6un2NFROFRg1JZLTzdCoRomg8Rvf9M6W6Gg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.27.2.tgz", + "integrity": "sha512-MJt5BRRSScPDwG2hLelYhAAKh9imjHK5+NE/tvnRLbIqUWa+0E9N4WNMjmp/kXXPHZGqPLxggwVhz7QP8CTR8w==", "cpu": [ "ia32" ], @@ -206,9 +206,9 @@ } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.25.0.tgz", - "integrity": "sha512-fC95c/xyNFueMhClxJmeRIj2yrSMdDfmqJnyOY4ZqsALkDrrKJfIg5NTMSzVBr5YW1jf+l7/cndBfP3MSDpoHw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.27.2.tgz", + "integrity": "sha512-lugyF1atnAT463aO6KPshVCJK5NgRnU4yb3FUumyVz+cGvZbontBgzeGFO1nF+dPueHD367a2ZXe1NtUkAjOtg==", "cpu": [ "loong64" ], @@ -223,9 +223,9 @@ } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.25.0.tgz", - "integrity": "sha512-nkAMFju7KDW73T1DdH7glcyIptm95a7Le8irTQNO/qtkoyypZAnjchQgooFUDQhNAy4iu08N79W4T4pMBwhPwQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.27.2.tgz", + "integrity": "sha512-nlP2I6ArEBewvJ2gjrrkESEZkB5mIoaTswuqNFRv/WYd+ATtUpe9Y09RnJvgvdag7he0OWgEZWhviS1OTOKixw==", "cpu": [ "mips64el" ], @@ -240,9 +240,9 @@ } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.25.0.tgz", - "integrity": "sha512-NhyOejdhRGS8Iwv+KKR2zTq2PpysF9XqY+Zk77vQHqNbo/PwZCzB5/h7VGuREZm1fixhs4Q/qWRSi5zmAiO4Fw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.27.2.tgz", + "integrity": "sha512-C92gnpey7tUQONqg1n6dKVbx3vphKtTHJaNG2Ok9lGwbZil6DrfyecMsp9CrmXGQJmZ7iiVXvvZH6Ml5hL6XdQ==", "cpu": [ "ppc64" ], @@ -257,9 +257,9 @@ } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.25.0.tgz", - "integrity": "sha512-5S/rbP5OY+GHLC5qXp1y/Mx//e92L1YDqkiBbO9TQOvuFXM+iDqUNG5XopAnXoRH3FjIUDkeGcY1cgNvnXp/kA==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.27.2.tgz", + "integrity": "sha512-B5BOmojNtUyN8AXlK0QJyvjEZkWwy/FKvakkTDCziX95AowLZKR6aCDhG7LeF7uMCXEJqwa8Bejz5LTPYm8AvA==", "cpu": [ "riscv64" ], @@ -274,9 +274,9 @@ } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.25.0.tgz", - "integrity": "sha512-XM2BFsEBz0Fw37V0zU4CXfcfuACMrppsMFKdYY2WuTS3yi8O1nFOhil/xhKTmE1nPmVyvQJjJivgDT+xh8pXJA==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.27.2.tgz", + "integrity": "sha512-p4bm9+wsPwup5Z8f4EpfN63qNagQ47Ua2znaqGH6bqLlmJ4bx97Y9JdqxgGZ6Y8xVTixUnEkoKSHcpRlDnNr5w==", "cpu": [ "s390x" ], @@ -291,9 +291,9 @@ } }, "node_modules/@esbuild/linux-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.25.0.tgz", - "integrity": "sha512-9yl91rHw/cpwMCNytUDxwj2XjFpxML0y9HAOH9pNVQDpQrBxHy01Dx+vaMu0N1CKa/RzBD2hB4u//nfc+Sd3Cw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.27.2.tgz", + "integrity": "sha512-uwp2Tip5aPmH+NRUwTcfLb+W32WXjpFejTIOWZFw/v7/KnpCDKG66u4DLcurQpiYTiYwQ9B7KOeMJvLCu/OvbA==", "cpu": [ "x64" ], @@ -308,9 +308,9 @@ } }, "node_modules/@esbuild/netbsd-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.25.0.tgz", - "integrity": "sha512-RuG4PSMPFfrkH6UwCAqBzauBWTygTvb1nxWasEJooGSJ/NwRw7b2HOwyRTQIU97Hq37l3npXoZGYMy3b3xYvPw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-arm64/-/netbsd-arm64-0.27.2.tgz", + "integrity": "sha512-Kj6DiBlwXrPsCRDeRvGAUb/LNrBASrfqAIok+xB0LxK8CHqxZ037viF13ugfsIpePH93mX7xfJp97cyDuTZ3cw==", "cpu": [ "arm64" ], @@ -325,9 +325,9 @@ } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.25.0.tgz", - "integrity": "sha512-jl+qisSB5jk01N5f7sPCsBENCOlPiS/xptD5yxOx2oqQfyourJwIKLRA2yqWdifj3owQZCL2sn6o08dBzZGQzA==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.27.2.tgz", + "integrity": "sha512-HwGDZ0VLVBY3Y+Nw0JexZy9o/nUAWq9MlV7cahpaXKW6TOzfVno3y3/M8Ga8u8Yr7GldLOov27xiCnqRZf0tCA==", "cpu": [ "x64" ], @@ -342,9 +342,9 @@ } }, "node_modules/@esbuild/openbsd-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.25.0.tgz", - "integrity": "sha512-21sUNbq2r84YE+SJDfaQRvdgznTD8Xc0oc3p3iW/a1EVWeNj/SdUCbm5U0itZPQYRuRTW20fPMWMpcrciH2EJw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.27.2.tgz", + "integrity": "sha512-DNIHH2BPQ5551A7oSHD0CKbwIA/Ox7+78/AWkbS5QoRzaqlev2uFayfSxq68EkonB+IKjiuxBFoV8ESJy8bOHA==", "cpu": [ "arm64" ], @@ -359,9 +359,9 @@ } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.25.0.tgz", - "integrity": "sha512-2gwwriSMPcCFRlPlKx3zLQhfN/2WjJ2NSlg5TKLQOJdV0mSxIcYNTMhk3H3ulL/cak+Xj0lY1Ym9ysDV1igceg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.27.2.tgz", + "integrity": "sha512-/it7w9Nb7+0KFIzjalNJVR5bOzA9Vay+yIPLVHfIQYG/j+j9VTH84aNB8ExGKPU4AzfaEvN9/V4HV+F+vo8OEg==", "cpu": [ "x64" ], @@ -375,10 +375,27 @@ "node": ">=18" } }, + "node_modules/@esbuild/openharmony-arm64": { + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/openharmony-arm64/-/openharmony-arm64-0.27.2.tgz", + "integrity": "sha512-LRBbCmiU51IXfeXk59csuX/aSaToeG7w48nMwA6049Y4J4+VbWALAuXcs+qcD04rHDuSCSRKdmY63sruDS5qag==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openharmony" + ], + "engines": { + "node": ">=18" + } + }, "node_modules/@esbuild/sunos-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.25.0.tgz", - "integrity": "sha512-bxI7ThgLzPrPz484/S9jLlvUAHYMzy6I0XiU1ZMeAEOBcS0VePBFxh1JjTQt3Xiat5b6Oh4x7UC7IwKQKIJRIg==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.27.2.tgz", + "integrity": "sha512-kMtx1yqJHTmqaqHPAzKCAkDaKsffmXkPHThSfRwZGyuqyIeBvf08KSsYXl+abf5HDAPMJIPnbBfXvP2ZC2TfHg==", "cpu": [ "x64" ], @@ -393,9 +410,9 @@ } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.25.0.tgz", - "integrity": "sha512-ZUAc2YK6JW89xTbXvftxdnYy3m4iHIkDtK3CLce8wg8M2L+YZhIvO1DKpxrd0Yr59AeNNkTiic9YLf6FTtXWMw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.27.2.tgz", + "integrity": "sha512-Yaf78O/B3Kkh+nKABUF++bvJv5Ijoy9AN1ww904rOXZFLWVc5OLOfL56W+C8F9xn5JQZa3UX6m+IktJnIb1Jjg==", "cpu": [ "arm64" ], @@ -410,9 +427,9 @@ } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.25.0.tgz", - "integrity": "sha512-eSNxISBu8XweVEWG31/JzjkIGbGIJN/TrRoiSVZwZ6pkC6VX4Im/WV2cz559/TXLcYbcrDN8JtKgd9DJVIo8GA==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.27.2.tgz", + "integrity": "sha512-Iuws0kxo4yusk7sw70Xa2E2imZU5HoixzxfGCdxwBdhiDgt9vX9VUCBhqcwY7/uh//78A1hMkkROMJq9l27oLQ==", "cpu": [ "ia32" ], @@ -427,9 +444,9 @@ } }, "node_modules/@esbuild/win32-x64": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.25.0.tgz", - "integrity": "sha512-ZENoHJBxA20C2zFzh6AI4fT6RraMzjYw4xKWemRTRmRVtN9c5DcH9r/f2ihEkMjOW5eGgrwCslG/+Y/3bL+DHQ==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.27.2.tgz", + "integrity": "sha512-sRdU18mcKf7F+YgheI/zGf5alZatMUTKj/jNS6l744f9u3WFu4v7twcUI9vu4mknF4Y9aDlblIie0IM+5xxaqQ==", "cpu": [ "x64" ], @@ -444,17 +461,17 @@ } }, "node_modules/@parcel/watcher": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", - "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.6.tgz", + "integrity": "sha512-tmmZ3lQxAe/k/+rNnXQRawJ4NjxO2hqiOLTHvWchtGZULp4RyFeh6aU4XdOYBFe2KE1oShQTv4AblOs2iOrNnQ==", "dev": true, "hasInstallScript": true, "license": "MIT", "dependencies": { - "detect-libc": "^1.0.3", + "detect-libc": "^2.0.3", "is-glob": "^4.0.3", - "micromatch": "^4.0.5", - "node-addon-api": "^7.0.0" + "node-addon-api": "^7.0.0", + "picomatch": "^4.0.3" }, "engines": { "node": ">= 10.0.0" @@ -464,25 +481,25 @@ "url": "https://opencollective.com/parcel" }, "optionalDependencies": { - "@parcel/watcher-android-arm64": "2.5.1", - "@parcel/watcher-darwin-arm64": "2.5.1", - "@parcel/watcher-darwin-x64": "2.5.1", - "@parcel/watcher-freebsd-x64": "2.5.1", - "@parcel/watcher-linux-arm-glibc": "2.5.1", - "@parcel/watcher-linux-arm-musl": "2.5.1", - "@parcel/watcher-linux-arm64-glibc": "2.5.1", - "@parcel/watcher-linux-arm64-musl": "2.5.1", - "@parcel/watcher-linux-x64-glibc": "2.5.1", - "@parcel/watcher-linux-x64-musl": "2.5.1", - "@parcel/watcher-win32-arm64": "2.5.1", - "@parcel/watcher-win32-ia32": "2.5.1", - "@parcel/watcher-win32-x64": "2.5.1" + "@parcel/watcher-android-arm64": "2.5.6", + "@parcel/watcher-darwin-arm64": "2.5.6", + "@parcel/watcher-darwin-x64": "2.5.6", + "@parcel/watcher-freebsd-x64": "2.5.6", + "@parcel/watcher-linux-arm-glibc": "2.5.6", + "@parcel/watcher-linux-arm-musl": "2.5.6", + "@parcel/watcher-linux-arm64-glibc": "2.5.6", + "@parcel/watcher-linux-arm64-musl": "2.5.6", + "@parcel/watcher-linux-x64-glibc": "2.5.6", + "@parcel/watcher-linux-x64-musl": "2.5.6", + "@parcel/watcher-win32-arm64": "2.5.6", + "@parcel/watcher-win32-ia32": "2.5.6", + "@parcel/watcher-win32-x64": "2.5.6" } }, "node_modules/@parcel/watcher-android-arm64": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", - "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.6.tgz", + "integrity": "sha512-YQxSS34tPF/6ZG7r/Ih9xy+kP/WwediEUsqmtf0cuCV5TPPKw/PQHRhueUo6JdeFJaqV3pyjm0GdYjZotbRt/A==", "cpu": [ "arm64" ], @@ -501,9 +518,9 @@ } }, "node_modules/@parcel/watcher-darwin-arm64": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz", - "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.6.tgz", + "integrity": "sha512-Z2ZdrnwyXvvvdtRHLmM4knydIdU9adO3D4n/0cVipF3rRiwP+3/sfzpAwA/qKFL6i1ModaabkU7IbpeMBgiVEA==", "cpu": [ "arm64" ], @@ -522,9 +539,9 @@ } }, "node_modules/@parcel/watcher-darwin-x64": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz", - "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.6.tgz", + "integrity": "sha512-HgvOf3W9dhithcwOWX9uDZyn1lW9R+7tPZ4sug+NGrGIo4Rk1hAXLEbcH1TQSqxts0NYXXlOWqVpvS1SFS4fRg==", "cpu": [ "x64" ], @@ -543,9 +560,9 @@ } }, "node_modules/@parcel/watcher-freebsd-x64": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz", - "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.6.tgz", + "integrity": "sha512-vJVi8yd/qzJxEKHkeemh7w3YAn6RJCtYlE4HPMoVnCpIXEzSrxErBW5SJBgKLbXU3WdIpkjBTeUNtyBVn8TRng==", "cpu": [ "x64" ], @@ -564,9 +581,9 @@ } }, "node_modules/@parcel/watcher-linux-arm-glibc": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz", - "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.6.tgz", + "integrity": "sha512-9JiYfB6h6BgV50CCfasfLf/uvOcJskMSwcdH1PHH9rvS1IrNy8zad6IUVPVUfmXr+u+Km9IxcfMLzgdOudz9EQ==", "cpu": [ "arm" ], @@ -585,9 +602,9 @@ } }, "node_modules/@parcel/watcher-linux-arm-musl": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz", - "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.6.tgz", + "integrity": "sha512-Ve3gUCG57nuUUSyjBq/MAM0CzArtuIOxsBdQ+ftz6ho8n7s1i9E1Nmk/xmP323r2YL0SONs1EuwqBp2u1k5fxg==", "cpu": [ "arm" ], @@ -606,9 +623,9 @@ } }, "node_modules/@parcel/watcher-linux-arm64-glibc": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", - "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.6.tgz", + "integrity": "sha512-f2g/DT3NhGPdBmMWYoxixqYr3v/UXcmLOYy16Bx0TM20Tchduwr4EaCbmxh1321TABqPGDpS8D/ggOTaljijOA==", "cpu": [ "arm64" ], @@ -627,9 +644,9 @@ } }, "node_modules/@parcel/watcher-linux-arm64-musl": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", - "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.6.tgz", + "integrity": "sha512-qb6naMDGlbCwdhLj6hgoVKJl2odL34z2sqkC7Z6kzir8b5W65WYDpLB6R06KabvZdgoHI/zxke4b3zR0wAbDTA==", "cpu": [ "arm64" ], @@ -648,9 +665,9 @@ } }, "node_modules/@parcel/watcher-linux-x64-glibc": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", - "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.6.tgz", + "integrity": "sha512-kbT5wvNQlx7NaGjzPFu8nVIW1rWqV780O7ZtkjuWaPUgpv2NMFpjYERVi0UYj1msZNyCzGlaCWEtzc+exjMGbQ==", "cpu": [ "x64" ], @@ -669,9 +686,9 @@ } }, "node_modules/@parcel/watcher-linux-x64-musl": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", - "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.6.tgz", + "integrity": "sha512-1JRFeC+h7RdXwldHzTsmdtYR/Ku8SylLgTU/reMuqdVD7CtLwf0VR1FqeprZ0eHQkO0vqsbvFLXUmYm/uNKJBg==", "cpu": [ "x64" ], @@ -690,9 +707,9 @@ } }, "node_modules/@parcel/watcher-win32-arm64": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", - "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.6.tgz", + "integrity": "sha512-3ukyebjc6eGlw9yRt678DxVF7rjXatWiHvTXqphZLvo7aC5NdEgFufVwjFfY51ijYEWpXbqF5jtrK275z52D4Q==", "cpu": [ "arm64" ], @@ -711,9 +728,9 @@ } }, "node_modules/@parcel/watcher-win32-ia32": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", - "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.6.tgz", + "integrity": "sha512-k35yLp1ZMwwee3Ez/pxBi5cf4AoBKYXj00CZ80jUz5h8prpiaQsiRPKQMxoLstNuqe2vR4RNPEAEcjEFzhEz/g==", "cpu": [ "ia32" ], @@ -732,9 +749,9 @@ } }, "node_modules/@parcel/watcher-win32-x64": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", - "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", + "version": "2.5.6", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.6.tgz", + "integrity": "sha512-hbQlYcCq5dlAX9Qx+kFb0FHue6vbjlf0FrNzSKdYK2APUf7tGfGxQCk2ihEREmbR6ZMc0MVAD5RIX/41gpUzTw==", "cpu": [ "x64" ], @@ -752,18 +769,6 @@ "url": "https://opencollective.com/parcel" } }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dev": true, - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/coffeescript": { "version": "1.12.7", "resolved": "https://registry.npmjs.org/coffeescript/-/coffeescript-1.12.7.tgz", @@ -790,22 +795,19 @@ } }, "node_modules/detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", "dev": true, "license": "Apache-2.0", - "bin": { - "detect-libc": "bin/detect-libc.js" - }, "engines": { - "node": ">=0.10" + "node": ">=8" } }, "node_modules/esbuild": { - "version": "0.25.0", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.25.0.tgz", - "integrity": "sha512-BXq5mqc8ltbaN34cDqWuYKyNhX8D/Z0J1xdtdQ8UcIIIyJyz+ZMKUt58tF3SrZ85jcfN/PZYhjR5uDQAYNVbuw==", + "version": "0.27.2", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.27.2.tgz", + "integrity": "sha512-HyNQImnsOC7X9PMNaCIeAm4ISCQXs5a5YasTXVliKv4uuBo1dKrG0A+uQS8M5eXjVMnLg3WgXaKvprHlFJQffw==", "dev": true, "hasInstallScript": true, "license": "MIT", @@ -816,31 +818,32 @@ "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.25.0", - "@esbuild/android-arm": "0.25.0", - "@esbuild/android-arm64": "0.25.0", - "@esbuild/android-x64": "0.25.0", - "@esbuild/darwin-arm64": "0.25.0", - "@esbuild/darwin-x64": "0.25.0", - "@esbuild/freebsd-arm64": "0.25.0", - "@esbuild/freebsd-x64": "0.25.0", - "@esbuild/linux-arm": "0.25.0", - "@esbuild/linux-arm64": "0.25.0", - "@esbuild/linux-ia32": "0.25.0", - "@esbuild/linux-loong64": "0.25.0", - "@esbuild/linux-mips64el": "0.25.0", - "@esbuild/linux-ppc64": "0.25.0", - "@esbuild/linux-riscv64": "0.25.0", - "@esbuild/linux-s390x": "0.25.0", - "@esbuild/linux-x64": "0.25.0", - "@esbuild/netbsd-arm64": "0.25.0", - "@esbuild/netbsd-x64": "0.25.0", - "@esbuild/openbsd-arm64": "0.25.0", - "@esbuild/openbsd-x64": "0.25.0", - "@esbuild/sunos-x64": "0.25.0", - "@esbuild/win32-arm64": "0.25.0", - "@esbuild/win32-ia32": "0.25.0", - "@esbuild/win32-x64": "0.25.0" + "@esbuild/aix-ppc64": "0.27.2", + "@esbuild/android-arm": "0.27.2", + "@esbuild/android-arm64": "0.27.2", + "@esbuild/android-x64": "0.27.2", + "@esbuild/darwin-arm64": "0.27.2", + "@esbuild/darwin-x64": "0.27.2", + "@esbuild/freebsd-arm64": "0.27.2", + "@esbuild/freebsd-x64": "0.27.2", + "@esbuild/linux-arm": "0.27.2", + "@esbuild/linux-arm64": "0.27.2", + "@esbuild/linux-ia32": "0.27.2", + "@esbuild/linux-loong64": "0.27.2", + "@esbuild/linux-mips64el": "0.27.2", + "@esbuild/linux-ppc64": "0.27.2", + "@esbuild/linux-riscv64": "0.27.2", + "@esbuild/linux-s390x": "0.27.2", + "@esbuild/linux-x64": "0.27.2", + "@esbuild/netbsd-arm64": "0.27.2", + "@esbuild/netbsd-x64": "0.27.2", + "@esbuild/openbsd-arm64": "0.27.2", + "@esbuild/openbsd-x64": "0.27.2", + "@esbuild/openharmony-arm64": "0.27.2", + "@esbuild/sunos-x64": "0.27.2", + "@esbuild/win32-arm64": "0.27.2", + "@esbuild/win32-ia32": "0.27.2", + "@esbuild/win32-x64": "0.27.2" } }, "node_modules/fast-plist": { @@ -849,18 +852,6 @@ "integrity": "sha1-pFr/NFGWAG1AbKbNzQX2kFHvNbg= sha512-2HxzrqJhmMoxVzARjYFvkzkL2dCBB8sogU5sD8gqcZWv5UCivK9/cXM9KIPDRwU+eD3mbRDN/GhW8bO/4dtMfg==", "dev": true }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dev": true, - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", @@ -882,29 +873,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true, - "engines": { - "node": ">=0.12.0" - } - }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dev": true, - "license": "MIT", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, "node_modules/node-addon-api": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-7.1.0.tgz", @@ -916,33 +884,22 @@ } }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, + "license": "MIT", "engines": { - "node": ">=8.6" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dev": true, - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, "node_modules/typescript": { - "version": "5.7.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz", - "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", diff --git a/extensions/package.json b/extensions/package.json index 734351856417..1a70ed9731dc 100644 --- a/extensions/package.json +++ b/extensions/package.json @@ -4,14 +4,14 @@ "license": "MIT", "description": "Dependencies shared by all extensions", "dependencies": { - "typescript": "^5.7.3" + "typescript": "^5.9.3" }, "scripts": { "postinstall": "node ./postinstall.mjs" }, "devDependencies": { - "@parcel/watcher": "2.5.1", - "esbuild": "0.25.0", + "@parcel/watcher": "2.5.6", + "esbuild": "0.27.2", "vscode-grammar-updater": "^1.1.0" }, "overrides": { diff --git a/extensions/php-language-features/package-lock.json b/extensions/php-language-features/package-lock.json index 21fdb6b8e9f6..b4db78806c17 100644 --- a/extensions/php-language-features/package-lock.json +++ b/extensions/php-language-features/package-lock.json @@ -9,54 +9,62 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "which": "^2.0.2" + "which": "^6.0.0" }, "devDependencies": { - "@types/node": "20.x", - "@types/which": "^2.0.0" + "@types/node": "25.x", + "@types/which": "^3.0.4" }, "engines": { "vscode": "0.10.x" } }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/@types/which": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/@types/which/-/which-2.0.0.tgz", - "integrity": "sha512-JHTNOEpZnACQdsTojWggn+SQ8IucfqEhtz7g8Z0G67WdSj4x3F0X5I2c/CVcl8z/QukGrIHeQ/N49v1au74XFQ==", - "dev": true + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/@types/which/-/which-3.0.4.tgz", + "integrity": "sha512-liyfuo/106JdlgSchJzXEQCVArk0CvevqPote8F8HgWgJ3dRCcTHgJIsLDuee0kxk/mhbInzIZk3QWSZJ8R+2w==", + "dev": true, + "license": "MIT" }, "node_modules/isexe": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", - "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA= sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-3.1.1.tgz", + "integrity": "sha512-LpB/54B+/2J5hqQ7imZHfdU31OlgQqx7ZicVlkm9kzg9/w8GKLEcFfJl/t7DCEDueOyBAD6zCCwTO6Fzs0NoEQ==", + "license": "ISC", + "engines": { + "node": ">=16" + } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" }, "node_modules/which": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", - "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/which/-/which-6.0.0.tgz", + "integrity": "sha512-f+gEpIKMR9faW/JgAgPK1D7mekkFoqbmiwvNzuhsHetni20QSgzg9Vhn0g2JSJkkfehQnqdUAx7/e15qS1lPxg==", + "license": "ISC", "dependencies": { - "isexe": "^2.0.0" + "isexe": "^3.1.1" }, "bin": { - "node-which": "bin/node-which" + "node-which": "bin/which.js" }, "engines": { - "node": ">= 8" + "node": "^20.17.0 || >=22.9.0" } } } diff --git a/extensions/php-language-features/package.json b/extensions/php-language-features/package.json index 989213b6c0c0..0194aeda2a9c 100644 --- a/extensions/php-language-features/package.json +++ b/extensions/php-language-features/package.json @@ -74,11 +74,11 @@ "watch": "npx gulp watch-extension:php-language-features" }, "dependencies": { - "which": "^2.0.2" + "which": "^6.0.0" }, "devDependencies": { - "@types/node": "20.x", - "@types/which": "^2.0.0" + "@types/node": "25.x", + "@types/which": "^3.0.4" }, "repository": { "type": "git", diff --git a/extensions/references-view/package-lock.json b/extensions/references-view/package-lock.json index bb5c5e10e1fe..448fb756e80b 100644 --- a/extensions/references-view/package-lock.json +++ b/extensions/references-view/package-lock.json @@ -9,26 +9,28 @@ "version": "1.0.0", "license": "MIT", "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "engines": { "vscode": "^1.67.0" } }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" } } } diff --git a/extensions/references-view/package.json b/extensions/references-view/package.json index 9566a965c760..97b98648c001 100644 --- a/extensions/references-view/package.json +++ b/extensions/references-view/package.json @@ -399,6 +399,6 @@ "watch": "npx gulp watch-extension:references-view" }, "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" } } diff --git a/extensions/ruby/cgmanifest.json b/extensions/ruby/cgmanifest.json index 49719a8afe98..f54ddba7ce79 100644 --- a/extensions/ruby/cgmanifest.json +++ b/extensions/ruby/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "Shopify/ruby-lsp", "repositoryUrl": "https://github.com/Shopify/ruby-lsp", - "commitHash": "01a8ecf608b7d8607adcd89c32db72ae3852f33b" + "commitHash": "958bb1aa0c7aa4b6119c947b69afa7f12b19dceb" } }, "licenseDetail": [ diff --git a/extensions/ruby/syntaxes/ruby.tmLanguage.json b/extensions/ruby/syntaxes/ruby.tmLanguage.json index 84fb37228e64..785b0e4a9287 100644 --- a/extensions/ruby/syntaxes/ruby.tmLanguage.json +++ b/extensions/ruby/syntaxes/ruby.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/Shopify/ruby-lsp/commit/01a8ecf608b7d8607adcd89c32db72ae3852f33b", + "version": "https://github.com/Shopify/ruby-lsp/commit/958bb1aa0c7aa4b6119c947b69afa7f12b19dceb", "name": "Ruby", "scopeName": "source.ruby", "patterns": [ @@ -30,7 +30,7 @@ } }, "comment": "class Namespace::ClassName < OtherNamespace::OtherClassName", - "match": "\b(class)\\s+(([a-zA-Z0-9_]+)((::)[a-zA-Z0-9_]+)*)\\s*((<)\\s*(([a-zA-Z0-9_]+)((::)[a-zA-Z0-9_]+)*))?", + "match": "\\b(class)\\s+(([a-zA-Z0-9_]+)((::)[a-zA-Z0-9_]+)*)\\s*((<)\\s*(([a-zA-Z0-9_]+)((::)[a-zA-Z0-9_]+)*))?", "name": "meta.class.ruby" }, { @@ -45,7 +45,7 @@ "name": "punctuation.separator.namespace.ruby" } }, - "match": "\b(module)\\s+(([a-zA-Z0-9_]+)((::)[a-zA-Z0-9_]+)*)", + "match": "\\b(module)\\s+(([a-zA-Z0-9_]+)((::)[a-zA-Z0-9_]+)*)", "name": "meta.module.ruby" }, { @@ -57,7 +57,7 @@ "name": "punctuation.separator.inheritance.ruby" } }, - "match": "\b(class)\\s*(<<)\\s*", + "match": "\\b(class)\\s*(<<)\\s*", "name": "meta.class.ruby" }, { @@ -125,7 +125,7 @@ "name": "variable.ruby" } }, - "match": "^\\s*([a-z]([A-Za-z0-9_])*)\\s*=[^=>]", + "match": "^\\s*([a-z]([A-Za-z0-9_])*)\\s*(?==[^=>])", "comment": "A local variable assignment" }, { @@ -2211,7 +2211,7 @@ ] }, { - "begin": "(?<={|{\\s|[^A-Za-z0-9_:@$]do|^do|[^A-Za-z0-9_:@$]do\\s|^do\\s)(\\|)", + "begin": "(?<={|{\\s+|[^A-Za-z0-9_:@$]do|^do|[^A-Za-z0-9_:@$]do\\s+|^do\\s+)(\\|)", "name": "meta.block.parameters.ruby", "captures": { "1": { @@ -2225,16 +2225,13 @@ "end": "(?=,|\\|\\s*)", "patterns": [ { - "match": "\\G([&*]?)([a-zA-Z][\\w_]*)|(_[\\w_]*)", + "match": "\\G((?:&|\\*\\*?)?)([a-zA-Z_][\\w_]*)", "captures": { "1": { "name": "storage.type.variable.ruby" }, "2": { "name": "variable.other.block.ruby" - }, - "3": { - "name": "variable.other.block.unused.ruby variable.other.constant.ruby" } } } diff --git a/extensions/scss/cgmanifest.json b/extensions/scss/cgmanifest.json index 12247769ce22..a67a4f546096 100644 --- a/extensions/scss/cgmanifest.json +++ b/extensions/scss/cgmanifest.json @@ -6,12 +6,12 @@ "git": { "name": "atom/language-sass", "repositoryUrl": "https://github.com/atom/language-sass", - "commitHash": "f52ab12f7f9346cc2568129d8c4419bd3d506b47" + "commitHash": "303bbf0c250fe380b9e57375598cfd916110758b" } }, "license": "MIT", "description": "The file syntaxes/scss.json was derived from the Atom package https://github.com/atom/language-sass which was originally converted from the TextMate bundle https://github.com/alexsancho/SASS.tmbundle.", - "version": "0.62.1" + "version": "0.61.4" } ], "version": 1 diff --git a/extensions/simple-browser/package-lock.json b/extensions/simple-browser/package-lock.json index c6d9b23636a9..9376726c60c1 100644 --- a/extensions/simple-browser/package-lock.json +++ b/extensions/simple-browser/package-lock.json @@ -9,84 +9,84 @@ "version": "1.0.0", "license": "MIT", "dependencies": { - "@vscode/extension-telemetry": "^0.9.8" + "@vscode/extension-telemetry": "^1.4.0" }, "devDependencies": { - "@types/vscode-webview": "^1.57.0", - "@vscode/codicons": "^0.0.36" + "@types/vscode-webview": "^1.57.5", + "@vscode/codicons": "^0.0.44" }, "engines": { "vscode": "^1.70.0" } }, "node_modules/@microsoft/1ds-core-js": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.4.tgz", - "integrity": "sha512-3gbDUQgAO8EoyQTNcAEkxpuPnioC0May13P1l1l0NKZ128L9Ts/sj8QsfwCRTjHz0HThlA+4FptcAJXNYUy3rg==", + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.10.tgz", + "integrity": "sha512-5fSZmkGwWkH+mrIA5M1GYPZdPM+SjXwCCl2Am7VhFoVwOBJNhRnwvIpAdzw6sFjiebN/rz+/YH0NdxztGZSa9Q==", "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" } }, "node_modules/@microsoft/1ds-post-js": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.4.tgz", - "integrity": "sha512-nlKjWricDj0Tn68Dt0P8lX9a+X7LYrqJ6/iSfQwMfDhRIGLqW+wxx8gxS+iGWC/oc8zMQAeiZaemUpCwQcwpRQ==", + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.10.tgz", + "integrity": "sha512-VSLjc9cT+Y+eTiSfYltJHJCejn8oYr0E6Pq2BMhOEO7F6IyLGYIxzKKvo78ze9x+iHX7KPTATcZ+PFgjGXuNqg==", "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "4.3.4", + "@microsoft/1ds-core-js": "4.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" } }, "node_modules/@microsoft/applicationinsights-channel-js": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.4.tgz", - "integrity": "sha512-Z4nrxYwGKP9iyrYtm7iPQXVOFy4FsEsX0nDKkAi96Qpgw+vEh6NH4ORxMMuES0EollBQ3faJyvYCwckuCVIj0g==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.10.tgz", + "integrity": "sha512-iolFLz1ocWAzIQqHIEjjov3gNTPkgFQ4ArHnBcJEYoffOGWlJt6copaevS5YPI5rHzmbySsengZ8cLJJBBrXzQ==", "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-common": "3.3.4", - "@microsoft/applicationinsights-core-js": "3.3.4", + "@microsoft/applicationinsights-common": "3.3.10", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-common": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.4.tgz", - "integrity": "sha512-4ms16MlIvcP4WiUPqopifNxcWCcrXQJ2ADAK/75uok2mNQe6ZNRsqb/P+pvhUxc8A5HRlvoXPP1ptDSN5Girgw==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.10.tgz", + "integrity": "sha512-RVIenPIvNgZCbjJdALvLM4rNHgAFuHI7faFzHCgnI6S2WCUNGHeXlQTs9EUUrL+n2TPp9/cd0KKMILU5VVyYiA==", "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-core-js": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.4.tgz", - "integrity": "sha512-MummANF0mgKIkdvVvfmHQTBliK114IZLRhTL0X0Ep+zjDwWMHqYZgew0nlFKAl6ggu42abPZFK5afpE7qjtYJA==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.10.tgz", + "integrity": "sha512-5yKeyassZTq2l+SAO4npu6LPnbS++UD+M+Ghjm9uRzoBwD8tumFx0/F8AkSVqbniSREd+ztH/2q2foewa2RZyg==", "license": "MIT", "dependencies": { "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { "tslib": ">= 1.0.0" @@ -102,18 +102,18 @@ } }, "node_modules/@microsoft/applicationinsights-web-basic": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.4.tgz", - "integrity": "sha512-OpEPXr8vU/t/M8T9jvWJzJx/pCyygIiR1nGM/2PTde0wn7anl71Gxl5fWol7K/WwFEORNjkL3CEyWOyDc+28AA==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.10.tgz", + "integrity": "sha512-AZib5DAT3NU0VT0nLWEwXrnoMDDgZ/5S4dso01CNU5ELNxLdg+1fvchstlVdMy4FrAnxzs8Wf/GIQNFYOVgpAw==", "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-channel-js": "3.3.4", - "@microsoft/applicationinsights-common": "3.3.4", - "@microsoft/applicationinsights-core-js": "3.3.4", + "@microsoft/applicationinsights-channel-js": "3.3.10", + "@microsoft/applicationinsights-common": "3.3.10", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { "tslib": ">= 1.0.0" @@ -138,33 +138,34 @@ } }, "node_modules/@nevware21/ts-utils": { - "version": "0.11.6", - "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.11.6.tgz", - "integrity": "sha512-OUUJTh3fnaUSzg9DEHgv3d7jC+DnPL65mIO7RaR+jWve7+MmcgIvF79gY97DPQ4frH+IpNR78YAYd/dW4gK3kg==", + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.12.5.tgz", + "integrity": "sha512-JPQZWPKQJjj7kAftdEZL0XDFfbMgXCGiUAZe0d7EhLC3QlXTlZdSckGqqRIQ2QNl0VTEZyZUvRBw6Ednw089Fw==", "license": "MIT" }, "node_modules/@types/vscode-webview": { - "version": "1.57.0", - "resolved": "https://registry.npmjs.org/@types/vscode-webview/-/vscode-webview-1.57.0.tgz", - "integrity": "sha512-x3Cb/SMa1IwRHfSvKaZDZOTh4cNoG505c3NjTqGlMC082m++x/ETUmtYniDsw6SSmYzZXO8KBNhYxR0+VqymqA==", - "dev": true + "version": "1.57.5", + "resolved": "https://registry.npmjs.org/@types/vscode-webview/-/vscode-webview-1.57.5.tgz", + "integrity": "sha512-iBAUYNYkz+uk1kdsq05fEcoh8gJmwT3lqqFPN7MGyjQ3HVloViMdo7ZJ8DFIP8WOK74PjOEilosqAyxV2iUFUw==", + "dev": true, + "license": "MIT" }, "node_modules/@vscode/codicons": { - "version": "0.0.36", - "resolved": "https://registry.npmjs.org/@vscode/codicons/-/codicons-0.0.36.tgz", - "integrity": "sha512-wsNOvNMMJ2BY8rC2N2MNBG7yOowV3ov8KlvUE/AiVUlHKTfWsw3OgAOQduX7h0Un6GssKD3aoTVH+TF3DSQwKQ==", + "version": "0.0.44", + "resolved": "https://registry.npmjs.org/@vscode/codicons/-/codicons-0.0.44.tgz", + "integrity": "sha512-F7qPRumUK3EHjNdopfICLGRf3iNPoZQt+McTHAn4AlOWPB3W2kL4H0S7uqEqbyZ6rCxaeDjpAn3MCUnwTu/VJQ==", "dev": true, "license": "CC-BY-4.0" }, "node_modules/@vscode/extension-telemetry": { - "version": "0.9.8", - "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.8.tgz", - "integrity": "sha512-7YcKoUvmHlIB8QYCE4FNzt3ErHi9gQPhdCM3ZWtpw1bxPT0I+lMdx52KHlzTNoJzQ2NvMX7HyzyDwBEiMgTrWQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-1.4.0.tgz", + "integrity": "sha512-mqypuqIDn+vQnaQ8tMdvCRn9BprZTH2j6nlFFv9hE6hB25n32F8rOZinos8OgblRi87X1qdBpjpb0Em1LGSG9w==", "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "^4.3.4", - "@microsoft/1ds-post-js": "^4.3.4", - "@microsoft/applicationinsights-web-basic": "^3.3.4" + "@microsoft/1ds-core-js": "^4.3.10", + "@microsoft/1ds-post-js": "^4.3.10", + "@microsoft/applicationinsights-web-basic": "^3.3.10" }, "engines": { "vscode": "^1.75.0" diff --git a/extensions/simple-browser/package.json b/extensions/simple-browser/package.json index 9aba9ad25036..dca21b0aa733 100644 --- a/extensions/simple-browser/package.json +++ b/extensions/simple-browser/package.json @@ -66,11 +66,11 @@ "watch-web": "npx webpack-cli --config extension-browser.webpack.config --mode none --watch --info-verbosity verbose" }, "dependencies": { - "@vscode/extension-telemetry": "^0.9.8" + "@vscode/extension-telemetry": "^1.4.0" }, "devDependencies": { - "@types/vscode-webview": "^1.57.0", - "@vscode/codicons": "^0.0.36" + "@types/vscode-webview": "^1.57.5", + "@vscode/codicons": "^0.0.44" }, "repository": { "type": "git", diff --git a/extensions/simple-browser/preview-src/index.ts b/extensions/simple-browser/preview-src/index.ts index 3d804aa60fa4..106146f86579 100644 --- a/extensions/simple-browser/preview-src/index.ts +++ b/extensions/simple-browser/preview-src/index.ts @@ -90,18 +90,23 @@ onceDocumentLoaded(() => { toggleFocusLockIndicatorEnabled(settings.focusLockIndicatorEnabled); function navigateTo(rawUrl: string): void { + let safeUrl: string | null = null; try { const url = new URL(rawUrl); - - // Try to bust the cache for the iframe - // There does not appear to be any way to reliably do this except modifying the url - url.searchParams.append('vscodeBrowserReqId', Date.now().toString()); - - iframe.src = url.toString(); + if (url.protocol === 'http:' || url.protocol === 'https:') { + // Try to bust the cache for the iframe + url.searchParams.append('vscodeBrowserReqId', Date.now().toString()); + safeUrl = url.toString(); + } } catch { - iframe.src = rawUrl; + // On parse error, do not attempt to match with regex; keep safeUrl as null. + } + if (safeUrl) { + iframe.src = safeUrl; + } else { + // Optionally, display an error or set to about:blank + iframe.src = 'about:blank'; } - vscode.setState({ url: rawUrl }); } }); diff --git a/extensions/sql/cgmanifest.json b/extensions/sql/cgmanifest.json index fec241862ef0..4a1455ebd319 100644 --- a/extensions/sql/cgmanifest.json +++ b/extensions/sql/cgmanifest.json @@ -6,11 +6,11 @@ "git": { "name": "microsoft/vscode-mssql", "repositoryUrl": "https://github.com/microsoft/vscode-mssql", - "commitHash": "49eff02f68b6ee73025c6665c672ca1c93385dde" + "commitHash": "d07e0f838eabff968e4387841427d3c3af8aeec6" } }, "license": "MIT", - "version": "1.23.0" + "version": "1.29.0" } ], "version": 1 diff --git a/extensions/sql/syntaxes/sql.tmLanguage.json b/extensions/sql/syntaxes/sql.tmLanguage.json index 9db452210265..4341bc81528f 100644 --- a/extensions/sql/syntaxes/sql.tmLanguage.json +++ b/extensions/sql/syntaxes/sql.tmLanguage.json @@ -4,7 +4,7 @@ "If you want to provide a fix or improvement, please create a pull request against the original repository.", "Once accepted there, we are happy to receive an update request." ], - "version": "https://github.com/microsoft/vscode-mssql/commit/49eff02f68b6ee73025c6665c672ca1c93385dde", + "version": "https://github.com/microsoft/vscode-mssql/commit/d07e0f838eabff968e4387841427d3c3af8aeec6", "name": "SQL", "scopeName": "source.sql", "patterns": [ @@ -372,7 +372,7 @@ "include": "#regexps" }, { - "match": "\\b(?i)(abort|abort_after_wait|absent|absolute|accent_sensitivity|acceptable_cursopt|acp|action|activation|add|address|admin|aes_128|aes_192|aes_256|affinity|after|aggregate|algorithm|all_constraints|all_errormsgs|all_indexes|all_levels|all_results|allow_connections|allow_dup_row|allow_encrypted_value_modifications|allow_page_locks|allow_row_locks|allow_snapshot_isolation|alter|altercolumn|always|anonymous|ansi_defaults|ansi_null_default|ansi_null_dflt_off|ansi_null_dflt_on|ansi_nulls|ansi_padding|ansi_warnings|appdomain|append|application|apply|arithabort|arithignore|array|assembly|asymmetric|asynchronous_commit|at|atan2|atomic|attach|attach_force_rebuild_log|attach_rebuild_log|audit|auth_realm|authentication|auto|auto_cleanup|auto_close|auto_create_statistics|auto_drop|auto_shrink|auto_update_statistics|auto_update_statistics_async|automated_backup_preference|automatic|autopilot|availability|availability_mode|backup|backup_priority|base64|basic|batches|batchsize|before|between|bigint|binary|binding|bit|block|blockers|blocksize|bmk|both|break|broker|broker_instance|bucket_count|buffer|buffercount|bulk_logged|by|call|caller|card|case|catalog|catch|cert|certificate|change_retention|change_tracking|change_tracking_context|changes|char|character|character_set|check_expiration|check_policy|checkconstraints|checkindex|checkpoint|checksum|cleanup_policy|clear|clear_port|close|clustered|codepage|collection|column_encryption_key|column_master_key|columnstore|columnstore_archive|colv_80_to_100|colv_100_to_80|commit_differential_base|committed|compatibility_level|compress_all_row_groups|compression|compression_delay|concat_null_yields_null|concatenate|configuration|connect|connection|containment|continue|continue_after_error|contract|contract_name|control|conversation|conversation_group_id|conversation_handle|copy|copy_only|count_rows|counter|create(\\\\s+or\\\\s+alter)?|credential|cross|cryptographic|cryptographic_provider|cube|cursor|cursor_close_on_commit|cursor_default|data|data_compression|data_flush_interval_seconds|data_mirroring|data_purity|data_source|database|database_name|database_snapshot|datafiletype|date_correlation_optimization|date|datefirst|dateformat|date_format|datetime|datetime2|datetimeoffset|day(s)?|db_chaining|dbid|dbidexec|dbo_only|deadlock_priority|deallocate|dec|decimal|declare|decrypt|decrypt_a|decryption|default_database|default_fulltext_language|default_language|default_logon_domain|default_schema|definition|delay|delayed_durability|delimitedtext|density_vector|dependent|des|description|desired_state|desx|differential|digest|disable|disable_broker|disable_def_cnst_chk|disabled|disk|distinct|distributed|distribution|drop|drop_existing|dts_buffers|dump|durability|dynamic|edition|elements|else|emergency|empty|enable|enable_broker|enabled|encoding|encrypted|encrypted_value|encryption|encryption_type|end|endpoint|endpoint_url|enhancedintegrity|entry|error_broker_conversations|errorfile|estimateonly|event|except|exec|executable|execute|exists|expand|expiredate|expiry_date|explicit|external|external_access|failover|failover_mode|failure_condition_level|fast|fast_forward|fastfirstrow|federated_service_account|fetch|field_terminator|fieldterminator|file|filelistonly|filegroup|filegrowth|filename|filestream|filestream_log|filestream_on|filetable|file_format|filter|first_row|fips_flagger|fire_triggers|first|firstrow|float|flush_interval_seconds|fmtonly|following|for|force|force_failover_allow_data_loss|force_service_allow_data_loss|forced|forceplan|formatfile|format_options|format_type|formsof|forward_only|free_cursors|free_exec_context|fullscan|fulltext|fulltextall|fulltextkey|function|generated|get|geography|geometry|global|go|goto|governor|guid|hadoop|hardening|hash|hashed|header_limit|headeronly|health_check_timeout|hidden|hierarchyid|histogram|histogram_steps|hits_cursors|hits_exec_context|hour(s)?|http|identity|identity_value|if|ifnull|ignore|ignore_constraints|ignore_dup_key|ignore_dup_row|ignore_triggers|image|immediate|implicit_transactions|include|include_null_values|incremental|index|inflectional|init|initiator|insensitive|insert|instead|int|integer|integrated|intersect|intermediate|interval_length_minutes|into|inuse_cursors|inuse_exec_context|io|is|isabout|iso_week|isolation|job_tracker_location|json|keep|keep_nulls|keep_replication|keepdefaults|keepfixed|keepidentity|keepnulls|kerberos|key|key_path|key_source|key_store_provider_name|keyset|kill|kilobytes_per_batch|labelonly|langid|language|last|lastrow|leading|legacy_cardinality_estimation|length|level|lifetime|lineage_80_to_100|lineage_100_to_80|listener_ip|listener_port|load|loadhistory|lob_compaction|local|local_service_name|locate|location|lock_escalation|lock_timeout|lockres|log|login|login_type|loop|manual|mark_in_use_for_removal|masked|master|match|matched|max_queue_readers|max_duration|max_outstanding_io_per_volume|maxdop|maxerrors|maxlength|maxtransfersize|max_plans_per_query|max_storage_size_mb|mediadescription|medianame|mediapassword|memogroup|memory_optimized|merge|message|message_forward_size|message_forwarding|microsecond|millisecond|minute(s)?|mirror_address|misses_cursors|misses_exec_context|mixed|modify|money|month|move|multi_user|must_change|name|namespace|nanosecond|native|native_compilation|nchar|ncharacter|nested_triggers|never|new_account|new_broker|newname|next|no|no_browsetable|no_checksum|no_compression|no_infomsgs|no_triggers|no_truncate|nocount|noexec|noexpand|noformat|noinit|nolock|nonatomic|nonclustered|nondurable|none|norecompute|norecovery|noreset|norewind|noskip|not|notification|nounload|now|nowait|ntext|ntlm|nulls|numeric|numeric_roundabort|nvarchar|object|objid|oem|offline|old_account|online|operation_mode|open|openjson|optimistic|option|orc|out|outer|output|over|override|owner|ownership|pad_index|page|page_checksum|page_verify|pagecount|paglock|param|parameter_sniffing|parameter_type_expansion|parameterization|parquet|parseonly|partial|partition|partner|password|path|pause|percentage|permission_set|persisted|period|physical_only|plan_forcing_mode|policy|pool|population|ports|preceding|precision|predicate|presume_abort|primary|primary_role|print|prior|priority |priority_level|private|proc(edure)?|procedure_name|profile|provider|quarter|query_capture_mode|query_governor_cost_limit|query_optimizer_hotfixes|query_store|queue|quoted_identifier|raiserror|range|raw|rcfile|rc2|rc4|rc4_128|rdbms|read_committed_snapshot|read|read_only|read_write|readcommitted|readcommittedlock|readonly|readpast|readuncommitted|readwrite|real|rebuild|receive|recmodel_70backcomp|recompile|reconfigure|recovery|recursive|recursive_triggers|redo_queue|reject_sample_value|reject_type|reject_value|relative|remote|remote_data_archive|remote_proc_transactions|remote_service_name|remove|removed_cursors|removed_exec_context|reorganize|repeat|repeatable|repeatableread|replace|replica|replicated|replnick_100_to_80|replnickarray_80_to_100|replnickarray_100_to_80|required|required_cursopt|resample|reset|resource|resource_manager_location|respect|restart|restore|restricted_user|resume|retaindays|retention|return|revert|rewind|rewindonly|returns|robust|role|rollup|root|round_robin|route|row|rowdump|rowguidcol|rowlock|row_terminator|rows|rows_per_batch|rowsets_only|rowterminator|rowversion|rsa_1024|rsa_2048|rsa_3072|rsa_4096|rsa_512|safe|safety|sample|save|scalar|schema|schemabinding|scoped|scroll|scroll_locks|sddl|second|secexpr|seconds|secondary|secondary_only|secondary_role|secret|security|securityaudit|selective|self|send|sent|sequence|serde_method|serializable|server|service|service_broker|service_name|service_objective|session_timeout|session|sessions|seterror|setopts|sets|shard_map_manager|shard_map_name|sharded|shared_memory|shortest_path|show_statistics|showplan_all|showplan_text|showplan_xml|showplan_xml_with_recompile|shrinkdb|shutdown|sid|signature|simple|single_blob|single_clob|single_nclob|single_user|singleton|site|size|size_based_cleanup_mode|skip|smalldatetime|smallint|smallmoney|snapshot|snapshot_import|snapshotrestorephase|soap|softnuma|sort_in_tempdb|sorted_data|sorted_data_reorg|spatial|sql|sql_bigint|sql_binary|sql_bit|sql_char|sql_date|sql_decimal|sql_double|sql_float|sql_guid|sql_handle|sql_longvarbinary|sql_longvarchar|sql_numeric|sql_real|sql_smallint|sql_time|sql_timestamp|sql_tinyint|sql_tsi_day|sql_tsi_frac_second|sql_tsi_hour|sql_tsi_minute|sql_tsi_month|sql_tsi_quarter|sql_tsi_second|sql_tsi_week|sql_tsi_year|sql_type_date|sql_type_time|sql_type_timestamp|sql_varbinary|sql_varchar|sql_variant|sql_wchar|sql_wlongvarchar|ssl|ssl_port|standard|standby|start|start_date|started|stat_header|state|statement|static|statistics|statistics_incremental|statistics_norecompute|statistics_only|statman|stats|stats_stream|status|stop|stop_on_error|stopat|stopatmark|stopbeforemark|stoplist|stopped|string_delimiter|subject|supplemental_logging|supported|suspend|symmetric|synchronous_commit|synonym|sysname|system|system_time|system_versioning|table|tableresults|tablock|tablockx|take|tape|target|target_index|target_partition|target_recovery_time|tcp|temporal_history_retention|text|textimage_on|then|thesaurus|throw|time|timeout|timestamp|tinyint|to|top|torn_page_detection|track_columns_updated|trailing|tran|transaction|transfer|transform_noise_words|triple_des|triple_des_3key|truncate|trustworthy|try|tsql|two_digit_year_cutoff|type|type_desc|type_warning|tzoffset|uid|unbounded|uncommitted|unique|uniqueidentifier|unlimited|unload|unlock|unsafe|updlock|url|use|useplan|useroptions|use_type_default|using|utcdatetime|valid_xml|validation|value|values|varbinary|varchar|verbose|verifyonly|version|view_metadata|virtual_device|visiblity|wait_at_low_priority|waitfor|webmethod|week|weekday|weight|well_formed_xml|when|while|widechar|widechar_ansi|widenative|window|windows|with|within|within group|witness|without|without_array_wrapper|workload|wsdl|xact_abort|xlock|xml|xmlschema|xquery|xsinil|year|zone)\\b", + "match": "\\b(?i)(abort|abort_after_wait|absent|absolute|accent_sensitivity|acceptable_cursopt|acp|action|activation|add|address|admin|aes_128|aes_192|aes_256|affinity|after|aggregate|algorithm|all_constraints|all_errormsgs|all_indexes|all_levels|all_results|allow_connections|allow_dup_row|allow_encrypted_value_modifications|allow_page_locks|allow_row_locks|allow_snapshot_isolation|alter|altercolumn|always|anonymous|ansi_defaults|ansi_null_default|ansi_null_dflt_off|ansi_null_dflt_on|ansi_nulls|ansi_padding|ansi_warnings|appdomain|append|application|apply|arithabort|arithignore|array|assembly|asymmetric|asynchronous_commit|at|atan2|atomic|attach|attach_force_rebuild_log|attach_rebuild_log|audit|auth_realm|authentication|auto|auto_cleanup|auto_close|auto_create_statistics|auto_drop|auto_shrink|auto_update_statistics|auto_update_statistics_async|automated_backup_preference|automatic|autopilot|availability|availability_mode|backup|backup_priority|base64|basic|batches|batchsize|before|between|bigint|binary|binding|bit|block|blockers|blocksize|bmk|both|break|broker|broker_instance|bucket_count|buffer|buffercount|bulk_logged|by|call|caller|card|case|catalog|catch|cert|certificate|change_retention|change_tracking|change_tracking_context|changes|char|character|character_set|check_expiration|check_policy|checkconstraints|checkindex|checkpoint|checksum|cleanup_policy|clear|clear_port|close|clustered|codepage|collection|column_encryption_key|column_master_key|columnstore|columnstore_archive|colv_80_to_100|colv_100_to_80|commit_differential_base|committed|compatibility_level|compress_all_row_groups|compression|compression_delay|concat_null_yields_null|concatenate|configuration|connect|connection|containment|continue|continue_after_error|contract|contract_name|control|conversation|conversation_group_id|conversation_handle|copy|copy_only|count_rows|counter|create(\\\\s+or\\\\s+alter)?|credential|cross|cryptographic|cryptographic_provider|cube|cursor|cursor_close_on_commit|cursor_default|data|data_compression|data_flush_interval_seconds|data_mirroring|data_purity|data_source|database|database_name|database_snapshot|datafiletype|date_correlation_optimization|date|datefirst|dateformat|date_format|datetime|datetime2|datetimeoffset|day(s)?|db_chaining|dbid|dbidexec|dbo_only|deadlock_priority|deallocate|dec|decimal|declare|decrypt|decrypt_a|decryption|default_database|default_fulltext_language|default_language|default_logon_domain|default_schema|definition|delay|delayed_durability|delimitedtext|density_vector|dependent|des|description|desired_state|desx|differential|digest|disable|disable_broker|disable_def_cnst_chk|disabled|disk|distinct|distributed|distribution|drop|drop_existing|dts_buffers|dump|durability|dynamic|edition|elements|else|emergency|empty|enable|enable_broker|enabled|encoding|encrypted|encrypted_value|encryption|encryption_type|end|endpoint|endpoint_url|enhancedintegrity|entry|error_broker_conversations|errorfile|estimateonly|event|except|exec|executable|execute|exists|expand|expiredate|expiry_date|explicit|external|external_access|failover|failover_mode|failure_condition_level|fast|fast_forward|fastfirstrow|federated_service_account|fetch|field_terminator|fieldterminator|file|filelistonly|filegroup|filegrowth|filename|filestream|filestream_log|filestream_on|filetable|file_format|filter|first_row|fips_flagger|fire_triggers|first|firstrow|float|flush_interval_seconds|fmtonly|following|for|force|force_failover_allow_data_loss|force_service_allow_data_loss|forced|forceplan|formatfile|format_options|format_type|formsof|forward_only|free_cursors|free_exec_context|fullscan|fulltext|fulltextall|fulltextkey|function|generated|get|geography|geometry|global|go|goto|governor|guid|hadoop|hardening|hash|hashed|header_limit|headeronly|health_check_timeout|hidden|hierarchyid|histogram|histogram_steps|hits_cursors|hits_exec_context|hour(s)?|http|identity|identity_value|if|ifnull|ignore|ignore_constraints|ignore_dup_key|ignore_dup_row|ignore_triggers|image|immediate|implicit_transactions|include|include_null_values|incremental|index|inflectional|init|initiator|insensitive|insert|instead|int|integer|integrated|intersect|intermediate|interval_length_minutes|into|inuse_cursors|inuse_exec_context|io|is|isabout|iso_week|isolation|job_tracker_location|json|keep|keep_nulls|keep_replication|keepdefaults|keepfixed|keepidentity|keepnulls|kerberos|key|key_path|key_source|key_store_provider_name|keyset|kill|kilobytes_per_batch|labelonly|langid|language|last|lastrow|leading|legacy_cardinality_estimation|length|level|lifetime|lineage_80_to_100|lineage_100_to_80|listener_ip|listener_port|load|loadhistory|lob_compaction|local|local_service_name|locate|location|lock_escalation|lock_timeout|lockres|log|login|login_type|loop|manual|mark_in_use_for_removal|masked|master|match|matched|max_queue_readers|max_duration|max_outstanding_io_per_volume|maxdop|maxerrors|maxlength|maxtransfersize|max_plans_per_query|max_storage_size_mb|mediadescription|medianame|mediapassword|memogroup|memory_optimized|merge|message|message_forward_size|message_forwarding|microsecond|millisecond|minute(s)?|mirror_address|misses_cursors|misses_exec_context|mixed|modify|money|month|move|multi_user|must_change|name|namespace|nanosecond|native|native_compilation|nchar|ncharacter|nested_triggers|never|new_account|new_broker|newname|next|no|no_browsetable|no_checksum|no_compression|no_infomsgs|no_triggers|no_truncate|nocount|noexec|noexpand|noformat|noinit|nolock|nonatomic|nonclustered|nondurable|none|norecompute|norecovery|noreset|norewind|noskip|not|notification|nounload|now|nowait|ntext|ntlm|nulls|numeric|numeric_roundabort|nvarchar|object|objid|oem|offline|old_account|online|operation_mode|open|openjson|optimistic|option|orc|out|outer|output|over|override|owner|ownership|pad_index|page|page_checksum|page_verify|pagecount|paglock|param|parameter_sniffing|parameter_type_expansion|parameterization|parquet|parseonly|partial|partition|partner|password|path|pause|percentage|permission_set|persisted|period|physical_only|plan_forcing_mode|policy|pool|population|ports|preceding|precision|predicate|presume_abort|primary|primary_role|print|prior|priority |priority_level|private|proc(edure)?|procedure_name|profile|provider|quarter|query_capture_mode|query_governor_cost_limit|query_optimizer_hotfixes|query_store|queue|quoted_identifier|raiserror|range|raw|rcfile|rc2|rc4|rc4_128|rdbms|read_committed_snapshot|read|read_only|read_write|readcommitted|readcommittedlock|readonly|readpast|readuncommitted|readwrite|real|rebuild|receive|recmodel_70backcomp|recompile|reconfigure|recovery|recursive|recursive_triggers|redo_queue|reject_sample_value|reject_type|reject_value|relative|remote|remote_data_archive|remote_proc_transactions|remote_service_name|remove|removed_cursors|removed_exec_context|reorganize|repeat|repeatable|repeatableread|replace|replica|replicated|replnick_100_to_80|replnickarray_80_to_100|replnickarray_100_to_80|required|required_cursopt|resample|reset|resource|resource_manager_location|respect|restart|restore|restricted_user|resume|retaindays|retention|return|revert|rewind|rewindonly|returns|robust|role|rollup|root|round_robin|route|row|rowdump|rowguidcol|rowlock|row_terminator|rows|rows_per_batch|rowsets_only|rowterminator|rowversion|rsa_1024|rsa_2048|rsa_3072|rsa_4096|rsa_512|safe|safety|sample|save|scalar|schema|schemabinding|scoped|scroll|scroll_locks|sddl|second|secexpr|seconds|secondary|secondary_only|secondary_role|secret|security|securityaudit|selective|self|send|sent|sequence|serde_method|serializable|server|service|service_broker|service_name|service_objective|session_timeout|session|sessions|seterror|setopts|sets|shard_map_manager|shard_map_name|sharded|shared_memory|shortest_path|show_statistics|showplan_all|showplan_text|showplan_xml|showplan_xml_with_recompile|shrinkdb|shutdown|sid|signature|simple|single_blob|single_clob|single_nclob|single_user|singleton|site|size|size_based_cleanup_mode|skip|smalldatetime|smallint|smallmoney|snapshot|snapshot_import|snapshotrestorephase|soap|softnuma|sort_in_tempdb|sorted_data|sorted_data_reorg|spatial|sql|sql_bigint|sql_binary|sql_bit|sql_char|sql_date|sql_decimal|sql_double|sql_float|sql_guid|sql_handle|sql_longvarbinary|sql_longvarchar|sql_numeric|sql_real|sql_smallint|sql_time|sql_timestamp|sql_tinyint|sql_tsi_day|sql_tsi_frac_second|sql_tsi_hour|sql_tsi_minute|sql_tsi_month|sql_tsi_quarter|sql_tsi_second|sql_tsi_week|sql_tsi_year|sql_type_date|sql_type_time|sql_type_timestamp|sql_varbinary|sql_varchar|sql_variant|sql_wchar|sql_wlongvarchar|ssl|ssl_port|standard|standby|start|start_date|started|stat_header|state|statement|static|statistics|statistics_incremental|statistics_norecompute|statistics_only|statman|stats|stats_stream|status|stop|stop_on_error|stopat|stopatmark|stopbeforemark|stoplist|stopped|string_delimiter|subject|supplemental_logging|supported|suspend|symmetric|synchronous_commit|synonym|sysname|system|system_time|system_versioning|table|tableresults|tablock|tablockx|take|tape|target|target_index|target_partition|target_recovery_time|tcp|temporal_history_retention|text|textimage_on|then|thesaurus|throw|time|timeout|timestamp|tinyint|to|top|torn_page_detection|track_columns_updated|trailing|tran|transaction|transfer|transform_noise_words|triple_des|triple_des_3key|truncate|trustworthy|try|tsql|two_digit_year_cutoff|type|type_desc|type_warning|tzoffset|uid|unbounded|uncommitted|unique|uniqueidentifier|unlimited|unload|unlock|unsafe|updlock|url|use|useplan|useroptions|use_type_default|using|utcdatetime|valid_xml|validation|value|values|varbinary|varchar|vector|verbose|verifyonly|version|view_metadata|virtual_device|visiblity|wait_at_low_priority|waitfor|webmethod|week|weekday|weight|well_formed_xml|when|while|widechar|widechar_ansi|widenative|window|windows|with|within|within group|witness|without|without_array_wrapper|workload|wsdl|xact_abort|xlock|xml|xmlschema|xquery|xsinil|year|zone)\\b", "name": "keyword.other.sql" }, { diff --git a/extensions/terminal-suggest/ThirdPartyNotices.txt b/extensions/terminal-suggest/ThirdPartyNotices.txt index 761d5b01a428..f6de57f6266a 100644 --- a/extensions/terminal-suggest/ThirdPartyNotices.txt +++ b/extensions/terminal-suggest/ThirdPartyNotices.txt @@ -28,5 +28,3 @@ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - diff --git a/extensions/terminal-suggest/cgmanifest.json b/extensions/terminal-suggest/cgmanifest.json index f39b1fae3712..ead3479e6673 100644 --- a/extensions/terminal-suggest/cgmanifest.json +++ b/extensions/terminal-suggest/cgmanifest.json @@ -82,6 +82,63 @@ "SOFTWARE." ], "version": "1.1.2" + }, + { + "component": { + "type": "git", + "git": { + "repositoryUrl": "https://github.com/zsh-users/zsh", + "commitHash": "435cb1b748ce1f2f5c38edc1d64f4ee2424f9b3a" + } + }, + "version": "5.9", + "licenseDetail": [ + "Unless otherwise noted in the header of specific files, files in this distribution have the licence shown below.", + "", + "However, note that certain shell functions are licensed under versions of the GNU General Public Licence. Anyone distributing the shell as a binary including those files needs to take account of this. Search shell functions for \"Copyright\" for specific copyright information. None of the core functions are affected by this, so those files may simply be omitted.", + "", + "--", + "", + "The Z Shell is copyright (c) 1992-2017 Paul Falstad, Richard Coleman, Zoltán Hidvégi, Andrew Main, Peter Stephenson, Sven Wischnowsky, and others. All rights reserved. Individual authors, whether or not specifically named, retain copyright in all changes; in what follows, they are referred to as `the Zsh Development Group'. This is for convenience only and this body has no legal status. The Z shell is distributed under the following licence; any provisions made in individual files take precedence.", + "", + "Permission is hereby granted, without written agreement and without licence or royalty fees, to use, copy, modify, and distribute this software and to distribute modified versions of this software for any purpose, provided that the above copyright notice and the following two paragraphs appear in all copies of this software.", + "", + "In no event shall the Zsh Development Group be liable to any party for direct, indirect, special, incidental, or consequential damages arising out of the use of this software and its documentation, even if the Zsh Development Group have been advised of the possibility of such damage.", + "", + "The Zsh Development Group specifically disclaim any warranties, including, but not limited to, the implied warranties of merchantability and fitness for a particular purpose. The software provided hereunder is on an \"as is\" basis, and the Zsh Development Group have no obligation to provide maintenance, support, updates, enhancements, or modifications." + ] + }, + { + "component": { + "type": "git", + "git": { + "repositoryUrl": "https://github.com/fish-shell/fish-shell", + "commitHash": "6627d403d33b4e74b49aa4db2a4f17709628cdc8" + } + }, + "version": "3.7.1", + "licenseDetail": [ + "Fish is a smart and user-friendly command line shell.", + "", + "Copyright (C) 2005-2009 Axel Liljencrantz", + "Copyright (C) 2009- fish-shell contributors", + "", + "fish is free software.", + "", + "Most of fish is licensed under the GNU General Public License version 2, and", + "you can redistribute it and/or modify it under the terms of the GNU GPL as", + "published by the Free Software Foundation.", + "", + "fish also includes software licensed under the Python Software Foundation License version 2, the MIT", + "license, and the GNU Library General Public License version 2.", + "", + "Full licensing information is contained in doc_src/license.rst.", + "", + "This program is distributed in the hope that it will be useful, but WITHOUT", + "ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or", + "FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for", + "more details." + ] } ], "version": 1 diff --git a/extensions/terminal-suggest/package.json b/extensions/terminal-suggest/package.json index dcc02bff6a24..f2e4104823c8 100644 --- a/extensions/terminal-suggest/package.json +++ b/extensions/terminal-suggest/package.json @@ -21,7 +21,8 @@ "scripts": { "compile": "npx gulp compile-extension:terminal-suggest", "watch": "npx gulp watch-extension:terminal-suggest", - "pull-zshbuiltins": "ts-node ./scripts/pullZshBuiltins.ts" + "pull-zshbuiltins": "ts-node ./scripts/pullZshBuiltins.ts", + "pull-fishbuiltins": "ts-node ./scripts/pullFishBuiltins.ts" }, "main": "./out/terminalSuggestMain", "activationEvents": [ diff --git a/extensions/terminal-suggest/scripts/pullFishBuiltins.ts b/extensions/terminal-suggest/scripts/pullFishBuiltins.ts new file mode 100644 index 000000000000..8ac6b870d451 --- /dev/null +++ b/extensions/terminal-suggest/scripts/pullFishBuiltins.ts @@ -0,0 +1,260 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as fs from 'fs/promises'; +import * as path from 'path'; +import { cleanupText, checkWindows, execAsync, copyright } from './terminalScriptHelpers'; + +checkWindows(); + +interface ICommandDetails { + description: string; + args: string | undefined; + shortDescription?: string; +} + +let fishBuiltinsCommandDescriptionsCache = new Map(); + +// Fallback descriptions for commands that don't return proper help information +const fallbackDescriptions: Record = { + '[': { + shortDescription: 'Test if a statement is true', + description: 'Evaluate an expression and return a status of true (0) or false (non-zero). Unlike the `test` command, the `[` command requires a closing `]`.', + args: 'EXPRESSION ]' + }, + 'break': { + shortDescription: 'Exit the current loop', + description: 'Terminate the execution of the nearest enclosing `while` or `for` loop and proceed with the next command after the loop.', + args: undefined + }, + 'breakpoint': { + shortDescription: 'Launch debug mode', + description: 'Pause execution and launch an interactive debug prompt. This is useful for inspecting the state of a script at a specific point.', + args: undefined + }, + 'case': { + shortDescription: 'Match a value against patterns', + description: 'Within a `switch` block, the `case` command specifies patterns to match against the given value, executing the associated block if a match is found.', + args: 'PATTERN...' + }, + 'continue': { + shortDescription: 'Skip to the next iteration of a loop', + description: 'Within a `while` or `for` loop, `continue` skips the remaining commands in the current iteration and proceeds to the next iteration of the loop.', + args: undefined + }, + 'else': { + shortDescription: 'Execute commands if the previous condition was false', + description: 'In an `if` block, the `else` section contains commands that execute if none of the preceding `if` or `else if` conditions were true.', + args: undefined + }, + 'end': { + shortDescription: 'Terminate a block of code', + description: 'Conclude a block of code initiated by constructs like `if`, `switch`, `while`, `for`, or `function`.', + args: undefined + }, + 'eval': { + shortDescription: 'Execute arguments as a command', + description: 'Concatenate all arguments into a single command and execute it. This allows for dynamic construction and execution of commands.', + args: 'COMMAND...' + }, + 'false': { + shortDescription: 'Return an unsuccessful result', + description: 'A command that returns a non-zero exit status, indicating failure. It is often used in scripts to represent a false condition.', + args: undefined + }, + 'realpath': { + shortDescription: 'Resolve and print the absolute path', + description: 'Convert each provided path to its absolute, canonical form by resolving symbolic links and relative path components.', + args: 'PATH...' + }, + ':': { + shortDescription: 'No operation command', + description: 'The `:` command is a no-op (no operation) command that returns a successful (zero) exit status. It can be used as a placeholder in scripts where a command is syntactically required but no action is desired.', + args: undefined + }, + 'test': { + shortDescription: 'Evaluate conditional expressions', + description: 'The `test` command evaluates conditional expressions and sets the exit status to 0 if the expression is true, and 1 if it is false. It supports various operators to evaluate expressions related to strings, numbers, and file attributes.', + args: 'EXPRESSION' + }, + 'true': { + shortDescription: 'Return a successful result', + description: 'The `true` command always returns a successful (zero) exit status. It is often used in scripts and conditional statements where an unconditional success result is needed.', + args: undefined + }, + 'printf': { + shortDescription: 'Display formatted text', + description: 'The `printf` command formats and prints text according to a specified format string. Unlike `echo`, `printf` does not append a newline unless explicitly included in the format.', + args: 'FORMAT [ARGUMENT...]' + } +}; + + +async function createCommandDescriptionsCache(): Promise { + const cachedCommandDescriptions: Map = new Map(); + + try { + // Get list of all builtins + const builtinsOutput = await execAsync('fish -c "builtin -n"').then(r => r.stdout.trim()); + const builtins = builtinsOutput.split('\n'); + + console.log(`Found ${builtins.length} Fish builtin commands`); + + for (const cmd of builtins) { + try { + // Get help info for each builtin + const helpOutput = await execAsync(`fish -c "${cmd} --help 2>&1"`).then(r => r.stdout); + let set = false; + if (helpOutput && !helpOutput.includes('No help for function') && !helpOutput.includes('See the web documentation')) { + const cleanHelpText = cleanupText(helpOutput); + + // Split the text into lines to process + const lines = cleanHelpText.split('\n'); + + + // Extract the short description, args, and full description + const { shortDescription, args, description } = extractHelpContent(cmd, lines); + + cachedCommandDescriptions.set(cmd, { + shortDescription, + description, + args + }); + set = description !== ''; + } + if (!set) { + // Use fallback descriptions for commands that don't return proper help + if (fallbackDescriptions[cmd]) { + console.info(`Using fallback description for ${cmd}`); + cachedCommandDescriptions.set(cmd, fallbackDescriptions[cmd]); + } else { + console.info(`No fallback description exists for ${cmd}`); + } + } + } catch { + // Use fallback descriptions for commands that throw an error + if (fallbackDescriptions[cmd]) { + console.info('Using fallback description for', cmd); + cachedCommandDescriptions.set(cmd, fallbackDescriptions[cmd]); + } else { + console.info(`Error getting help for ${cmd}`); + } + } + } + } catch (e) { + console.error('Error creating Fish builtins cache:', e); + process.exit(1); + } + + fishBuiltinsCommandDescriptionsCache = cachedCommandDescriptions; +} + +/** + * Extracts short description, args, and full description from help text lines + */ +function extractHelpContent(cmd: string, lines: string[]): { shortDescription: string; args: string | undefined; description: string } { + let shortDescription = ''; + let args: string | undefined; + let description = ''; + + // Skip the first line (usually just command name and basic usage) + let i = 1; + + // Skip any leading empty lines + while (i < lines.length && lines[i].trim().length === 0) { + i++; + } + + // The next non-empty line after the command name is typically + // either the short description or additional usage info + const startLine = i; + + // Find where the short description starts + if (i < lines.length) { + // First, check if the line has a command prefix and remove it + let firstContentLine = lines[i].trim(); + const cmdPrefixRegex = new RegExp(`^${cmd}\\s*-\\s*`, 'i'); + firstContentLine = firstContentLine.replace(cmdPrefixRegex, ''); + + // First non-empty line is the short description + shortDescription = firstContentLine; + i++; + + // Next non-empty line (after short description) is typically args + while (i < lines.length && lines[i].trim().length === 0) { + i++; + } + + if (i < lines.length) { + // Found a line after the short description - that's our args + args = lines[i].trim(); + i++; + } + } + + // Find the DESCRIPTION marker which marks the end of args section + let descriptionIndex = -1; + for (let j = i; j < lines.length; j++) { + if (lines[j].trim() === 'DESCRIPTION') { + descriptionIndex = j; + break; + } + } + + // If DESCRIPTION marker is found, consider everything between i and descriptionIndex as part of args + if (descriptionIndex > i) { + // Combine lines from i up to (but not including) descriptionIndex + const additionalArgs = lines.slice(i, descriptionIndex).join('\n').trim(); + if (additionalArgs) { + args = args ? `${args}\n${additionalArgs}` : additionalArgs; + } + i = descriptionIndex + 1; // Move past the DESCRIPTION line + } + + // The rest is the full description (skipping any empty lines after args) + while (i < lines.length && lines[i].trim().length === 0) { + i++; + } + + // Combine the remaining lines into the full description + description = lines.slice(Math.max(i, startLine)).join('\n').trim(); + + // If description is empty, use the short description + if (!description && shortDescription) { + description = shortDescription; + } + + // Extract just the first sentence for short description + const firstPeriodIndex = shortDescription.indexOf('.'); + if (firstPeriodIndex > 0) { + shortDescription = shortDescription.substring(0, firstPeriodIndex + 1).trim(); + } else if (shortDescription.length > 100) { + shortDescription = shortDescription.substring(0, 100) + '...'; + } + + return { + shortDescription, + args, + description + }; +} + +const main = async () => { + try { + await createCommandDescriptionsCache(); + console.log('Created Fish command descriptions cache with', fishBuiltinsCommandDescriptionsCache.size, 'entries'); + + // Save the cache to a TypeScript file + const cacheFilePath = path.join(__dirname, '../src/shell/fishBuiltinsCache.ts'); + const cacheObject = Object.fromEntries(fishBuiltinsCommandDescriptionsCache); + const tsContent = `${copyright}\n\nexport const fishBuiltinsCommandDescriptionsCache = ${JSON.stringify(cacheObject, null, 2)} as const;`; + await fs.writeFile(cacheFilePath, tsContent, 'utf8'); + console.log('Saved Fish command descriptions cache to fishBuiltinsCache.ts with', Object.keys(cacheObject).length, 'entries'); + } catch (error) { + console.error('Error:', error); + } +}; + +main(); diff --git a/extensions/terminal-suggest/scripts/pullZshBuiltins.ts b/extensions/terminal-suggest/scripts/pullZshBuiltins.ts index 9f60c302fc7e..a05866b16bc5 100644 --- a/extensions/terminal-suggest/scripts/pullZshBuiltins.ts +++ b/extensions/terminal-suggest/scripts/pullZshBuiltins.ts @@ -3,16 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { exec } from 'child_process'; -import { promisify } from 'util'; import * as fs from 'fs/promises'; import * as path from 'path'; -import { platform } from 'os'; +import { checkWindows, execAsync, copyright } from './terminalScriptHelpers'; -if (platform() === 'win32') { - console.error('\x1b[31mThis command is not supported on Windows\x1b[0m'); - process.exit(1); -} +checkWindows(); + +const latestZshVersion = 5.9; const shortDescriptions: Map = new Map([ ['.', 'Source a file'], @@ -24,7 +21,11 @@ const shortDescriptions: Map = new Map([ ['break', 'Exit from a loop'], ['builtin', 'Executes a builtin'], ['bye', 'Exit the shell'], + ['cap', 'Manipulating POSIX capability sets'], + ['cd', 'Change the current directory'], ['chdir', 'Change the current directory'], + ['clone', 'Clone shell onto another terminal'], + ['command', 'Execute a command'], ['comparguments', 'Complete arguments'], ['compcall', 'Complete call'], ['compctl', 'Complete control'], @@ -42,6 +43,8 @@ const shortDescriptions: Map = new Map([ ['disown', 'Remove job from job table'], ['echo', 'Write on standard output'], ['echotc', 'Echo terminal capabilities'], + ['echoti', 'Echo terminal info'], + ['emulate', 'Emulate a shell'], ['enable', 'Enable shell features'], ['eval', 'Execute arguments in shell'], ['exec', 'Replace shell with command'], @@ -53,6 +56,7 @@ const shortDescriptions: Map = new Map([ ['float', 'Floating point arithmetic'], ['functions', 'List functions'], ['getcap', 'Get capabilities'], + ['getln', 'Get line from buffer'], ['getopts', 'Parse positional parameters'], ['hash', 'Remember command locations'], ['history', 'Command history'], @@ -71,13 +75,18 @@ const shortDescriptions: Map = new Map([ ['pushln', 'Push arguments onto the buffer'], ['pwd', 'Print working directory'], ['r', 'Re-execute command'], + ['read', 'Read a line from input'], ['readonly', 'Mark variables as read-only'], ['rehash', 'Recompute command hash table'], + ['return', 'Return from a function'], ['sched', 'Schedule commands'], + ['set', 'Set shell options'], ['setcap', 'Set capabilities'], + ['setopt', 'Set shell options'], ['shift', 'Shift positional parameters'], ['source', 'Source a file'], ['stat', 'Display file status'], + ['suspend', 'Suspend the shell'], ['test', 'Evaluate a conditional expression'], ['times', 'Display shell times'], ['trap', 'Set signal handlers'], @@ -94,82 +103,115 @@ const shortDescriptions: Map = new Map([ ['unset', 'Unset values and attributes of variables'], ['unsetopt', 'Unset shell options'], ['vared', 'Edit shell variables'], + ['wait', 'Wait for a process'], ['whence', 'Locate a command'], ['where', 'Locate a command'], ['which', 'Locate a command'], ['zcompile', 'Compile functions'], ['zformat', 'Format strings'], ['zftp', 'Zsh FTP client'], + ['zle', 'Zsh line editor'], + ['zmodload', 'Load a module'], ['zparseopts', 'Parse options'], ['zprof', 'Zsh profiler'], + ['zpty', 'Zsh pseudo terminal'], + ['zregexparse', 'Parse regex'], ['zsocket', 'Zsh socket interface'], - ['zstyle', 'Define styles'] + ['zstyle', 'Define styles'], + ['ztcp', 'Manipulate TCP sockets'], ]); -const execAsync = promisify(exec); -let zshBuiltinsCommandDescriptionsCache = new Map(); +interface ICommandDetails { + description: string; + args: string | undefined; + shortDescription?: string; +} + +let zshBuiltinsCommandDescriptionsCache = new Map(); + async function createCommandDescriptionsCache(): Promise { const cachedCommandDescriptions: Map = new Map(); let output = ''; - + const zshVersionOutput = await execAsync('zsh --version').then(r => r.stdout); + const zshVersionMatch = zshVersionOutput.match(/zsh (\d+\.\d+)/); + if (!zshVersionMatch) { + console.error('\x1b[31mFailed to determine zsh version\x1b[0m'); + process.exit(1); + } + const zshVersion = parseFloat(zshVersionMatch[1]); + if (zshVersion < latestZshVersion) { + console.error(`\x1b[31mZsh version must be ${latestZshVersion} or higher\x1b[0m`); + process.exit(1); + } try { - output = await execAsync('man zshbuiltins').then(r => r.stdout); + output = await execAsync('pandoc --from man --to markdown --wrap=none < $(man -w zshbuiltins)').then(r => r.stdout); } catch { } + const commands: Map = new Map(); + const commandRegex = /^\*\*(?[a-z\.:]+)\*\*(?:\s\*.+\*)?(?:\s\\\[.+\\\])?$/; if (output) { - // Strip all backspaces from the output - output = output.replace(/.\x08/g, ''); const lines = output.split('\n'); - let currentCommand: string | undefined; - let currentDescription: string[] = []; - let currentArgs: string | undefined; - let commandSectionStarted = false; // Flag to ensure we ignore unrelated lines at the start - - for (let i = 0; i < lines.length; i++) { - const line = lines[i]; - - // Detect command names (lines starting with exactly 7 spaces) - const cmdMatch = line.match(/^\s{7}(\S+)(?:\s+(.*))?/); - if (cmdMatch?.length && cmdMatch.length > 1) { - commandSectionStarted = true; // Now we know we're in the right section - - // Store the previous command before moving on to the new one - if (currentCommand && currentDescription.join(' ').trim().length) { - const shortDescription = shortDescriptions.get(currentCommand); - const description = currentDescription.join(' ').trim(); - const args = `${currentCommand} ${currentArgs}`; - if (shortDescription) { - cachedCommandDescriptions.set(currentCommand, { - shortDescription, - description, - args - }); - } else { - cachedCommandDescriptions.set(currentCommand, { - description, - args - }); + let currentCommandStart = 0; + let seenOutput = false; + let i = 0; + for (; i < lines.length; i++) { + if (!currentCommand || seenOutput) { + const match = lines[i].match(commandRegex); + if (match?.groups?.command) { + if (currentCommand) { + commands.set(currentCommand, lines.slice(currentCommandStart, i)); } + currentCommand = match.groups.command; + currentCommandStart = i; + seenOutput = false; } - - // Start a new command entry - currentCommand = cmdMatch[1]; - currentArgs = cmdMatch[2]; - currentDescription = []; // Reset description for the new command } - // Capture description lines (14 spaces indentation) only if we have detected a command section - else if (commandSectionStarted && currentCommand && line.match(/^\s{14}/)) { - currentDescription.push(line.trim()); + if (!currentCommand) { + continue; } + // There may be several examples of usage + if (!seenOutput) { + seenOutput = lines[i].length > 0 && !lines[i].match(commandRegex); + } + } + if (currentCommand) { + commands.set(currentCommand, lines.slice(currentCommandStart, i - 1)); } + } - // Store the last command in the loop - if (currentCommand && currentDescription.join(' ').trim().length) { - cachedCommandDescriptions.set(currentCommand, { - description: currentDescription.join(' ').trim(), - args: currentArgs ? `${currentCommand} ${currentArgs}` : undefined + if (commands.size === 0) { + console.error('\x1b[31mFailed to parse command descriptions\x1b[30m'); + process.exit(1); + } + + for (const [command, lines] of commands) { + const shortDescription = shortDescriptions.get(command); + let argsEnd = 0; + try { + while (true) { + const line = lines[++argsEnd]; + if (line.trim().length > 0 && !line.match(commandRegex)) { + break; + } + } + } catch (e) { + console.log(e); + } + const formattedArgs = lines.slice(0, argsEnd - 1).join('\n'); + const args = (await execAsync(`pandoc --from markdown --to plain <<< "${formattedArgs}"`)).stdout.trim(); + const description = lines.slice(argsEnd).map(e => formatLineAsMarkdown(e)).join('\n').trim(); + if (shortDescription) { + cachedCommandDescriptions.set(command, { + shortDescription, + description, + args + }); + } else { + cachedCommandDescriptions.set(command, { + description, + args }); } } @@ -177,16 +219,36 @@ async function createCommandDescriptionsCache(): Promise { zshBuiltinsCommandDescriptionsCache = cachedCommandDescriptions; } +function formatLineAsMarkdown(text: string): string { + // Detect any inline code blocks which use the form `code' (backtick, single quote) and convert + // them to standard markdown `code` (backtick, backtick). This doesn't attempt to remove + // formatting inside the code blocks. We probably need to use the original .troff format to do + // this + const formattedText = text.replace(/\\`([^']+)\\'/g, '`$1`'); + return formattedText; +} const main = async () => { try { await createCommandDescriptionsCache(); console.log('created command descriptions cache with ', zshBuiltinsCommandDescriptionsCache.size, 'entries'); - // Save the cache to a JSON file - const cacheFilePath = path.join(__dirname, '../src/shell/zshBuiltinsCache.json'); + + const missingShortDescription: string[] = []; + for (const [command, entry] of zshBuiltinsCommandDescriptionsCache.entries()) { + if (entry.shortDescription === undefined) { + missingShortDescription.push(command); + } + } + if (missingShortDescription.length > 0) { + console.log('\x1b[31mmissing short description for commands:\n' + missingShortDescription.join('\n') + '\x1b[0m'); + } + + // Save the cache to a TypeScript file + const cacheFilePath = path.join(__dirname, '../src/shell/zshBuiltinsCache.ts'); const cacheObject = Object.fromEntries(zshBuiltinsCommandDescriptionsCache); - await fs.writeFile(cacheFilePath, JSON.stringify(cacheObject, null, 2), 'utf8'); - console.log('saved command descriptions cache to zshBuiltinsCache.json with ', Object.keys(cacheObject).length, 'entries'); + const tsContent = `${copyright}\n\nexport const zshBuiltinsCommandDescriptionsCache = ${JSON.stringify(cacheObject, null, 2)} as const;`; + await fs.writeFile(cacheFilePath, tsContent, 'utf8'); + console.log('saved command descriptions cache to zshBuiltinsCache.ts with ', Object.keys(cacheObject).length, 'entries'); } catch (error) { console.error('Error:', error); } diff --git a/extensions/terminal-suggest/scripts/terminalScriptHelpers.ts b/extensions/terminal-suggest/scripts/terminalScriptHelpers.ts new file mode 100644 index 000000000000..402e6d5d288a --- /dev/null +++ b/extensions/terminal-suggest/scripts/terminalScriptHelpers.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 { platform } from 'os'; +import { exec } from 'child_process'; +import { promisify } from 'util'; + +export const execAsync = promisify(exec); + +/** + * Cleans up text from terminal control sequences and formatting artifacts + */ +export function cleanupText(text: string): string { + // Remove ANSI escape codes + let cleanedText = text.replace(/\x1b\[\d+m/g, ''); + + // Remove backspace sequences (like a\bb which tries to print a, move back, print b) + // This regex looks for a character followed by a backspace and another character + const backspaceRegex = /.\x08./g; + while (backspaceRegex.test(cleanedText)) { + cleanedText = cleanedText.replace(backspaceRegex, match => match.charAt(2)); + } + + // Remove any remaining backspaces and their preceding characters + cleanedText = cleanedText.replace(/.\x08/g, ''); + + // Remove underscores that are used for formatting in some fish help output + cleanedText = cleanedText.replace(/_\b/g, ''); + + return cleanedText; +} + +/** + * Copyright notice for generated files + */ +export const copyright = `/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/`; + +/** + * Checks if the script is running on Windows and exits if so + */ +export function checkWindows(): void { + if (platform() === 'win32') { + console.error('\x1b[31mThis command is not supported on Windows\x1b[0m'); + process.exit(1); + } +} diff --git a/extensions/terminal-suggest/scripts/update-specs.js b/extensions/terminal-suggest/scripts/update-specs.js index 3573c6664d6d..775c5dd32648 100644 --- a/extensions/terminal-suggest/scripts/update-specs.js +++ b/extensions/terminal-suggest/scripts/update-specs.js @@ -3,14 +3,55 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +// @ts-check + const fs = require('fs'); const path = require('path'); const upstreamSpecs = require('../out/constants.js').upstreamSpecs; - const extRoot = path.resolve(path.join(__dirname, '..')); +const replaceStrings = [ + [ + 'import { filepaths } from "@fig/autocomplete-generators";', + 'import { filepaths } from \'../../helpers/filepaths\';' + ], +]; +const indentSearch = [20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1].map(e => new RegExp('^' + ' '.repeat(e * 2), 'gm')); +const indentReplaceValue = [20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1].map(e => '\t'.repeat(e)); + +const specSpecificReplaceStrings = new Map([ + ['git', [ + [ + 'import { ai } from "@fig/autocomplete-generators";', + 'function ai(...args: any[]): undefined { return undefined; }' + ], [ + 'prompt: async ({ executeCommand }) => {', + 'prompt: async ({ executeCommand }: any) => {' + ], [ + 'message: async ({ executeCommand }) =>', + 'message: async ({ executeCommand }: any) =>' + ] + ]], +]); + for (const spec of upstreamSpecs) { const source = path.join(extRoot, `third_party/autocomplete/src/${spec}.ts`); const destination = path.join(extRoot, `src/completions/upstream/${spec}.ts`); fs.copyFileSync(source, destination); + + let content = fs.readFileSync(destination).toString(); + for (const replaceString of replaceStrings) { + content = content.replaceAll(replaceString[0], replaceString[1]); + } + for (let i = 0; i < indentSearch.length; i++) { + content = content.replaceAll(indentSearch[i], indentReplaceValue[i]); + } + const thisSpecReplaceStrings = specSpecificReplaceStrings.get(spec); + if (thisSpecReplaceStrings) { + for (const replaceString of thisSpecReplaceStrings) { + content = content.replaceAll(replaceString[0], replaceString[1]); + } + } + + fs.writeFileSync(destination, content); } diff --git a/extensions/terminal-suggest/src/completions/code-insiders.ts b/extensions/terminal-suggest/src/completions/code-insiders.ts index 89d01dc536ff..1eb4f04acb17 100644 --- a/extensions/terminal-suggest/src/completions/code-insiders.ts +++ b/extensions/terminal-suggest/src/completions/code-insiders.ts @@ -2,12 +2,17 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import code from './code'; +import code, { commonOptions, extensionManagementOptions, troubleshootingOptions } from './code'; const codeInsidersCompletionSpec: Fig.Spec = { ...code, name: 'code-insiders', description: 'Visual Studio Code Insiders', + options: [ + ...commonOptions, + ...extensionManagementOptions('code-insiders'), + ...troubleshootingOptions('code-insiders'), + ], }; export default codeInsidersCompletionSpec; diff --git a/extensions/terminal-suggest/src/completions/code.ts b/extensions/terminal-suggest/src/completions/code.ts index c514a32e2e19..919253c387f6 100644 --- a/extensions/terminal-suggest/src/completions/code.ts +++ b/extensions/terminal-suggest/src/completions/code.ts @@ -3,7 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -const commonOptions: Fig.Option[] = [ +import { filepaths } from '../helpers/filepaths'; + +export const commonOptions: Fig.Option[] = [ { name: '-', description: `Read from stdin (e.g. 'ps aux | grep code | code -')`, @@ -60,7 +62,6 @@ const commonOptions: Fig.Option[] = [ 'Open a file at the path on the specified line and character position', args: { name: 'file:line[:character]', - // TODO: Support :line[:character] completion? template: 'filepaths', }, }, @@ -135,9 +136,25 @@ const commonOptions: Fig.Option[] = [ name: ['-h', '--help'], description: 'Print usage', }, + { + name: '--locate-shell-integration-path', + description: + 'Print the path to the shell integration script for the provided shell', + args: { + isOptional: false, + name: 'shell', + description: 'The shell to locate the integration script for', + suggestions: [ + 'bash', + 'fish', + 'pwsh', + 'zsh', + ] + } + } ]; -const extensionManagementOptions: Fig.Option[] = [ +export const extensionManagementOptions = (cliName: string): Fig.Option[] => [ { name: '--extensions-dir', description: 'Set the root path for extensions', @@ -188,8 +205,13 @@ const extensionManagementOptions: Fig.Option[] = [ description: `Installs or updates an extension. The argument is either an extension id or a path to a VSIX. The identifier of an extension is '\${ publisher }.\${ name }'. Use '--force' argument to update to latest version. To install a specific version provide '@\${version}'. For example: 'vscode.csharp@1.2.3'`, args: { - // TODO: Create extension ID generator name: 'extension-id[@version] | path-to-vsix', + generators: [ + createCodeGenerators(cliName), + filepaths({ + extensions: ['vsix'], + }), + ], }, }, { @@ -201,8 +223,8 @@ const extensionManagementOptions: Fig.Option[] = [ name: '--uninstall-extension', description: 'Uninstalls an extension', args: { - // TODO: Create extension ID generator name: 'extension-id', + generators: createCodeGenerators(cliName) }, }, { @@ -212,7 +234,7 @@ const extensionManagementOptions: Fig.Option[] = [ }, ]; -const troubleshootingOptions: Fig.Option[] = [ +export const troubleshootingOptions = (cliName: string): Fig.Option[] => [ { name: ['-v', '--version'], description: 'Print version', @@ -254,8 +276,8 @@ const troubleshootingOptions: Fig.Option[] = [ name: '--disable-extension', description: 'Disable an extension', args: { - // TODO: Create extension ID generator name: 'extension-id', + generators: createCodeGenerators(cliName) }, }, { @@ -301,6 +323,26 @@ const troubleshootingOptions: Fig.Option[] = [ }, ]; +export function createCodeGenerators(cliName: string): Fig.Generator { + return { + script: [cliName, '--list-extensions', '--show-versions'], + postProcess: parseInstalledExtensions + }; +} + +export function parseInstalledExtensions(out: string): Fig.Suggestion[] | undefined { + const extensions = out.split('\n').filter(Boolean).map((line) => { + const [id, version] = line.split('@'); + return { + name: id, + type: 'option' as Fig.SuggestionType, + description: `Version: ${version}` + }; + }); + return extensions; +} + + const codeCompletionSpec: Fig.Spec = { name: 'code', description: 'Visual Studio Code', @@ -310,9 +352,10 @@ const codeCompletionSpec: Fig.Spec = { }, options: [ ...commonOptions, - ...extensionManagementOptions, - ...troubleshootingOptions, + ...extensionManagementOptions('code'), + ...troubleshootingOptions('code'), ], }; export default codeCompletionSpec; + diff --git a/extensions/terminal-suggest/src/completions/index.d.ts b/extensions/terminal-suggest/src/completions/index.d.ts index 44dcb9814456..56ff7fac7fcf 100644 --- a/extensions/terminal-suggest/src/completions/index.d.ts +++ b/extensions/terminal-suggest/src/completions/index.d.ts @@ -1296,5 +1296,50 @@ declare namespace Fig { * */ cache?: Cache; + + /** + * Options related to file paths used in the generator, including filtering options and handling of file extensions. + */ + filepathOptions?: FilepathsOptions; // <-- VS Code edit to return file resource request config as we resolve files in core + } + + type FilepathsOptions = { + /** + * Show suggestions with any of these extensions. Do not include the leading dot. + */ + extensions?: string[]; + /** + * Show suggestions where the name exactly matches one of these strings + */ + equals?: string | string[]; + /** + * Show suggestions where the name matches this expression + */ + matches?: RegExp; + /** + * Will treat folders like files, filtering based on the name. + */ + filterFolders?: boolean; + /** + * Set properties of suggestions of type 'file'. + */ + editFileSuggestions?: Omit; + /** + * Set properties of suggestions of type 'folder'. + */ + editFolderSuggestions?: Omit; + /** + * Start to suggest filepaths and folders from this directory. + */ + rootDirectory?: string; + /** + * Set how the generator should display folders: + * - **Default:** `always` will always suggest folders. + * - `never`: will never suggest folders. + * - `only`: will show only folders and no files. + */ + showFolders?: 'always' | 'never' | 'only'; } } + + diff --git a/extensions/terminal-suggest/src/completions/npx.ts b/extensions/terminal-suggest/src/completions/npx.ts new file mode 100644 index 000000000000..360103911d6b --- /dev/null +++ b/extensions/terminal-suggest/src/completions/npx.ts @@ -0,0 +1,145 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +const completionSpec: Fig.Spec = { + name: 'npx', + description: 'Execute binaries from npm packages', + args: { + name: 'command', + isCommand: true, + generators: { + script: [ + 'bash', + '-c', + 'until [[ -d node_modules/ ]] || [[ $PWD = \'/\' ]]; do cd ..; done; ls -1 node_modules/.bin/', + ], + postProcess: function (out) { + return out + .split('\n') + .map((name) => ({ + name, + icon: 'fig://icon?type=command', + loadSpec: name, + })); + }, + }, + isOptional: true, + }, + + options: [ + { + name: ['--package', '-p'], + description: 'Package to be installed', + args: { + name: 'package', + }, + }, + { + name: '--cache', + args: { + name: 'path', + template: 'filepaths', + }, + description: 'Location of the npm cache', + }, + { + name: '--always-spawn', + description: 'Always spawn a child process to execute the command', + }, + { + name: '-y', + description: 'Execute npx command without prompting for confirmation', + }, + { + description: 'Skip installation if a package is missing', + name: '--no-install', + }, + { + args: { + name: 'path', + template: 'filepaths', + }, + description: 'Path to user npmrc', + name: '--userconfig', + }, + { + name: ['--call', '-c'], + args: { + name: 'script', + }, + description: 'Execute string as if inside `npm run-script`', + }, + { + name: ['--shell', '-s'], + description: 'Shell to execute the command with, if any', + args: { + name: 'shell', + suggestions: [ + { + name: 'bash', + }, + { + name: 'fish', + }, + { + name: 'zsh', + }, + ], + }, + }, + { + args: { + name: 'shell-fallback', + suggestions: [ + { + name: 'bash', + }, + { + name: 'fish', + }, + { + name: 'zsh', + }, + ], + }, + name: '--shell-auto-fallback', + description: + 'Generate shell code to use npx as the "command not found" fallback', + }, + { + name: '--ignore-existing', + description: + 'Ignores existing binaries in $PATH, or in the localproject. This forces npx to do a temporary install and use the latest version', + }, + { + name: ['--quiet', '-q'], + description: + 'Suppress output from npx itself. Subcommands will not be affected', + }, + { + name: '--npm', + args: { + name: 'path to binary', + template: 'filepaths', + }, + description: 'Npm binary to use for internal operations', + }, + { + args: {}, + description: 'Extra node argument when calling a node binary', + name: ['--node-arg', '-n'], + }, + { + description: 'Show version number', + name: ['--version', '-v'], + }, + { + description: 'Show help', + name: ['--help', '-h'], + }, + ], +}; + +export default completionSpec; diff --git a/extensions/terminal-suggest/src/completions/set-location.ts b/extensions/terminal-suggest/src/completions/set-location.ts new file mode 100644 index 000000000000..ae550d6fb992 --- /dev/null +++ b/extensions/terminal-suggest/src/completions/set-location.ts @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +const cdSpec: Fig.Spec = { + name: 'Set-Location', + description: 'Change the shell working directory', + args: { + name: 'folder', + template: 'folders', + suggestions: [ + { + name: '-', + description: 'Go to previous directory in history stack', + hidden: true, + }, + { + name: '+', + description: 'Go to next directory in history stack', + hidden: true, + }, + ], + } +}; + +export default cdSpec; diff --git a/extensions/terminal-suggest/src/completions/upstream/apt.ts b/extensions/terminal-suggest/src/completions/upstream/apt.ts index c2157963fd9b..a6ec97abfd0a 100644 --- a/extensions/terminal-suggest/src/completions/upstream/apt.ts +++ b/extensions/terminal-suggest/src/completions/upstream/apt.ts @@ -1,4 +1,4 @@ -// import { filepaths } from "@fig/autocomplete-generators"; +import { filepaths } from '../../fig/autocomplete-tools/generators' const packages: Fig.Generator = { // only trigger when the token length transitions to or from 0 @@ -137,7 +137,7 @@ const completionSpec: Fig.Spec = { template: 'filepaths', generators: [ packages, - // filepaths({ extensions: ["deb"] }) + filepaths({ extensions: ["deb"] }) ], }, options: [ diff --git a/extensions/terminal-suggest/src/completions/upstream/brew.ts b/extensions/terminal-suggest/src/completions/upstream/brew.ts index 99b87c45ca9a..4e0f0cbe2bb8 100644 --- a/extensions/terminal-suggest/src/completions/upstream/brew.ts +++ b/extensions/terminal-suggest/src/completions/upstream/brew.ts @@ -1,1675 +1,1675 @@ const servicesGenerator = (action: string): Fig.Generator => ({ - script: ["bash", "-c", "brew services list | sed -e 's/ .*//' | tail -n +2"], - postProcess: function (out) { - return out - .split("\n") - .filter((line) => !line.includes("unbound")) - .map((line) => ({ - name: line, - icon: "fig://icon?type=package", - description: `${action} ${line}`, - })); - }, + script: ["bash", "-c", "brew services list | sed -e 's/ .*//' | tail -n +2"], + postProcess: function (out) { + return out + .split("\n") + .filter((line) => !line.includes("unbound")) + .map((line) => ({ + name: line, + icon: "fig://icon?type=package", + description: `${action} ${line}`, + })); + }, }); const repositoriesGenerator = (): Fig.Generator => ({ - script: ["brew", "tap"], - postProcess: (out) => { - return out.split("\n").map((line) => ({ name: line })); - }, + script: ["brew", "tap"], + postProcess: (out) => { + return out.split("\n").map((line) => ({ name: line })); + }, }); const formulaeGenerator: Fig.Generator = { - script: ["brew", "list", "-1"], - postProcess: function (out) { - return out - .split("\n") - .filter((line) => !line.includes("=")) - .map((formula) => ({ - name: formula, - icon: "🍺", - description: "Installed formula", - })); - }, + script: ["brew", "list", "-1"], + postProcess: function (out) { + return out + .split("\n") + .filter((line) => !line.includes("=")) + .map((formula) => ({ + name: formula, + icon: "🍺", + description: "Installed formula", + })); + }, }; const outdatedformulaeGenerator: Fig.Generator = { - script: ["brew", "outdated", "-q"], - postProcess: function (out) { - return out.split("\n").map((formula) => ({ - name: formula, - icon: "🍺", - description: "Outdated formula", - })); - }, + script: ["brew", "outdated", "-q"], + postProcess: function (out) { + return out.split("\n").map((formula) => ({ + name: formula, + icon: "🍺", + description: "Outdated formula", + })); + }, }; const generateAllFormulae: Fig.Generator = { - script: ["brew", "formulae"], - postProcess: function (out) { - return out.split("\n").map((formula) => ({ - name: formula, - icon: "🍺", - description: "Formula", - priority: 51, - })); - }, + script: ["brew", "formulae"], + postProcess: function (out) { + return out.split("\n").map((formula) => ({ + name: formula, + icon: "🍺", + description: "Formula", + priority: 51, + })); + }, }; const generateAllCasks: Fig.Generator = { - script: ["brew", "casks"], - postProcess: function (out) { - return out.split("\n").map((cask) => ({ - name: cask, - icon: "🍺", - description: "Cask", - priority: 52, - })); - }, + script: ["brew", "casks"], + postProcess: function (out) { + return out.split("\n").map((cask) => ({ + name: cask, + icon: "🍺", + description: "Cask", + priority: 52, + })); + }, }; const generateAliases: Fig.Generator = { - script: [ - "bash", - "-c", - 'find ~/.brew-aliases/ -type f ! -name "*.*" -d 1 | sed "s/.*\\///"', - ], - postProcess: function (out) { - return out - .split("\n") - .filter((line) => line && line.trim() !== "") - .map((line) => ({ - name: line, - icon: "fig://icon?type=command", - description: `Execute alias ${line}`, - })); - }, + script: [ + "bash", + "-c", + 'find ~/.brew-aliases/ -type f ! -name "*.*" -d 1 | sed "s/.*\\///"', + ], + postProcess: function (out) { + return out + .split("\n") + .filter((line) => line && line.trim() !== "") + .map((line) => ({ + name: line, + icon: "fig://icon?type=command", + description: `Execute alias ${line}`, + })); + }, }; const commonOptions: Fig.Option[] = [ - { - name: ["-d", "--debug"], - description: "Display any debugging information", - }, - { - name: ["-q", "--quiet"], - description: "Make some output more quiet", - }, - { - name: ["-v", "--verbose"], - description: "Make some output more verbose", - }, - { name: ["-h", "--help"], description: "Show help message" }, + { + name: ["-d", "--debug"], + description: "Display any debugging information", + }, + { + name: ["-q", "--quiet"], + description: "Make some output more quiet", + }, + { + name: ["-v", "--verbose"], + description: "Make some output more verbose", + }, + { name: ["-h", "--help"], description: "Show help message" }, ]; const completionSpec: Fig.Spec = { - name: "brew", - description: "Package manager for macOS", - subcommands: [ - { - name: "list", - description: "List all installed formulae", - options: [ - ...commonOptions, - { - name: ["--formula", "--formulae"], - description: - "List only formulae, or treat all named arguments as formulae", - }, - { - name: ["--cask", "--casks"], - description: "List only casks, or treat all named arguments as casks", - }, - { - name: "--unbrewed", - description: - "List files in Homebrew's prefix not installed by Homebrew. (disabled; replaced by brew --prefix --unbrewed)", - }, - { - name: "--full-name", - description: - "Print formulae with fully-qualified names. Unless --full-name, --versions or", - }, - { - name: "--pinned", - description: - "List only pinned formulae, or only the specified (pinned) formulae if formula are provided", - }, - { - name: "--versions", - description: - "Show the version number for installed formulae, or only the specified formulae if formula are provided", - }, - { - name: "--multiple", - description: "Only show formulae with multiple versions installed", - }, - { - name: "--pinned", - description: - "List only pinned formulae, or only the specified (pinned) formulae if formula are provided. See also pin, unpin", - }, - { - name: "-1", - description: - "Force output to be one entry per line. This is the default when output is not to a terminal", - }, - { - name: "-l", - description: - "List formulae and/or casks in long format. Has no effect when a formula or cask name is passed as an argument", - }, - { - name: "-r", - description: - "Reverse the order of the formulae and/or casks sort to list the oldest entries first. Has no effect when a formula or cask name is passed as an argument", - }, - { - name: "-t", - description: - "Sort formulae and/or casks by time modified, listing most recently modified first. Has no effect when a formula or cask name is passed as an argument", - }, - ], - args: { - isOptional: true, - isVariadic: true, - name: "formula", - generators: formulaeGenerator, - }, - }, - { - name: "ls", - description: "List all installed formulae", - options: [ - ...commonOptions, - { - name: "--formula", - description: - "List only formulae, or treat all named arguments as formulae", - }, - { - name: "--cask", - description: "List only casks, or treat all named arguments as casks", - }, - { - name: "--unbrewed", - description: - "List files in Homebrew's prefix not installed by Homebrew. (disabled; replaced by brew --prefix --unbrewed)", - }, - { - name: "--full-name", - description: - "Print formulae with fully-qualified names. Unless --full-name, --versions or", - }, - { - name: "--pinned", - description: - "List only pinned formulae, or only the specified (pinned) formulae if formula are provided", - }, - { - name: "--versions", - description: - "Show the version number for installed formulae, or only the specified formulae if formula are provided", - }, - { - name: "--multiple", - description: "Only show formulae with multiple versions installed", - }, - { - name: "--pinned", - description: - "List only pinned formulae, or only the specified (pinned) formulae if formula are provided", - }, - { - name: "-1", - description: - "Force output to be one entry per line. This is the default when output is not to a terminal", - }, - { - name: "-l", - description: - "List formulae and/or casks in long format. Has no effect when a formula or cask name is passed as an argument", - }, - { - name: "-r", - description: - "Reverse the order of the formulae and/or casks sort to list the oldest entries first. Has no effect when a formula or cask name is passed as an argument", - }, - { - name: "-t", - description: - "Sort formulae and/or casks by time modified, listing most recently modified first. Has no effect when a formula or cask name is passed as an argument", - }, - ], - args: { - isOptional: true, - isVariadic: true, - name: "formula", - generators: formulaeGenerator, - }, - }, - { - name: "leaves", - description: - "List installed formulae that are not dependencies of another installed formula", - options: [ - { - name: ["-r", "--installed-on-request"], - description: "Show manually installed formula", - }, - { - name: ["-p", "--installed-as-dependency"], - description: "Show installed formula as dependencies", - }, - ], - }, - { - name: "doctor", - description: "Check your system for potential problems", - options: [ - ...commonOptions, - { - name: "--list-checks", - description: "List all audit methods", - }, - { - name: ["-D", "--audit-debug"], - description: "Enable debugging and profiling of audit methods", - }, - ], - }, - { - name: ["abv", "info"], - description: "Display brief statistics for your Homebrew installation", - args: { - isVariadic: true, - isOptional: true, - name: "formula", - description: "Formula or cask to summarize", - generators: [generateAllFormulae, generateAllCasks], - }, - options: [ - { - name: ["--cask", "--casks"], - description: "List only casks, or treat all named arguments as casks", - }, - { - name: "--analytics", - description: - "List global Homebrew analytics data or, if specified, installation and build error data for formula", - }, - { - name: "--days", - description: "How many days of analytics data to retrieve", - exclusiveOn: ["--analytics"], - args: { - name: "days", - description: "Number of days of data to retrieve", - suggestions: ["30", "90", "365"], - }, - }, - { - name: "--category", - description: "Which type of analytics data to retrieve", - exclusiveOn: ["--analytics"], - args: { - generators: { - custom: async (ctx) => { - // if anything provided after the subcommand does not begin with '-' - // then a formula has been provided and we should provide info on it - if ( - ctx.slice(2, ctx.length - 1).some((token) => token[0] !== "-") - ) { - return ["install", "install-on-request", "build-error"].map( - (sugg) => ({ - name: sugg, - }) - ); - } + name: "brew", + description: "Package manager for macOS", + subcommands: [ + { + name: "list", + description: "List all installed formulae", + options: [ + ...commonOptions, + { + name: ["--formula", "--formulae"], + description: + "List only formulae, or treat all named arguments as formulae", + }, + { + name: ["--cask", "--casks"], + description: "List only casks, or treat all named arguments as casks", + }, + { + name: "--unbrewed", + description: + "List files in Homebrew's prefix not installed by Homebrew. (disabled; replaced by brew --prefix --unbrewed)", + }, + { + name: "--full-name", + description: + "Print formulae with fully-qualified names. Unless --full-name, --versions or", + }, + { + name: "--pinned", + description: + "List only pinned formulae, or only the specified (pinned) formulae if formula are provided", + }, + { + name: "--versions", + description: + "Show the version number for installed formulae, or only the specified formulae if formula are provided", + }, + { + name: "--multiple", + description: "Only show formulae with multiple versions installed", + }, + { + name: "--pinned", + description: + "List only pinned formulae, or only the specified (pinned) formulae if formula are provided. See also pin, unpin", + }, + { + name: "-1", + description: + "Force output to be one entry per line. This is the default when output is not to a terminal", + }, + { + name: "-l", + description: + "List formulae and/or casks in long format. Has no effect when a formula or cask name is passed as an argument", + }, + { + name: "-r", + description: + "Reverse the order of the formulae and/or casks sort to list the oldest entries first. Has no effect when a formula or cask name is passed as an argument", + }, + { + name: "-t", + description: + "Sort formulae and/or casks by time modified, listing most recently modified first. Has no effect when a formula or cask name is passed as an argument", + }, + ], + args: { + isOptional: true, + isVariadic: true, + name: "formula", + generators: formulaeGenerator, + }, + }, + { + name: "ls", + description: "List all installed formulae", + options: [ + ...commonOptions, + { + name: "--formula", + description: + "List only formulae, or treat all named arguments as formulae", + }, + { + name: "--cask", + description: "List only casks, or treat all named arguments as casks", + }, + { + name: "--unbrewed", + description: + "List files in Homebrew's prefix not installed by Homebrew. (disabled; replaced by brew --prefix --unbrewed)", + }, + { + name: "--full-name", + description: + "Print formulae with fully-qualified names. Unless --full-name, --versions or", + }, + { + name: "--pinned", + description: + "List only pinned formulae, or only the specified (pinned) formulae if formula are provided", + }, + { + name: "--versions", + description: + "Show the version number for installed formulae, or only the specified formulae if formula are provided", + }, + { + name: "--multiple", + description: "Only show formulae with multiple versions installed", + }, + { + name: "--pinned", + description: + "List only pinned formulae, or only the specified (pinned) formulae if formula are provided", + }, + { + name: "-1", + description: + "Force output to be one entry per line. This is the default when output is not to a terminal", + }, + { + name: "-l", + description: + "List formulae and/or casks in long format. Has no effect when a formula or cask name is passed as an argument", + }, + { + name: "-r", + description: + "Reverse the order of the formulae and/or casks sort to list the oldest entries first. Has no effect when a formula or cask name is passed as an argument", + }, + { + name: "-t", + description: + "Sort formulae and/or casks by time modified, listing most recently modified first. Has no effect when a formula or cask name is passed as an argument", + }, + ], + args: { + isOptional: true, + isVariadic: true, + name: "formula", + generators: formulaeGenerator, + }, + }, + { + name: "leaves", + description: + "List installed formulae that are not dependencies of another installed formula", + options: [ + { + name: ["-r", "--installed-on-request"], + description: "Show manually installed formula", + }, + { + name: ["-p", "--installed-as-dependency"], + description: "Show installed formula as dependencies", + }, + ], + }, + { + name: "doctor", + description: "Check your system for potential problems", + options: [ + ...commonOptions, + { + name: "--list-checks", + description: "List all audit methods", + }, + { + name: ["-D", "--audit-debug"], + description: "Enable debugging and profiling of audit methods", + }, + ], + }, + { + name: ["abv", "info"], + description: "Display brief statistics for your Homebrew installation", + args: { + isVariadic: true, + isOptional: true, + name: "formula", + description: "Formula or cask to summarize", + generators: [generateAllFormulae, generateAllCasks], + }, + options: [ + { + name: ["--cask", "--casks"], + description: "List only casks, or treat all named arguments as casks", + }, + { + name: "--analytics", + description: + "List global Homebrew analytics data or, if specified, installation and build error data for formula", + }, + { + name: "--days", + description: "How many days of analytics data to retrieve", + exclusiveOn: ["--analytics"], + args: { + name: "days", + description: "Number of days of data to retrieve", + suggestions: ["30", "90", "365"], + }, + }, + { + name: "--category", + description: "Which type of analytics data to retrieve", + exclusiveOn: ["--analytics"], + args: { + generators: { + custom: async (ctx) => { + // if anything provided after the subcommand does not begin with '-' + // then a formula has been provided and we should provide info on it + if ( + ctx.slice(2, ctx.length - 1).some((token) => token[0] !== "-") + ) { + return ["install", "install-on-request", "build-error"].map( + (sugg) => ({ + name: sugg, + }) + ); + } - // if no formulas are specified, then we should provide system info - return ["cask-install", "os-version"].map((sugg) => ({ - name: sugg, - })); - }, - }, - }, - }, - { - name: "--github", - description: "Open the GitHub source page for formula in a browser", - }, - { - name: "--json", - description: "Print a JSON representation", - }, - { - name: "--installed", - exclusiveOn: ["--json"], - description: "Print JSON of formulae that are currently installed", - }, - { - name: "--all", - exclusiveOn: ["--json"], - description: "Print JSON of all available formulae", - }, - { - name: ["-v", "--verbose"], - description: "Show more verbose analytics data for formulae", - }, - { - name: "--formula", - description: "Treat all named arguments as formulae", - }, - { - name: "--cash", - description: "Treat all named arguments as casks", - }, - { - name: ["-d", "--debug"], - description: "Display any debugging information", - }, - { - name: ["-q", "--quiet"], - description: "List only the names of outdated kegs", - }, - { - name: ["-h", "--help"], - description: "Get help with services command", - }, - ], - }, - { - name: "update", - description: "Fetch the newest version of Homebrew and all formulae", - options: [ - { - name: ["-f", "--force"], - description: "Always do a slower, full update check", - }, - { - name: ["-v", "--verbose"], - description: - "Print the directories checked and git operations performed", - }, - { - name: ["-d", "--debug"], - description: - "Display a trace of all shell commands as they are executed", - }, - { name: ["-h", "--help"], description: "Show help message" }, - { - name: "--merge", - description: - "Use git merge to apply updates (rather than git rebase)", - }, - { - name: "--preinstall", - description: - "Run on auto-updates (e.g. before brew install). Skips some slower steps", - }, - ], - }, - { - name: "outdated", - description: - "List installed casks and formulae that have an updated version available", - options: [ - { - name: ["-d", "--debug"], - description: "Display any debugging information", - }, - { - name: ["-q", "--quiet"], - description: "List only the names of outdated kegs", - }, - { - name: ["-v", "--verbose"], - description: "Include detailed version information", - }, - { - name: ["-h", "--help"], - description: "Show help message for the outdated command", - }, - { name: "--cask", description: "List only outdated casks" }, - { - name: "--fetch-HEAD", - description: - "Fetch the upstream repository to detect if the HEAD installation of the formula is outdated", - }, - { name: "--formula", description: "List only outdated formulae" }, - { - name: "--greedy", - description: - "Print outdated casks with auto_updates or version :latest", - }, - { - name: "--greedy-latest", - description: - "Print outdated casks including those with version :latest", - }, - { - name: "--greedy-auto-updates", - description: - "Print outdated casks including those with auto_updates true", - }, - { name: "--json", description: "Print output in JSON format" }, - ], - }, - { - name: "pin", - description: "Pin formula, preventing them from being upgraded", - options: commonOptions, - args: { - isVariadic: true, - name: "formula", - generators: formulaeGenerator, - }, - }, - { - name: "unpin", - description: "Unpin formula, allowing them to be upgraded", - options: commonOptions, - args: { - isVariadic: true, - name: "formula", - generators: formulaeGenerator, - }, - }, - { - name: "upgrade", - description: - "Upgrade outdated casks and outdated, unpinned formulae using the same options they were originally installed with, plus any appended brew formula options", - options: [ - { - name: ["-d", "--debug"], - description: - "If brewing fails, open an interactive debugging session with access to IRB or a shell inside the temporary build directory", - }, - { - name: ["-f", "--force"], - description: - "Install formulae without checking for previously installed keg-only or non-migrated versions. When installing casks, overwrite existing files (binaries and symlinks are excluded, unless originally from the same cask)", - }, - { - name: ["-v", "--verbose"], - description: "Print the verification and postinstall steps", - }, - { - name: ["-n", "--dry-run"], - description: - "Show what would be upgraded, but do not actually upgrade anything", - }, - { - name: ["-s", "--build-from-source"], - description: - "Compile formula from source even if a bottle is provided. Dependencies will still be installed from bottles if they are available", - }, - { - name: ["-i", "--interactive"], - description: "Download and patch formula, then open a shell", - }, - { name: ["-g", "--git"], description: "Create a Git repository" }, - { - name: ["-q", "--quiet"], - description: "Make some output more quiet", - }, - { name: ["-h", "--help"], description: "Show this message" }, - { - name: ["--formula", "--formulae"], - description: - "Treat all named arguments as formulae. If no named arguments are specified, upgrade only outdated formulae", - }, - { - name: "--env", - description: "Disabled other than for internal Homebrew use", - }, - { - name: "--ignore-dependencies", - description: - "An unsupported Homebrew development flag to skip installing any dependencies of any kind. If the dependencies are not already present, the formula will have issues. If you're not developing Homebrew, consider adjusting your PATH rather than using this flag", - }, - { - name: "--only-dependencies", - description: - "Install the dependencies with specified options but do not install the formula itself", - }, - { - name: "--cc", - description: - "Attempt to compile using the specified compiler, which should be the name of the compiler's executable", - args: { - name: "compiler", - suggestions: ["gcc-7", "llvm_clang", "clang"], - }, - }, - { - name: "--force-bottle", - description: - "Install from a bottle if it exists for the current or newest version of macOS, even if it would not normally be used for installation", - }, - { - name: "--include-test", - description: - "Install testing dependencies required to run brew test formula", - }, - { - name: "--HEAD", - description: - "If formula defines it, install the HEAD version, aka. main, trunk, unstable, master", - }, - { - name: "--fetch-HEAD", - description: - "Fetch the upstream repository to detect if the HEAD installation of the formula is outdated. Otherwise, the repository's HEAD will only be checked for updates when a new stable or development version has been released", - }, - { - name: "--ignore-pinned", - description: - "Set a successful exit status even if pinned formulae are not upgraded", - }, - { - name: "--keep-tmp", - description: "Retain the temporary files created during installation", - }, - { - name: "--build-bottle", - description: - "Prepare the formula for eventual bottling during installation, skipping any post-install steps", - }, - { - name: "--bottle-arch", - description: - "Optimise bottles for the specified architecture rather than the oldest architecture supported by the version of macOS the bottles are built on", - }, - { - name: "--display-times", - description: - "Print install times for each formula at the end of the run", - }, - { - name: ["--cask", "--casks"], - description: - "Treat all named arguments as casks. If no named arguments are specified, upgrade only outdated casks", - }, - { - name: "--binaries", - description: - "Disable/enable linking of helper executables (default: enabled)", - exclusiveOn: ["--no-binaries"], - }, - { - name: "--no-binaries", - description: - "Disable/enable linking of helper executables (default: enabled)", - exclusiveOn: ["--binaries"], - }, - { - name: "--require-sha", - description: "Require all casks to have a checksum", - }, - { - name: "--quarantine", - description: - "Disable/enable quarantining of downloads (default: enabled)", - exclusiveOn: ["--no-quarantine"], - }, - { - name: "--no-quarantine", - description: - "Disable/enable quarantining of downloads (default: enabled)", - exclusiveOn: ["--quarantine"], - }, - { - name: "--skip-cask-deps", - description: "Skip installing cask dependencies", - }, - { - name: "--greedy", - description: - "Also include casks with auto_updates true or version :latest", - exclusiveOn: ["--greedy-latest", "--greedy-auto-updates"], - }, - { - name: "--greedy-latest", - description: "Also include casks with version :latest", - }, - { - name: "--greedy-auto-updates", - description: "Also include casks with auto_updates true", - }, - { - name: "--appdir", - description: - "Target location for Applications (default: /Applications)", - args: { - name: "location", - template: "folders", - }, - }, - { - name: "--colorpickerdir", - description: - "Target location for Color Pickers (default: ~/Library/ColorPickers)", - args: { - name: "location", - template: "folders", - }, - }, - { - name: "--prefpanedir", - description: - "Target location for Preference Panes (default: ~/Library/PreferencePanes)", - args: { - name: "location", - template: "folders", - }, - }, - { - name: "--qlplugindir", - description: - "Target location for QuickLook Plugins (default: ~/Library/QuickLook)", - args: { - name: "location", - template: "folders", - }, - }, - { - name: "--mdimporterdir", - description: - "Target location for Spotlight Plugins (default: ~/Library/Spotlight)", - args: { - name: "location", - template: "folders", - }, - }, - { - name: "--dictionarydir", - description: - "Target location for Dictionaries (default: ~/Library/Dictionaries)", - args: { - name: "location", - template: "folders", - }, - }, - { - name: "--fontdir", - description: "Target location for Fonts (default: ~/Library/Fonts)", - args: { - name: "location", - template: "folders", - }, - }, - { - name: "--servicedir", - description: - "Target location for Services (default: ~/Library/Services)", - args: { - name: "location", - template: "folders", - }, - }, - { - name: "--input-methoddir", - description: - "Target location for Input Methods (default: ~/Library/Input Methods)", - args: { - name: "location", - template: "folders", - }, - }, - { - name: "--internet-plugindir", - description: - "Target location for Internet Plugins (default: ~/Library/Internet Plug-Ins)", - args: { - name: "location", - template: "folders", - }, - }, - { - name: "--audio-unit-plugindir", - description: - "Target location for Audio Unit Plugins (default: ~/Library/Audio/Plug-Ins/Components)", - args: { - name: "location", - template: "folders", - }, - }, - { - name: "--vst-plugindir", - description: - "Target location for VST Plugins (default: ~/Library/Audio/Plug-Ins/VST)", - args: { - name: "location", - template: "folders", - }, - }, - { - name: "--vst3-plugindir", - description: - "Target location for VST3 Plugins (default: ~/Library/Audio/Plug-Ins/VST3)", - args: { - name: "location", - template: "folders", - }, - }, - { - name: "--screen-saverdir", - description: - "Target location for Screen Savers (default: ~/Library/Screen Savers)", - args: { - name: "location", - template: "folders", - }, - }, - { - name: "--language", - description: - "Comma-separated list of language codes to prefer for cask installation. The first matching language is used, otherwise it reverts to the cask's default language. The default value is the language of your system", - }, - ], - args: { - isVariadic: true, - isOptional: true, - name: "outdated_formula|outdated_cask", - generators: outdatedformulaeGenerator, - }, - }, - { - name: "search", - description: - "Perform a substring search of cask tokens and formula names", - options: [ - ...commonOptions, - { - name: "--formula", - description: "Search online and locally for formulae", - }, - { - name: "--cask", - description: "Search online and locally for casks", - }, - { - name: "--desc", - description: - "Search for formulae with a description matching text and casks with a name matching text", - }, - { - name: "--pull-request", - description: "Search for GitHub pull requests containing text", - }, - { - name: "--open", - description: "Search for only open GitHub pull requests", - }, - { - name: "--closed", - description: "Search for only closed GitHub pull requests", - }, - { - name: ["--repology", "--macports"], - description: "Search for text in the given database", - }, - { - name: ["--fink", "--opensuse"], - description: "Search for text in the given database", - }, - { - name: ["--fedora", "--debian"], - description: "Search for text in the given database", - }, - { - name: "--ubuntu", - description: "Search for text in the given database", - }, - ], - }, - { - name: "config", - description: "Show Homebrew and system configuration info", - }, - { - name: "postinstall", - description: "Rerun the post install step for formula", - options: [ - { - name: ["-d", "--debug"], - description: "Display any debugging information", - }, - { - name: ["-v", "--verbose"], - description: "Make some output more verbose", - }, - { - name: ["-q", "--quiet"], - description: "Make some output more quiet", - }, - { name: ["-h", "--help"], description: "Show this message" }, - ], - args: { - isVariadic: true, - name: "formula", - generators: formulaeGenerator, - }, - }, - { - name: "install", - description: "Install ", - options: [ - { - name: ["-f", "--force"], - description: - "Install formulae without checking for previously installed keg-only or non-migrated versions. When installing casks", - }, - { - name: ["-v", "--verbose"], - description: "Print the verification and postinstall steps", - }, - { - name: ["-s", "--build-from-source"], - description: - "Compile formula from source even if a bottle is provided. Dependencies will still be installed from bottles if they are available", - }, - { - name: ["-i", "--interactive"], - description: "Download and patch formula", - }, - { name: ["-g", "--git"], description: "Create a Git repository" }, - { - name: ["-q", "--quiet"], - description: "Make some output more quiet", - }, - { name: ["-h", "--help"], description: "Show this message" }, - { - name: "--formula", - description: "Treat all named arguments as formulae", - }, - { - name: "--env", - description: "Disabled other than for internal Homebrew use", - }, - { - name: "--ignore-dependencies", - description: - "An unsupported Homebrew development flag to skip installing any dependencies of any kind. If the dependencies are not already present, the formula will have issues. If you're not developing Homebrew, consider adjusting your PATH rather than using this flag", - }, - { - name: "--only-dependencies", - description: - "Install the dependencies with specified options but do not install the formula itself", - }, - { - name: "--cc", - description: - "Attempt to compile using the specified compiler, which should be the name of the compiler's executable", - args: { - name: "compiler", - suggestions: ["gcc-7", "llvm_clang", "clang"], - }, - }, - { - name: "--force-bottle", - description: - "Install from a bottle if it exists for the current or newest version of macOS, even if it would not normally be used for installation", - }, - { - name: "--include-test", - description: - "Install testing dependencies required to run brew test formula", - }, - { - name: "--HEAD", - description: - "If formula defines it, install the HEAD version, aka. main, trunk, unstable, master", - }, - { - name: "--fetch-HEAD", - description: - "Fetch the upstream repository to detect if the HEAD installation of the formula is outdated. Otherwise, the repository's HEAD will only be checked for updates when a new stable or development version has been released", - }, - { - name: "--keep-tmp", - description: "Retain the temporary files created during installation", - }, - { - name: "--build-bottle", - description: - "Prepare the formula for eventual bottling during installation, skipping any post-install steps", - }, - { - name: "--bottle-arch", - description: - "Optimise bottles for the specified architecture rather than the oldest architecture supported by the version of macOS the bottles are built on", - }, - { - name: "--display-times", - description: - "Print install times for each formula at the end of the run", - }, - { - name: "--cask", - description: "--casks Treat all named arguments as casks", - }, - { - name: "--binaries", - description: - "Disable/enable linking of helper executables (default: enabled)", - }, - { - name: "--no-binaries", - description: - "Disable/enable linking of helper executables (default: enabled)", - }, - { - name: "--require-sha", - description: "Require all casks to have a checksum", - }, - { - name: "--quarantine", - description: - "Disable/enable quarantining of downloads (default: enabled)", - }, - { - name: "--no-quarantine", - description: - "Disable/enable quarantining of downloads (default: enabled)", - }, - { - name: "--skip-cask-deps", - description: "Skip installing cask dependencies", - }, - { - name: "--appdir", - description: - "Target location for Applications (default: /Applications)", - args: { - name: "location", - template: "folders", - }, - }, - { - name: "--colorpickerdir", - description: - "Target location for Color Pickers (default: ~/Library/ColorPickers)", - args: { - name: "location", - template: "folders", - }, - }, - { - name: "--prefpanedir", - description: - "Target location for Preference Panes (default: ~/Library/PreferencePanes)", - args: { - name: "location", - template: "folders", - }, - }, - { - name: "--qlplugindir", - description: - "Target location for QuickLook Plugins (default: ~/Library/QuickLook)", - args: { - name: "location", - template: "folders", - }, - }, - { - name: "--mdimporterdir", - description: - "Target location for Spotlight Plugins (default: ~/Library/Spotlight)", - args: { - name: "location", - template: "folders", - }, - }, - { - name: "--dictionarydir", - description: - "Target location for Dictionaries (default: ~/Library/Dictionaries)", - args: { - name: "location", - template: "folders", - }, - }, - { - name: "--fontdir", - description: "Target location for Fonts (default: ~/Library/Fonts)", - args: { - name: "location", - template: "folders", - }, - }, - { - name: "--servicedir", - description: - "Target location for Services (default: ~/Library/Services)", - args: { - name: "location", - template: "folders", - }, - }, - { - name: "--input-methoddir", - description: - "Target location for Input Methods (default: ~/Library/Input Methods)", - args: { - name: "location", - template: "folders", - }, - }, - { - name: "--internet-plugindir", - description: - "Target location for Internet Plugins (default: ~/Library/Internet Plug-Ins)", - args: { - name: "location", - template: "folders", - }, - }, - { - name: "--audio-unit-plugindir", - description: - "Target location for Audio Unit Plugins (default: ~/Library/Audio/Plug-Ins/Components)", - args: { - name: "location", - template: "folders", - }, - }, - { - name: "--vst-plugindir", - description: - "Target location for VST Plugins (default: ~/Library/Audio/Plug-Ins/VST)", - args: { - name: "location", - template: "folders", - }, - }, - { - name: "--vst3-plugindir", - description: - "Target location for VST3 Plugins (default: ~/Library/Audio/Plug-Ins/VST3)", - args: { - name: "location", - template: "folders", - }, - }, - { - name: "--screen-saverdir", - description: - "Target location for Screen Savers (default: ~/Library/Screen Savers)", - args: { - name: "location", - template: "folders", - }, - }, - { - name: "--language", - description: - "Comma-separated list of language codes to prefer for cask installation. The first matching language is used, otherwise it reverts to the cask's default language. The default value is the language of your system", - }, - ], - args: { - isVariadic: true, - name: "formula", - description: "Formula or cask to install", - generators: [generateAllFormulae, generateAllCasks], - }, - }, - { - name: "reinstall", - description: - "Uninstall and then reinstall a formula or cask using the same options it was originally installed with, plus any appended options specific to a formula", - options: [ - { - name: ["-d", "--debug"], - description: - "If brewing fails, open an interactive debugging session with access to IRB or a shell inside the temporary build directory", - }, - { - name: ["-f", "--force"], - description: - "Install formulae without checking for previously installed keg-only or non-migrated versions. When installing casks", - }, - { - name: ["-v", "--verbose"], - description: "Print the verification and postinstall steps", - }, - { - name: ["-s", "--build-from-source"], - description: - "Compile formula from source even if a bottle is provided. Dependencies will still be installed from bottles if they are available", - }, - { - name: ["-i", "--interactive"], - description: "Download and patch formula", - }, - { name: ["-g", "--git"], description: "Create a Git repository" }, - { - name: "--formula", - description: "Treat all named arguments as formulae", - }, - { - name: "--force-bottle", - description: - "Install from a bottle if it exists for the current or newest version of macOS, even if it would not normally be used for installation", - }, - { - name: "--keep-tmp", - description: "Retain the temporary files created during installation", - }, - { - name: "--display-times", - description: - "Print install times for each formula at the end of the run", - }, - { - name: "--cask", - description: "--casks Treat all named arguments as casks", - }, - { - name: "--binaries", - description: - "Disable/enable linking of helper executables (default: enabled)", - exclusiveOn: ["--no-binaries"], - }, - { - name: "--no-binaries", - description: - "Disable/enable linking of helper executables (default: enabled)", - exclusiveOn: ["--binaries"], - }, - { - name: "--require-sha", - description: "Require all casks to have a checksum", - }, - { - name: "--quarantine", - description: - "Disable/enable quarantining of downloads (default: enabled)", - exclusiveOn: ["--no-quarantine"], - }, - { - name: "--no-quarantine", - description: - "Disable/enable quarantining of downloads (default: enabled)", - exclusiveOn: ["--quarantine"], - }, - { - name: "--skip-cask-deps", - description: "Skip installing cask dependencies", - }, - ], - args: { - isVariadic: true, - name: "formula", - generators: formulaeGenerator, - }, - }, - { - name: ["uninstall", "remove", "rm"], - description: "Uninstall a formula or cask", - args: { - isVariadic: true, - name: "formula", - generators: formulaeGenerator, - }, - }, - { - // NOTE: this is actually a command even if it has the double dash in the front - name: "--prefix", - description: "Prefix of ", - args: { - isVariadic: true, - name: "formula", - generators: formulaeGenerator, - }, - options: [ - { - name: "--unbrewed", - description: - "List files in Homebrew's prefix not installed by Homebrew", - }, - { - name: "--installed", - description: - "Outputs nothing and returns a failing status code if formula is not installed", - }, - ], - }, - { - name: "cask", - description: - "Homebrew Cask provides a friendly CLI workflow for the administration of macOS applications distributed as binaries", - subcommands: [ - { - name: "install", - description: "Installs the given cask", - args: { - name: "cask", - description: "Cask to install", - }, - }, - { - name: "uninstall", - description: "Uninstalls the given cask", - options: [ - ...commonOptions, - { - name: "--zap", - description: - "Remove all files associated with a cask. May remove files which are shared between applications", - }, - { - name: "--ignore-dependencies", - description: - "Don't fail uninstall, even if formula is a dependency of any installed formulae", - }, - { - name: "--formula", - description: "Treat all named arguments as formulae", - }, - { - name: "--cask", - description: "Treat all named arguments as casks", - }, - ], - args: { - isVariadic: true, + // if no formulas are specified, then we should provide system info + return ["cask-install", "os-version"].map((sugg) => ({ + name: sugg, + })); + }, + }, + }, + }, + { + name: "--github", + description: "Open the GitHub source page for formula in a browser", + }, + { + name: "--json", + description: "Print a JSON representation", + }, + { + name: "--installed", + exclusiveOn: ["--json"], + description: "Print JSON of formulae that are currently installed", + }, + { + name: "--all", + exclusiveOn: ["--json"], + description: "Print JSON of all available formulae", + }, + { + name: ["-v", "--verbose"], + description: "Show more verbose analytics data for formulae", + }, + { + name: "--formula", + description: "Treat all named arguments as formulae", + }, + { + name: "--cash", + description: "Treat all named arguments as casks", + }, + { + name: ["-d", "--debug"], + description: "Display any debugging information", + }, + { + name: ["-q", "--quiet"], + description: "List only the names of outdated kegs", + }, + { + name: ["-h", "--help"], + description: "Get help with services command", + }, + ], + }, + { + name: "update", + description: "Fetch the newest version of Homebrew and all formulae", + options: [ + { + name: ["-f", "--force"], + description: "Always do a slower, full update check", + }, + { + name: ["-v", "--verbose"], + description: + "Print the directories checked and git operations performed", + }, + { + name: ["-d", "--debug"], + description: + "Display a trace of all shell commands as they are executed", + }, + { name: ["-h", "--help"], description: "Show help message" }, + { + name: "--merge", + description: + "Use git merge to apply updates (rather than git rebase)", + }, + { + name: "--preinstall", + description: + "Run on auto-updates (e.g. before brew install). Skips some slower steps", + }, + ], + }, + { + name: "outdated", + description: + "List installed casks and formulae that have an updated version available", + options: [ + { + name: ["-d", "--debug"], + description: "Display any debugging information", + }, + { + name: ["-q", "--quiet"], + description: "List only the names of outdated kegs", + }, + { + name: ["-v", "--verbose"], + description: "Include detailed version information", + }, + { + name: ["-h", "--help"], + description: "Show help message for the outdated command", + }, + { name: "--cask", description: "List only outdated casks" }, + { + name: "--fetch-HEAD", + description: + "Fetch the upstream repository to detect if the HEAD installation of the formula is outdated", + }, + { name: "--formula", description: "List only outdated formulae" }, + { + name: "--greedy", + description: + "Print outdated casks with auto_updates or version :latest", + }, + { + name: "--greedy-latest", + description: + "Print outdated casks including those with version :latest", + }, + { + name: "--greedy-auto-updates", + description: + "Print outdated casks including those with auto_updates true", + }, + { name: "--json", description: "Print output in JSON format" }, + ], + }, + { + name: "pin", + description: "Pin formula, preventing them from being upgraded", + options: commonOptions, + args: { + isVariadic: true, + name: "formula", + generators: formulaeGenerator, + }, + }, + { + name: "unpin", + description: "Unpin formula, allowing them to be upgraded", + options: commonOptions, + args: { + isVariadic: true, + name: "formula", + generators: formulaeGenerator, + }, + }, + { + name: "upgrade", + description: + "Upgrade outdated casks and outdated, unpinned formulae using the same options they were originally installed with, plus any appended brew formula options", + options: [ + { + name: ["-d", "--debug"], + description: + "If brewing fails, open an interactive debugging session with access to IRB or a shell inside the temporary build directory", + }, + { + name: ["-f", "--force"], + description: + "Install formulae without checking for previously installed keg-only or non-migrated versions. When installing casks, overwrite existing files (binaries and symlinks are excluded, unless originally from the same cask)", + }, + { + name: ["-v", "--verbose"], + description: "Print the verification and postinstall steps", + }, + { + name: ["-n", "--dry-run"], + description: + "Show what would be upgraded, but do not actually upgrade anything", + }, + { + name: ["-s", "--build-from-source"], + description: + "Compile formula from source even if a bottle is provided. Dependencies will still be installed from bottles if they are available", + }, + { + name: ["-i", "--interactive"], + description: "Download and patch formula, then open a shell", + }, + { name: ["-g", "--git"], description: "Create a Git repository" }, + { + name: ["-q", "--quiet"], + description: "Make some output more quiet", + }, + { name: ["-h", "--help"], description: "Show this message" }, + { + name: ["--formula", "--formulae"], + description: + "Treat all named arguments as formulae. If no named arguments are specified, upgrade only outdated formulae", + }, + { + name: "--env", + description: "Disabled other than for internal Homebrew use", + }, + { + name: "--ignore-dependencies", + description: + "An unsupported Homebrew development flag to skip installing any dependencies of any kind. If the dependencies are not already present, the formula will have issues. If you're not developing Homebrew, consider adjusting your PATH rather than using this flag", + }, + { + name: "--only-dependencies", + description: + "Install the dependencies with specified options but do not install the formula itself", + }, + { + name: "--cc", + description: + "Attempt to compile using the specified compiler, which should be the name of the compiler's executable", + args: { + name: "compiler", + suggestions: ["gcc-7", "llvm_clang", "clang"], + }, + }, + { + name: "--force-bottle", + description: + "Install from a bottle if it exists for the current or newest version of macOS, even if it would not normally be used for installation", + }, + { + name: "--include-test", + description: + "Install testing dependencies required to run brew test formula", + }, + { + name: "--HEAD", + description: + "If formula defines it, install the HEAD version, aka. main, trunk, unstable, master", + }, + { + name: "--fetch-HEAD", + description: + "Fetch the upstream repository to detect if the HEAD installation of the formula is outdated. Otherwise, the repository's HEAD will only be checked for updates when a new stable or development version has been released", + }, + { + name: "--ignore-pinned", + description: + "Set a successful exit status even if pinned formulae are not upgraded", + }, + { + name: "--keep-tmp", + description: "Retain the temporary files created during installation", + }, + { + name: "--build-bottle", + description: + "Prepare the formula for eventual bottling during installation, skipping any post-install steps", + }, + { + name: "--bottle-arch", + description: + "Optimise bottles for the specified architecture rather than the oldest architecture supported by the version of macOS the bottles are built on", + }, + { + name: "--display-times", + description: + "Print install times for each formula at the end of the run", + }, + { + name: ["--cask", "--casks"], + description: + "Treat all named arguments as casks. If no named arguments are specified, upgrade only outdated casks", + }, + { + name: "--binaries", + description: + "Disable/enable linking of helper executables (default: enabled)", + exclusiveOn: ["--no-binaries"], + }, + { + name: "--no-binaries", + description: + "Disable/enable linking of helper executables (default: enabled)", + exclusiveOn: ["--binaries"], + }, + { + name: "--require-sha", + description: "Require all casks to have a checksum", + }, + { + name: "--quarantine", + description: + "Disable/enable quarantining of downloads (default: enabled)", + exclusiveOn: ["--no-quarantine"], + }, + { + name: "--no-quarantine", + description: + "Disable/enable quarantining of downloads (default: enabled)", + exclusiveOn: ["--quarantine"], + }, + { + name: "--skip-cask-deps", + description: "Skip installing cask dependencies", + }, + { + name: "--greedy", + description: + "Also include casks with auto_updates true or version :latest", + exclusiveOn: ["--greedy-latest", "--greedy-auto-updates"], + }, + { + name: "--greedy-latest", + description: "Also include casks with version :latest", + }, + { + name: "--greedy-auto-updates", + description: "Also include casks with auto_updates true", + }, + { + name: "--appdir", + description: + "Target location for Applications (default: /Applications)", + args: { + name: "location", + template: "folders", + }, + }, + { + name: "--colorpickerdir", + description: + "Target location for Color Pickers (default: ~/Library/ColorPickers)", + args: { + name: "location", + template: "folders", + }, + }, + { + name: "--prefpanedir", + description: + "Target location for Preference Panes (default: ~/Library/PreferencePanes)", + args: { + name: "location", + template: "folders", + }, + }, + { + name: "--qlplugindir", + description: + "Target location for QuickLook Plugins (default: ~/Library/QuickLook)", + args: { + name: "location", + template: "folders", + }, + }, + { + name: "--mdimporterdir", + description: + "Target location for Spotlight Plugins (default: ~/Library/Spotlight)", + args: { + name: "location", + template: "folders", + }, + }, + { + name: "--dictionarydir", + description: + "Target location for Dictionaries (default: ~/Library/Dictionaries)", + args: { + name: "location", + template: "folders", + }, + }, + { + name: "--fontdir", + description: "Target location for Fonts (default: ~/Library/Fonts)", + args: { + name: "location", + template: "folders", + }, + }, + { + name: "--servicedir", + description: + "Target location for Services (default: ~/Library/Services)", + args: { + name: "location", + template: "folders", + }, + }, + { + name: "--input-methoddir", + description: + "Target location for Input Methods (default: ~/Library/Input Methods)", + args: { + name: "location", + template: "folders", + }, + }, + { + name: "--internet-plugindir", + description: + "Target location for Internet Plugins (default: ~/Library/Internet Plug-Ins)", + args: { + name: "location", + template: "folders", + }, + }, + { + name: "--audio-unit-plugindir", + description: + "Target location for Audio Unit Plugins (default: ~/Library/Audio/Plug-Ins/Components)", + args: { + name: "location", + template: "folders", + }, + }, + { + name: "--vst-plugindir", + description: + "Target location for VST Plugins (default: ~/Library/Audio/Plug-Ins/VST)", + args: { + name: "location", + template: "folders", + }, + }, + { + name: "--vst3-plugindir", + description: + "Target location for VST3 Plugins (default: ~/Library/Audio/Plug-Ins/VST3)", + args: { + name: "location", + template: "folders", + }, + }, + { + name: "--screen-saverdir", + description: + "Target location for Screen Savers (default: ~/Library/Screen Savers)", + args: { + name: "location", + template: "folders", + }, + }, + { + name: "--language", + description: + "Comma-separated list of language codes to prefer for cask installation. The first matching language is used, otherwise it reverts to the cask's default language. The default value is the language of your system", + }, + ], + args: { + isVariadic: true, + isOptional: true, + name: "outdated_formula|outdated_cask", + generators: outdatedformulaeGenerator, + }, + }, + { + name: "search", + description: + "Perform a substring search of cask tokens and formula names", + options: [ + ...commonOptions, + { + name: "--formula", + description: "Search online and locally for formulae", + }, + { + name: "--cask", + description: "Search online and locally for casks", + }, + { + name: "--desc", + description: + "Search for formulae with a description matching text and casks with a name matching text", + }, + { + name: "--pull-request", + description: "Search for GitHub pull requests containing text", + }, + { + name: "--open", + description: "Search for only open GitHub pull requests", + }, + { + name: "--closed", + description: "Search for only closed GitHub pull requests", + }, + { + name: ["--repology", "--macports"], + description: "Search for text in the given database", + }, + { + name: ["--fink", "--opensuse"], + description: "Search for text in the given database", + }, + { + name: ["--fedora", "--debian"], + description: "Search for text in the given database", + }, + { + name: "--ubuntu", + description: "Search for text in the given database", + }, + ], + }, + { + name: "config", + description: "Show Homebrew and system configuration info", + }, + { + name: "postinstall", + description: "Rerun the post install step for formula", + options: [ + { + name: ["-d", "--debug"], + description: "Display any debugging information", + }, + { + name: ["-v", "--verbose"], + description: "Make some output more verbose", + }, + { + name: ["-q", "--quiet"], + description: "Make some output more quiet", + }, + { name: ["-h", "--help"], description: "Show this message" }, + ], + args: { + isVariadic: true, + name: "formula", + generators: formulaeGenerator, + }, + }, + { + name: "install", + description: "Install ", + options: [ + { + name: ["-f", "--force"], + description: + "Install formulae without checking for previously installed keg-only or non-migrated versions. When installing casks", + }, + { + name: ["-v", "--verbose"], + description: "Print the verification and postinstall steps", + }, + { + name: ["-s", "--build-from-source"], + description: + "Compile formula from source even if a bottle is provided. Dependencies will still be installed from bottles if they are available", + }, + { + name: ["-i", "--interactive"], + description: "Download and patch formula", + }, + { name: ["-g", "--git"], description: "Create a Git repository" }, + { + name: ["-q", "--quiet"], + description: "Make some output more quiet", + }, + { name: ["-h", "--help"], description: "Show this message" }, + { + name: "--formula", + description: "Treat all named arguments as formulae", + }, + { + name: "--env", + description: "Disabled other than for internal Homebrew use", + }, + { + name: "--ignore-dependencies", + description: + "An unsupported Homebrew development flag to skip installing any dependencies of any kind. If the dependencies are not already present, the formula will have issues. If you're not developing Homebrew, consider adjusting your PATH rather than using this flag", + }, + { + name: "--only-dependencies", + description: + "Install the dependencies with specified options but do not install the formula itself", + }, + { + name: "--cc", + description: + "Attempt to compile using the specified compiler, which should be the name of the compiler's executable", + args: { + name: "compiler", + suggestions: ["gcc-7", "llvm_clang", "clang"], + }, + }, + { + name: "--force-bottle", + description: + "Install from a bottle if it exists for the current or newest version of macOS, even if it would not normally be used for installation", + }, + { + name: "--include-test", + description: + "Install testing dependencies required to run brew test formula", + }, + { + name: "--HEAD", + description: + "If formula defines it, install the HEAD version, aka. main, trunk, unstable, master", + }, + { + name: "--fetch-HEAD", + description: + "Fetch the upstream repository to detect if the HEAD installation of the formula is outdated. Otherwise, the repository's HEAD will only be checked for updates when a new stable or development version has been released", + }, + { + name: "--keep-tmp", + description: "Retain the temporary files created during installation", + }, + { + name: "--build-bottle", + description: + "Prepare the formula for eventual bottling during installation, skipping any post-install steps", + }, + { + name: "--bottle-arch", + description: + "Optimise bottles for the specified architecture rather than the oldest architecture supported by the version of macOS the bottles are built on", + }, + { + name: "--display-times", + description: + "Print install times for each formula at the end of the run", + }, + { + name: "--cask", + description: "--casks Treat all named arguments as casks", + }, + { + name: "--binaries", + description: + "Disable/enable linking of helper executables (default: enabled)", + }, + { + name: "--no-binaries", + description: + "Disable/enable linking of helper executables (default: enabled)", + }, + { + name: "--require-sha", + description: "Require all casks to have a checksum", + }, + { + name: "--quarantine", + description: + "Disable/enable quarantining of downloads (default: enabled)", + }, + { + name: "--no-quarantine", + description: + "Disable/enable quarantining of downloads (default: enabled)", + }, + { + name: "--skip-cask-deps", + description: "Skip installing cask dependencies", + }, + { + name: "--appdir", + description: + "Target location for Applications (default: /Applications)", + args: { + name: "location", + template: "folders", + }, + }, + { + name: "--colorpickerdir", + description: + "Target location for Color Pickers (default: ~/Library/ColorPickers)", + args: { + name: "location", + template: "folders", + }, + }, + { + name: "--prefpanedir", + description: + "Target location for Preference Panes (default: ~/Library/PreferencePanes)", + args: { + name: "location", + template: "folders", + }, + }, + { + name: "--qlplugindir", + description: + "Target location for QuickLook Plugins (default: ~/Library/QuickLook)", + args: { + name: "location", + template: "folders", + }, + }, + { + name: "--mdimporterdir", + description: + "Target location for Spotlight Plugins (default: ~/Library/Spotlight)", + args: { + name: "location", + template: "folders", + }, + }, + { + name: "--dictionarydir", + description: + "Target location for Dictionaries (default: ~/Library/Dictionaries)", + args: { + name: "location", + template: "folders", + }, + }, + { + name: "--fontdir", + description: "Target location for Fonts (default: ~/Library/Fonts)", + args: { + name: "location", + template: "folders", + }, + }, + { + name: "--servicedir", + description: + "Target location for Services (default: ~/Library/Services)", + args: { + name: "location", + template: "folders", + }, + }, + { + name: "--input-methoddir", + description: + "Target location for Input Methods (default: ~/Library/Input Methods)", + args: { + name: "location", + template: "folders", + }, + }, + { + name: "--internet-plugindir", + description: + "Target location for Internet Plugins (default: ~/Library/Internet Plug-Ins)", + args: { + name: "location", + template: "folders", + }, + }, + { + name: "--audio-unit-plugindir", + description: + "Target location for Audio Unit Plugins (default: ~/Library/Audio/Plug-Ins/Components)", + args: { + name: "location", + template: "folders", + }, + }, + { + name: "--vst-plugindir", + description: + "Target location for VST Plugins (default: ~/Library/Audio/Plug-Ins/VST)", + args: { + name: "location", + template: "folders", + }, + }, + { + name: "--vst3-plugindir", + description: + "Target location for VST3 Plugins (default: ~/Library/Audio/Plug-Ins/VST3)", + args: { + name: "location", + template: "folders", + }, + }, + { + name: "--screen-saverdir", + description: + "Target location for Screen Savers (default: ~/Library/Screen Savers)", + args: { + name: "location", + template: "folders", + }, + }, + { + name: "--language", + description: + "Comma-separated list of language codes to prefer for cask installation. The first matching language is used, otherwise it reverts to the cask's default language. The default value is the language of your system", + }, + ], + args: { + isVariadic: true, + name: "formula", + description: "Formula or cask to install", + generators: [generateAllFormulae, generateAllCasks], + }, + }, + { + name: "reinstall", + description: + "Uninstall and then reinstall a formula or cask using the same options it was originally installed with, plus any appended options specific to a formula", + options: [ + { + name: ["-d", "--debug"], + description: + "If brewing fails, open an interactive debugging session with access to IRB or a shell inside the temporary build directory", + }, + { + name: ["-f", "--force"], + description: + "Install formulae without checking for previously installed keg-only or non-migrated versions. When installing casks", + }, + { + name: ["-v", "--verbose"], + description: "Print the verification and postinstall steps", + }, + { + name: ["-s", "--build-from-source"], + description: + "Compile formula from source even if a bottle is provided. Dependencies will still be installed from bottles if they are available", + }, + { + name: ["-i", "--interactive"], + description: "Download and patch formula", + }, + { name: ["-g", "--git"], description: "Create a Git repository" }, + { + name: "--formula", + description: "Treat all named arguments as formulae", + }, + { + name: "--force-bottle", + description: + "Install from a bottle if it exists for the current or newest version of macOS, even if it would not normally be used for installation", + }, + { + name: "--keep-tmp", + description: "Retain the temporary files created during installation", + }, + { + name: "--display-times", + description: + "Print install times for each formula at the end of the run", + }, + { + name: "--cask", + description: "--casks Treat all named arguments as casks", + }, + { + name: "--binaries", + description: + "Disable/enable linking of helper executables (default: enabled)", + exclusiveOn: ["--no-binaries"], + }, + { + name: "--no-binaries", + description: + "Disable/enable linking of helper executables (default: enabled)", + exclusiveOn: ["--binaries"], + }, + { + name: "--require-sha", + description: "Require all casks to have a checksum", + }, + { + name: "--quarantine", + description: + "Disable/enable quarantining of downloads (default: enabled)", + exclusiveOn: ["--no-quarantine"], + }, + { + name: "--no-quarantine", + description: + "Disable/enable quarantining of downloads (default: enabled)", + exclusiveOn: ["--quarantine"], + }, + { + name: "--skip-cask-deps", + description: "Skip installing cask dependencies", + }, + ], + args: { + isVariadic: true, + name: "formula", + generators: formulaeGenerator, + }, + }, + { + name: ["uninstall", "remove", "rm"], + description: "Uninstall a formula or cask", + args: { + isVariadic: true, + name: "formula", + generators: formulaeGenerator, + }, + }, + { + // NOTE: this is actually a command even if it has the double dash in the front + name: "--prefix", + description: "Prefix of ", + args: { + isVariadic: true, + name: "formula", + generators: formulaeGenerator, + }, + options: [ + { + name: "--unbrewed", + description: + "List files in Homebrew's prefix not installed by Homebrew", + }, + { + name: "--installed", + description: + "Outputs nothing and returns a failing status code if formula is not installed", + }, + ], + }, + { + name: "cask", + description: + "Homebrew Cask provides a friendly CLI workflow for the administration of macOS applications distributed as binaries", + subcommands: [ + { + name: "install", + description: "Installs the given cask", + args: { + name: "cask", + description: "Cask to install", + }, + }, + { + name: "uninstall", + description: "Uninstalls the given cask", + options: [ + ...commonOptions, + { + name: "--zap", + description: + "Remove all files associated with a cask. May remove files which are shared between applications", + }, + { + name: "--ignore-dependencies", + description: + "Don't fail uninstall, even if formula is a dependency of any installed formulae", + }, + { + name: "--formula", + description: "Treat all named arguments as formulae", + }, + { + name: "--cask", + description: "Treat all named arguments as casks", + }, + ], + args: { + isVariadic: true, - generators: { - script: ["brew", "list", "-1", "--cask"], - postProcess: function (out) { - return out.split("\n").map((formula) => { - return { - name: formula, - icon: "🍺", - description: "Installed formula", - }; - }); - }, - }, - }, - }, - ], - }, - { - name: "cleanup", - description: - "Remove stale lock files and outdated downloads for all formulae and casks and remove old versions of installed formulae", - options: [ - ...commonOptions, - { - name: ["--prune", "--prune=all"], - description: "Remove all cache files older than specified days", - }, - { - name: ["-n", "--dry-run"], - description: - "Show what would be removed, but do not actually remove anything", - }, - { - name: "-s", - description: - "Scrub the cache, including downloads for even the latest versions", - }, - { - name: "--prune-prefix", - description: - "Only prune the symlinks and directories from the prefix and remove no other files", - }, - ], - args: { - isVariadic: true, - isOptional: true, - generators: servicesGenerator("Cleanup"), - }, - }, - { - name: "services", - description: - "Manage background services with macOS' launchctl(1) daemon manager", - options: [ - ...commonOptions, - { - name: "--file", - description: - "Use the plist file from this location to start or run the service", - }, - { - name: "--all", - description: "Run subcommand on all services", - }, - { - name: ["-v", "--verbose"], - description: "Make some output more verbose", - }, - { - name: ["-h", "--help"], - description: "Get help with services command", - }, - ], - subcommands: [ - { - name: "cleanup", - description: "Remove all unused services", - }, - { - name: "list", - description: "List all services", - }, - { - name: "run", - description: - "Run the service formula without registering to launch at login (or boot)", - options: [ - { - name: "--all", - description: "Start all services", - }, - ], - args: { - isVariadic: true, - generators: servicesGenerator("Run"), - }, - }, - { - name: "start", - description: - "Start the service formula immediately and register it to launch at login", - options: [ - { - name: "--all", - description: "Start all services", - }, - ], - args: { - isVariadic: true, - generators: servicesGenerator("Start"), - }, - }, - { - name: "stop", - description: - "Stop the service formula immediately and unregister it from launching at", - options: [ - { - name: "--all", - description: "Start all services", - }, - ], - args: { - isVariadic: true, - generators: servicesGenerator("Stop"), - }, - }, - { - name: "restart", - description: - "Stop (if necessary) and start the service formula immediately and register it to launch at login (or boot)", - options: [ - { - name: "--all", - description: "Start all services", - }, - ], - args: { - isVariadic: true, - generators: servicesGenerator("Restart"), - }, - }, - ], - }, - { - name: "analytics", - description: "Manages analytics preferences", - subcommands: [ - { - name: "on", - description: "Turns on analytics", - }, - { - name: "off", - description: "Turns off analytics", - }, - { - name: "regenerate-uuid", - description: "Regenerate the UUID used for analytics", - }, - ], - }, - { - name: "autoremove", - description: - "Uninstall formulae that were only installed as a dependency of another formula and are now no longer needed", - options: [ - { - name: ["-n", "--dry-run"], - description: - "List what would be uninstalled, but do not actually uninstall anything", - }, - ], - }, - { - name: "tap", - description: "Tap a formula repository", - options: [ - ...commonOptions, - { - name: "--full", - description: - "Convert a shallow clone to a full clone without untapping", - }, - { - name: "--shallow", - description: "Fetch tap as a shallow clone rather than a full clone", - }, - { - name: "--force-auto-update", - description: "Auto-update tap even if it is not hosted on GitHub", - }, - { - name: "--repair", - description: - "Migrate tapped formulae from symlink-based to directory-based structure", - }, - { - name: "--list-pinned", - description: "List all pinned taps", - }, - ], - args: { - name: "user/repo or URL", - }, - }, - { - name: "untap", - description: "Remove a tapped formula repository", - args: { - name: "repository", - generators: repositoriesGenerator(), - }, - options: [ - { - name: ["-f", "--force"], - description: - "Untap even if formulae or casks from this tap are currently installed", - }, - { - name: ["-d", "--debug"], - description: "Display any debugging information", - }, - { - name: ["-q", "--quiet"], - description: "Make some output more quiet", - }, - { - name: ["-v", "--verbose"], - description: "Make some output more verbose", - }, - { - name: ["-h", "--help"], - description: "Show help message", - }, - ], - }, - { - name: "link", - description: - "Symlink all of formula's installed files into Homebrew's prefix", - args: { - isOptional: true, - isVariadic: true, - name: "formula", - generators: formulaeGenerator, - }, - options: [ - { - name: "--overwrite", - description: - "Delete files that already exist in the prefix while linking", - }, - { - name: ["-n", "--dry-run"], - description: - "List files which would be linked or deleted by brew link --overwrite without actually linking or deleting any files", - }, - { - name: ["-f", "--force"], - description: "Allow keg-only formulae to be linked", - }, - { - name: "--HEAD", - description: - "Link the HEAD version of the formula if it is installed", - }, - ], - }, - { - name: "unlink", - description: "Remove symlinks for formula from Homebrew's prefix", - args: { - isOptional: true, - isVariadic: true, - name: "formula", - generators: formulaeGenerator, - }, - options: [ - { - name: ["-n", "--dry-run"], - description: - "List files which would be unlinked without actually unlinking or deleting any files", - }, - ], - }, - { - name: "formulae", - description: "List all available formulae", - }, - { - name: "casks", - description: "List all available casks", - }, - { - name: "edit", - description: "", - args: { - isVariadic: true, - isOptional: true, - name: "formula", - description: "Formula or cask to install", - generators: [generateAllFormulae, generateAllCasks], - }, - options: [ - ...commonOptions, - { - name: ["--formula", "--formulae"], - description: "Treat all named arguments as formulae", - }, - { - name: ["--cask", "--casks"], - description: "Treat all named arguments as casks", - }, - ], - }, - { - name: ["home", "homepage"], - description: - "Open a formula, cask's homepage in a browser, or open Homebrew's own homepage if no argument is provided", - args: { - isVariadic: true, - isOptional: true, - name: "formula", - description: "Formula or cask to open homepage for", - generators: [generateAllFormulae, generateAllCasks], - }, - options: [ - ...commonOptions, - { - name: ["--formula", "--formulae"], - description: "Treat all named arguments as formulae", - }, - { - name: ["--cask", "--casks"], - description: "Treat all named arguments as casks", - }, - ], - }, - { - name: "alias", - description: "Manage custom user created brew aliases", - options: [ - { - name: "--edit", - description: "Edit aliases in a text editor", - }, - { - name: ["-d", "--debug"], - description: "Display any debugging information", - }, - { - name: ["-q", "--quiet"], - description: "Make some output more quiet", - }, - { - name: ["-v", "--verbose"], - description: "Make some output more verbose", - }, - { - name: ["-h", "--help"], - description: "Show help message", - }, - ], - args: { - name: "alias", - generators: generateAliases, - description: "Display the alias command", - isOptional: true, - }, - }, - { - name: "developer", - description: "Display the current state of Homebrew's developer mode", - args: { - name: "state", - description: "Turn Homebrew's developer mode on or off respectively", - suggestions: ["on", "off"], - isOptional: true, - }, - }, - ], - options: [ - { - name: "--version", - description: "The current Homebrew version", - }, - ], - args: { - name: "alias", - generators: generateAliases, - description: "Custom user defined brew alias", - isOptional: true, - }, + generators: { + script: ["brew", "list", "-1", "--cask"], + postProcess: function (out) { + return out.split("\n").map((formula) => { + return { + name: formula, + icon: "🍺", + description: "Installed formula", + }; + }); + }, + }, + }, + }, + ], + }, + { + name: "cleanup", + description: + "Remove stale lock files and outdated downloads for all formulae and casks and remove old versions of installed formulae", + options: [ + ...commonOptions, + { + name: ["--prune", "--prune=all"], + description: "Remove all cache files older than specified days", + }, + { + name: ["-n", "--dry-run"], + description: + "Show what would be removed, but do not actually remove anything", + }, + { + name: "-s", + description: + "Scrub the cache, including downloads for even the latest versions", + }, + { + name: "--prune-prefix", + description: + "Only prune the symlinks and directories from the prefix and remove no other files", + }, + ], + args: { + isVariadic: true, + isOptional: true, + generators: servicesGenerator("Cleanup"), + }, + }, + { + name: "services", + description: + "Manage background services with macOS' launchctl(1) daemon manager", + options: [ + ...commonOptions, + { + name: "--file", + description: + "Use the plist file from this location to start or run the service", + }, + { + name: "--all", + description: "Run subcommand on all services", + }, + { + name: ["-v", "--verbose"], + description: "Make some output more verbose", + }, + { + name: ["-h", "--help"], + description: "Get help with services command", + }, + ], + subcommands: [ + { + name: "cleanup", + description: "Remove all unused services", + }, + { + name: "list", + description: "List all services", + }, + { + name: "run", + description: + "Run the service formula without registering to launch at login (or boot)", + options: [ + { + name: "--all", + description: "Start all services", + }, + ], + args: { + isVariadic: true, + generators: servicesGenerator("Run"), + }, + }, + { + name: "start", + description: + "Start the service formula immediately and register it to launch at login", + options: [ + { + name: "--all", + description: "Start all services", + }, + ], + args: { + isVariadic: true, + generators: servicesGenerator("Start"), + }, + }, + { + name: "stop", + description: + "Stop the service formula immediately and unregister it from launching at", + options: [ + { + name: "--all", + description: "Start all services", + }, + ], + args: { + isVariadic: true, + generators: servicesGenerator("Stop"), + }, + }, + { + name: "restart", + description: + "Stop (if necessary) and start the service formula immediately and register it to launch at login (or boot)", + options: [ + { + name: "--all", + description: "Start all services", + }, + ], + args: { + isVariadic: true, + generators: servicesGenerator("Restart"), + }, + }, + ], + }, + { + name: "analytics", + description: "Manages analytics preferences", + subcommands: [ + { + name: "on", + description: "Turns on analytics", + }, + { + name: "off", + description: "Turns off analytics", + }, + { + name: "regenerate-uuid", + description: "Regenerate the UUID used for analytics", + }, + ], + }, + { + name: "autoremove", + description: + "Uninstall formulae that were only installed as a dependency of another formula and are now no longer needed", + options: [ + { + name: ["-n", "--dry-run"], + description: + "List what would be uninstalled, but do not actually uninstall anything", + }, + ], + }, + { + name: "tap", + description: "Tap a formula repository", + options: [ + ...commonOptions, + { + name: "--full", + description: + "Convert a shallow clone to a full clone without untapping", + }, + { + name: "--shallow", + description: "Fetch tap as a shallow clone rather than a full clone", + }, + { + name: "--force-auto-update", + description: "Auto-update tap even if it is not hosted on GitHub", + }, + { + name: "--repair", + description: + "Migrate tapped formulae from symlink-based to directory-based structure", + }, + { + name: "--list-pinned", + description: "List all pinned taps", + }, + ], + args: { + name: "user/repo or URL", + }, + }, + { + name: "untap", + description: "Remove a tapped formula repository", + args: { + name: "repository", + generators: repositoriesGenerator(), + }, + options: [ + { + name: ["-f", "--force"], + description: + "Untap even if formulae or casks from this tap are currently installed", + }, + { + name: ["-d", "--debug"], + description: "Display any debugging information", + }, + { + name: ["-q", "--quiet"], + description: "Make some output more quiet", + }, + { + name: ["-v", "--verbose"], + description: "Make some output more verbose", + }, + { + name: ["-h", "--help"], + description: "Show help message", + }, + ], + }, + { + name: "link", + description: + "Symlink all of formula's installed files into Homebrew's prefix", + args: { + isOptional: true, + isVariadic: true, + name: "formula", + generators: formulaeGenerator, + }, + options: [ + { + name: "--overwrite", + description: + "Delete files that already exist in the prefix while linking", + }, + { + name: ["-n", "--dry-run"], + description: + "List files which would be linked or deleted by brew link --overwrite without actually linking or deleting any files", + }, + { + name: ["-f", "--force"], + description: "Allow keg-only formulae to be linked", + }, + { + name: "--HEAD", + description: + "Link the HEAD version of the formula if it is installed", + }, + ], + }, + { + name: "unlink", + description: "Remove symlinks for formula from Homebrew's prefix", + args: { + isOptional: true, + isVariadic: true, + name: "formula", + generators: formulaeGenerator, + }, + options: [ + { + name: ["-n", "--dry-run"], + description: + "List files which would be unlinked without actually unlinking or deleting any files", + }, + ], + }, + { + name: "formulae", + description: "List all available formulae", + }, + { + name: "casks", + description: "List all available casks", + }, + { + name: "edit", + description: "", + args: { + isVariadic: true, + isOptional: true, + name: "formula", + description: "Formula or cask to install", + generators: [generateAllFormulae, generateAllCasks], + }, + options: [ + ...commonOptions, + { + name: ["--formula", "--formulae"], + description: "Treat all named arguments as formulae", + }, + { + name: ["--cask", "--casks"], + description: "Treat all named arguments as casks", + }, + ], + }, + { + name: ["home", "homepage"], + description: + "Open a formula, cask's homepage in a browser, or open Homebrew's own homepage if no argument is provided", + args: { + isVariadic: true, + isOptional: true, + name: "formula", + description: "Formula or cask to open homepage for", + generators: [generateAllFormulae, generateAllCasks], + }, + options: [ + ...commonOptions, + { + name: ["--formula", "--formulae"], + description: "Treat all named arguments as formulae", + }, + { + name: ["--cask", "--casks"], + description: "Treat all named arguments as casks", + }, + ], + }, + { + name: "alias", + description: "Manage custom user created brew aliases", + options: [ + { + name: "--edit", + description: "Edit aliases in a text editor", + }, + { + name: ["-d", "--debug"], + description: "Display any debugging information", + }, + { + name: ["-q", "--quiet"], + description: "Make some output more quiet", + }, + { + name: ["-v", "--verbose"], + description: "Make some output more verbose", + }, + { + name: ["-h", "--help"], + description: "Show help message", + }, + ], + args: { + name: "alias", + generators: generateAliases, + description: "Display the alias command", + isOptional: true, + }, + }, + { + name: "developer", + description: "Display the current state of Homebrew's developer mode", + args: { + name: "state", + description: "Turn Homebrew's developer mode on or off respectively", + suggestions: ["on", "off"], + isOptional: true, + }, + }, + ], + options: [ + { + name: "--version", + description: "The current Homebrew version", + }, + ], + args: { + name: "alias", + generators: generateAliases, + description: "Custom user defined brew alias", + isOptional: true, + }, }; export default completionSpec; diff --git a/extensions/terminal-suggest/src/completions/upstream/cat.ts b/extensions/terminal-suggest/src/completions/upstream/cat.ts index 4e06dacd8b47..b67e19b9c59d 100644 --- a/extensions/terminal-suggest/src/completions/upstream/cat.ts +++ b/extensions/terminal-suggest/src/completions/upstream/cat.ts @@ -1,50 +1,50 @@ const completionSpec: Fig.Spec = { - name: "cat", - description: "Concatenate and print files", - args: { - isVariadic: true, - template: "filepaths", - }, - options: [ - { - name: "-b", - description: "Number the non-blank output lines, starting at 1", - }, - - { - name: "-e", - description: - "Display non-printing characters (see the -v option), and display a dollar sign (‘$’) at the end of each line", - }, - - { - name: "-l", - description: - "Set an exclusive advisory lock on the standard output file descriptor. This lock is set using fcntl(2) with the F_SETLKW command. If the output file is already locked, cat will block until the lock is acquired", - }, - - { name: "-n", description: "Number the output lines, starting at 1" }, - - { - name: "-s", - description: - "Squeeze multiple adjacent empty lines, causing the output to be single spaced", - }, - - { - name: "-t", - description: - "Display non-printing characters (see the -v option), and display tab characters as ‘^I’", - }, - - { name: "-u", description: "Disable output buffering" }, - - { - name: "-v", - description: - "Display non-printing characters so they are visible. Control characters print as ‘^X’ for control-X; the delete character (octal 0177) prints as ‘^?’. Non-ASCII characters (with the high bit set) are printed as ‘M-’ (for meta) followed by the character for the low 7 bits", - }, - ], + name: "cat", + description: "Concatenate and print files", + args: { + isVariadic: true, + template: "filepaths", + }, + options: [ + { + name: "-b", + description: "Number the non-blank output lines, starting at 1", + }, + + { + name: "-e", + description: + "Display non-printing characters (see the -v option), and display a dollar sign (‘$’) at the end of each line", + }, + + { + name: "-l", + description: + "Set an exclusive advisory lock on the standard output file descriptor. This lock is set using fcntl(2) with the F_SETLKW command. If the output file is already locked, cat will block until the lock is acquired", + }, + + { name: "-n", description: "Number the output lines, starting at 1" }, + + { + name: "-s", + description: + "Squeeze multiple adjacent empty lines, causing the output to be single spaced", + }, + + { + name: "-t", + description: + "Display non-printing characters (see the -v option), and display tab characters as ‘^I’", + }, + + { name: "-u", description: "Disable output buffering" }, + + { + name: "-v", + description: + "Display non-printing characters so they are visible. Control characters print as ‘^X’ for control-X; the delete character (octal 0177) prints as ‘^?’. Non-ASCII characters (with the high bit set) are printed as ‘M-’ (for meta) followed by the character for the low 7 bits", + }, + ], }; export default completionSpec; diff --git a/extensions/terminal-suggest/src/completions/upstream/chmod.ts b/extensions/terminal-suggest/src/completions/upstream/chmod.ts index 93bc7264e8bb..c3b72a01a2fe 100644 --- a/extensions/terminal-suggest/src/completions/upstream/chmod.ts +++ b/extensions/terminal-suggest/src/completions/upstream/chmod.ts @@ -1,88 +1,88 @@ const completionSpec: Fig.Spec = { - name: "chmod", - description: "Change file modes or Access Control Lists", - args: [ - { - name: "mode", - suggestions: [ - // Some of the most common chmod's (non-exhaustive) - { - name: "u+x", - type: "arg", - description: "Give execute permission for the user", - icon: "🔐", - }, - { - name: "a+rx", - type: "arg", - description: "Adds read and execute permissions for all classes", - icon: "🔐", - }, - { - name: "744", - type: "arg", - description: - "Sets read, write, and execute permissions for user, and sets read permission for Group and Others", - icon: "🔐", - }, - { - name: "664", - type: "arg", - description: - "Sets read and write permissions for user and Group, and provides read to Others", - icon: "🔐", - }, - { - name: "777", - type: "arg", - description: "⚠️ allows all actions for all users", - icon: "🔐", - }, - ], - }, - { - // Modifying - template: "filepaths", - }, - ], + name: "chmod", + description: "Change file modes or Access Control Lists", + args: [ + { + name: "mode", + suggestions: [ + // Some of the most common chmod's (non-exhaustive) + { + name: "u+x", + type: "arg", + description: "Give execute permission for the user", + icon: "🔐", + }, + { + name: "a+rx", + type: "arg", + description: "Adds read and execute permissions for all classes", + icon: "🔐", + }, + { + name: "744", + type: "arg", + description: + "Sets read, write, and execute permissions for user, and sets read permission for Group and Others", + icon: "🔐", + }, + { + name: "664", + type: "arg", + description: + "Sets read and write permissions for user and Group, and provides read to Others", + icon: "🔐", + }, + { + name: "777", + type: "arg", + description: "⚠️ allows all actions for all users", + icon: "🔐", + }, + ], + }, + { + // Modifying + template: "filepaths", + }, + ], - options: [ - { - name: "-f", - description: - "Do not display a diagnostic message if chmod could not modify the mode for file, nor modify the exit status to reflect such failures", - }, - { - name: "-H", - description: - "If the -R option is specified, symbolic links on the command line are followed and hence unaffected by the command. (Symbolic links encountered during tree traversal are not followed.)", - }, - { - name: "-h", - description: - "If the file is a symbolic link, change the mode of the link itself rather than the file that the link points to", - }, - { - name: "-L", - description: - "If the -R option is specified, all symbolic links are followed", - }, - { - name: "-P", - description: - "If the -R option is specified, no symbolic links are followed. This is the default", - }, - { - name: "-R", - description: - "Change the modes of the file hierarchies rooted in the files, instead of just the files themselves. Beware of unintentionally matching the ``..'' hard link to the parent directory when using wildcards like ``.*''", - }, - { - name: "-v", - description: - "Cause chmod to be verbose, showing filenames as the mode is modified. If the -v flag is specified more than once, the old and new modes of the file will also be printed, in both octal and symbolic notation", - }, - ], + options: [ + { + name: "-f", + description: + "Do not display a diagnostic message if chmod could not modify the mode for file, nor modify the exit status to reflect such failures", + }, + { + name: "-H", + description: + "If the -R option is specified, symbolic links on the command line are followed and hence unaffected by the command. (Symbolic links encountered during tree traversal are not followed.)", + }, + { + name: "-h", + description: + "If the file is a symbolic link, change the mode of the link itself rather than the file that the link points to", + }, + { + name: "-L", + description: + "If the -R option is specified, all symbolic links are followed", + }, + { + name: "-P", + description: + "If the -R option is specified, no symbolic links are followed. This is the default", + }, + { + name: "-R", + description: + "Change the modes of the file hierarchies rooted in the files, instead of just the files themselves. Beware of unintentionally matching the ``..'' hard link to the parent directory when using wildcards like ``.*''", + }, + { + name: "-v", + description: + "Cause chmod to be verbose, showing filenames as the mode is modified. If the -v flag is specified more than once, the old and new modes of the file will also be printed, in both octal and symbolic notation", + }, + ], }; export default completionSpec; diff --git a/extensions/terminal-suggest/src/completions/upstream/chown.ts b/extensions/terminal-suggest/src/completions/upstream/chown.ts index c271be0bd401..3ee4f0f0142b 100644 --- a/extensions/terminal-suggest/src/completions/upstream/chown.ts +++ b/extensions/terminal-suggest/src/completions/upstream/chown.ts @@ -1,117 +1,117 @@ export const existingUsersandGroups: Fig.Generator = { - custom: async function (tokens, executeShellCommand) { - const colonAdded = tokens.find((token) => token.includes(":")); - const nFlagUsed = tokens.find((token) => /^-.*n.*/.test(token)); + custom: async function (tokens, executeShellCommand) { + const colonAdded = tokens.find((token) => token.includes(":")); + const nFlagUsed = tokens.find((token) => /^-.*n.*/.test(token)); - let shell: string; - // Using `:` as a trigger, check to see if a colon is added - // in the current command. If it is, get the system groups - // else retrieve the list of system users - if (colonAdded) { - const { stdout } = await executeShellCommand({ - command: "bash", - args: [ - "-c", - "dscl . -list /Groups PrimaryGroupID | tr -s ' '| sort -r", - ], - }); - shell = stdout; - } else { - const { stdout } = await executeShellCommand({ - command: "bash", - args: ["-c", "dscl . -list /Users UniqueID | tr -s ' '| sort -r"], - }); - shell = stdout; - } + let shell: string; + // Using `:` as a trigger, check to see if a colon is added + // in the current command. If it is, get the system groups + // else retrieve the list of system users + if (colonAdded) { + const { stdout } = await executeShellCommand({ + command: "bash", + args: [ + "-c", + "dscl . -list /Groups PrimaryGroupID | tr -s ' '| sort -r", + ], + }); + shell = stdout; + } else { + const { stdout } = await executeShellCommand({ + command: "bash", + args: ["-c", "dscl . -list /Users UniqueID | tr -s ' '| sort -r"], + }); + shell = stdout; + } - return ( - shell - .split("\n") - // The shell command retrieves a table - // with rows that look like `user uid` - // so each row is split again to get the - // user/group and uid/gid - .map((line) => line.split(" ")) - .map((value) => { - return { - // If the user has entered the option n - // suggest the uid/gid instead of user/group - name: nFlagUsed ? value[1] : value[0], - description: colonAdded - ? `Group - ${nFlagUsed ? value[0] : `gid: ${value[1]}`}` - : `User - ${nFlagUsed ? value[0] : `uid: ${value[1]}`}`, - icon: colonAdded ? "👥" : "👤", - priority: 90, - }; - }) - ); - }, - trigger: ":", - getQueryTerm: ":", + return ( + shell + .split("\n") + // The shell command retrieves a table + // with rows that look like `user uid` + // so each row is split again to get the + // user/group and uid/gid + .map((line) => line.split(" ")) + .map((value) => { + return { + // If the user has entered the option n + // suggest the uid/gid instead of user/group + name: nFlagUsed ? value[1] : value[0], + description: colonAdded + ? `Group - ${nFlagUsed ? value[0] : `gid: ${value[1]}`}` + : `User - ${nFlagUsed ? value[0] : `uid: ${value[1]}`}`, + icon: colonAdded ? "👥" : "👤", + priority: 90, + }; + }) + ); + }, + trigger: ":", + getQueryTerm: ":", }; const completionSpec: Fig.Spec = { - name: "chown", - description: - "Change the user and/or group ownership of a given file, directory, or symbolic link", - args: [ - { - name: "owner[:group] or :group", - generators: existingUsersandGroups, - }, - { - name: "file/directory", - isVariadic: true, - template: ["filepaths", "folders"], - }, - ], - options: [ - { - name: "-f", - description: - "Don't report any failure to change file owner or group, nor modify the exit status to reflect such failures", - }, - { - name: "-h", - description: - "If the file is a symbolic link, change the user ID and/or the group ID of the link itself", - }, - { - name: "-n", - description: - "Interpret user ID and group ID as numeric, avoiding name lookups", - }, - { - name: "-v", - description: - "Cause chown to be verbose, showing files as the owner is modified", - }, - { - name: "-R", - description: - "Change the user ID and/or the group ID for the file hierarchies rooted in the files instead of just the files themselves", - }, - { - name: "-H", - description: - "If the -R option is specified, symbolic links on the command line are followed", - exclusiveOn: ["-L", "-P"], - dependsOn: ["-R"], - }, - { - name: "-L", - description: - "If the -R option is specified, all symbolic links are followed", - exclusiveOn: ["-H", "-P"], - dependsOn: ["-R"], - }, - { - name: "-P", - description: - "If the -R option is specified, no symbolic links are followed", - exclusiveOn: ["-H", "-L"], - dependsOn: ["-R"], - }, - ], + name: "chown", + description: + "Change the user and/or group ownership of a given file, directory, or symbolic link", + args: [ + { + name: "owner[:group] or :group", + generators: existingUsersandGroups, + }, + { + name: "file/directory", + isVariadic: true, + template: ["filepaths", "folders"], + }, + ], + options: [ + { + name: "-f", + description: + "Don't report any failure to change file owner or group, nor modify the exit status to reflect such failures", + }, + { + name: "-h", + description: + "If the file is a symbolic link, change the user ID and/or the group ID of the link itself", + }, + { + name: "-n", + description: + "Interpret user ID and group ID as numeric, avoiding name lookups", + }, + { + name: "-v", + description: + "Cause chown to be verbose, showing files as the owner is modified", + }, + { + name: "-R", + description: + "Change the user ID and/or the group ID for the file hierarchies rooted in the files instead of just the files themselves", + }, + { + name: "-H", + description: + "If the -R option is specified, symbolic links on the command line are followed", + exclusiveOn: ["-L", "-P"], + dependsOn: ["-R"], + }, + { + name: "-L", + description: + "If the -R option is specified, all symbolic links are followed", + exclusiveOn: ["-H", "-P"], + dependsOn: ["-R"], + }, + { + name: "-P", + description: + "If the -R option is specified, no symbolic links are followed", + exclusiveOn: ["-H", "-L"], + dependsOn: ["-R"], + }, + ], }; export default completionSpec; diff --git a/extensions/terminal-suggest/src/completions/upstream/cp.ts b/extensions/terminal-suggest/src/completions/upstream/cp.ts index 65d3d065cfda..91a05f48e65f 100644 --- a/extensions/terminal-suggest/src/completions/upstream/cp.ts +++ b/extensions/terminal-suggest/src/completions/upstream/cp.ts @@ -1,79 +1,79 @@ const completionSpec: Fig.Spec = { - name: "cp", - description: "Copy files and directories", - args: [ - { - name: "source", - template: ["filepaths", "folders"], - isVariadic: true, - }, - { - name: "target", - template: ["filepaths", "folders"], - }, - ], - options: [ - { - name: "-a", - description: - "Preserves structure and attributes of files but not directory structure", - }, - { - name: "-f", - description: - "If the destination file cannot be opened, remove it and create a new file, without prompting for confirmation", - exclusiveOn: ["-n"], - }, - { - name: "-H", - description: - "If the -R option is specified, symbolic links on the command line are followed", - exclusiveOn: ["-L", "-P"], - dependsOn: ["-R"], - }, - { - name: "-i", - description: - "Cause cp to write a prompt to the standard error output before copying a file that would overwrite an existing file", - exclusiveOn: ["-n"], - }, - { - name: "-L", - description: - "If the -R option is specified, all symbolic links are followed", - exclusiveOn: ["-H", "-P"], - dependsOn: ["-R"], - }, - { - name: "-n", - description: "Do not overwrite an existing file", - exclusiveOn: ["-f", "-i"], - }, - { - name: "-P", - description: - "If the -R option is specified, no symbolic links are followed", - exclusiveOn: ["-H", "-L"], - dependsOn: ["-R"], - }, - { - name: "-R", - description: - "If source designates a directory, cp copies the directory and the entire subtree connected at that point. If source ends in a /, the contents of the directory are copied rather than the directory itself", - }, - { - name: "-v", - description: "Cause cp to be verbose, showing files as they are copied", - }, - { - name: "-X", - description: "Do not copy Extended Attributes (EAs) or resource forks", - }, - { - name: "-c", - description: "Copy files using clonefile", - }, - ], + name: "cp", + description: "Copy files and directories", + args: [ + { + name: "source", + template: ["filepaths", "folders"], + isVariadic: true, + }, + { + name: "target", + template: ["filepaths", "folders"], + }, + ], + options: [ + { + name: "-a", + description: + "Preserves structure and attributes of files but not directory structure", + }, + { + name: "-f", + description: + "If the destination file cannot be opened, remove it and create a new file, without prompting for confirmation", + exclusiveOn: ["-n"], + }, + { + name: "-H", + description: + "If the -R option is specified, symbolic links on the command line are followed", + exclusiveOn: ["-L", "-P"], + dependsOn: ["-R"], + }, + { + name: "-i", + description: + "Cause cp to write a prompt to the standard error output before copying a file that would overwrite an existing file", + exclusiveOn: ["-n"], + }, + { + name: "-L", + description: + "If the -R option is specified, all symbolic links are followed", + exclusiveOn: ["-H", "-P"], + dependsOn: ["-R"], + }, + { + name: "-n", + description: "Do not overwrite an existing file", + exclusiveOn: ["-f", "-i"], + }, + { + name: "-P", + description: + "If the -R option is specified, no symbolic links are followed", + exclusiveOn: ["-H", "-L"], + dependsOn: ["-R"], + }, + { + name: "-R", + description: + "If source designates a directory, cp copies the directory and the entire subtree connected at that point. If source ends in a /, the contents of the directory are copied rather than the directory itself", + }, + { + name: "-v", + description: "Cause cp to be verbose, showing files as they are copied", + }, + { + name: "-X", + description: "Do not copy Extended Attributes (EAs) or resource forks", + }, + { + name: "-c", + description: "Copy files using clonefile", + }, + ], }; export default completionSpec; diff --git a/extensions/terminal-suggest/src/completions/upstream/curl.ts b/extensions/terminal-suggest/src/completions/upstream/curl.ts index d47732b64472..eb6cfd319c4e 100644 --- a/extensions/terminal-suggest/src/completions/upstream/curl.ts +++ b/extensions/terminal-suggest/src/completions/upstream/curl.ts @@ -1,914 +1,914 @@ const completionSpec: Fig.Spec = { - name: "curl", - description: "Transfer a URL", - args: { name: "URL", template: "history" }, - options: [ - { - name: ["-a", "--append"], - description: "Append to target file when uploading", - }, - { - name: ["-E", "--cert"], - description: "Client certificate file and password", - args: { - name: "certificate[:password]", - generators: { - getQueryTerm: ":", - }, - }, - }, - { - name: ["-K", "--config"], - description: "Read config from a file", - args: { name: "file", template: "filepaths" }, - }, - { - name: ["-C", "--continue-at"], - description: "Resumed transfer offset", - args: { name: "offset" }, - }, - { - name: ["-b", "--cookie"], - description: "Send cookies from string/file", - args: { name: "data or filename", template: "filepaths" }, - }, - { - name: ["-c", "--cookie-jar"], - description: "Write cookies to after operation", - args: { name: "filename", template: "filepaths" }, - }, - { - name: ["-d", "--data"], - description: "HTTP POST data", - insertValue: "-d '{cursor}'", - args: { name: "data" }, - isRepeatable: true, - }, - { name: ["-q", "--disable"], description: "Disable .curlrc" }, - { - name: ["-D", "--dump-header"], - description: "Write the received headers to ", - args: { name: "filename", template: "filepaths" }, - }, - { - name: ["-f", "--fail"], - description: "Fail silently (no output at all) on HTTP errors", - }, - { - name: ["-F", "--form"], - description: "Specify multipart MIME data", - args: { name: "content" }, - isRepeatable: true, - }, - { - name: ["-P", "--ftp-port"], - description: "Use PORT instead of PASV", - args: { name: "address" }, - }, - { - name: ["-G", "--get"], - description: "Put the post data in the URL and use GET", - }, - { - name: ["-g", "--globoff"], - description: "Disable URL sequences and ranges using {} and []", - }, - { name: ["-I", "--head"], description: "Show document info only" }, - { - name: ["-H", "--header"], - description: "Pass custom header(s) to server", - args: { - name: "header/file", - suggestions: [ - { name: "Content-Type: application/json" }, - { name: "Content-Type: application/x-www-form-urlencoded" }, - ], - }, - }, - { name: ["-h", "--help"], description: "This help text" }, - { name: ["-0", "--http1.0"], description: "Use HTTP 1.0" }, - { - name: ["-i", "--include"], - description: "Include protocol response headers in the output", - }, - { - name: ["-k", "--insecure"], - description: "Allow insecure server connections when using SSL", - }, - { name: ["-4", "--ipv4"], description: "Resolve names to IPv4 addresses" }, - { name: ["-6", "--ipv6"], description: "Resolve names to IPv6 addresses" }, - { - name: ["-j", "--junk-session-cookies"], - description: "Ignore session cookies read from file", - }, - { name: ["-l", "--list-only"], description: "List only mode" }, - { name: ["-L", "--location"], description: "Follow redirects" }, - { name: ["-M", "--manual"], description: "Display the full manual" }, - { - name: ["-m", "--max-time"], - description: "Maximum time allowed for the transfer", - args: { name: "seconds" }, - }, - { - name: ["-n", "--netrc"], - description: "Must read .netrc for user name and password", - }, - { - name: ["-:", "--next"], - description: "Make next URL use its separate set of options", - }, - { - name: ["-N", "--no-buffer"], - description: "Disable buffering of the output stream", - }, - { - name: ["-o", "--output"], - description: "Write to file instead of stdout", - args: { name: "file", template: "filepaths" }, - }, - { - name: ["-#", "--progress-bar"], - description: "Display transfer progress as a bar", - }, - { - name: ["-x", "--proxy"], - description: "[protocol://]host[:port] Use this proxy", - }, - { - name: ["-U", "--proxy-user"], - description: "Proxy user and password", - args: { name: "user:password" }, - }, - { - name: ["-p", "--proxytunnel"], - description: "Operate through an HTTP proxy tunnel (using CONNECT)", - }, - { - name: ["-Q", "--quote"], - description: "Send command(s) to server before transfer", - }, - { - name: ["-r", "--range"], - description: "Retrieve only the bytes within RANGE", - args: { name: "range" }, - }, - { - name: ["-e", "--referer"], - description: "Referrer URL", - args: { name: "URL" }, - }, - { - name: ["-J", "--remote-header-name"], - description: "Use the header-provided filename", - }, - { - name: ["-O", "--remote-name"], - description: "Write output to a file named as the remote file", - }, - { - name: ["-R", "--remote-time"], - description: "Set the remote file's time on the local output", - }, - { - name: ["-X", "--request"], - description: "Specify request command to use", - args: { - name: "command", - suggestions: [ - { name: "GET" }, - { name: "HEAD" }, - { name: "POST" }, - { name: "PUT" }, - { name: "DELETE" }, - { name: "CONNECT" }, - { name: "OPTIONS" }, - { name: "TRACE" }, - { name: "PATCH" }, - ], - }, - }, - { - name: ["-S", "--show-error"], - description: "Show error even when -s is used", - }, - { name: ["-s", "--silent"], description: "Silent mode" }, - { - name: ["-Y", "--speed-limit"], - description: "Stop transfers slower than this", - args: { name: "speed" }, - }, - { - name: ["-y", "--speed-time"], - description: "Trigger 'speed-limit' abort after this time", - args: { name: "seconds" }, - }, - { name: ["-2", "--sslv2"], description: "Use SSLv2" }, - { name: ["-3", "--sslv3"], description: "Use SSLv3" }, - { - name: ["-t", "--telnet-option"], - description: "Set telnet option", - args: { name: "val" }, - }, - { - name: ["-z", "--time-cond"], - description: "Transfer based on a time condition", - args: { name: "time" }, - }, - { name: ["-1", "--tlsv1"], description: "Use TLSv1.0 or greater" }, - { - name: ["-T", "--upload-file"], - description: "Transfer local FILE to destination", - args: { name: "file", template: "filepaths" }, - }, - { name: ["-B", "--use-ascii"], description: "Use ASCII/text transfer" }, - { - name: ["-u", "--user"], - description: "Server user and password", - args: { name: "user:password" }, - }, - { - name: ["-A", "--user-agent"], - description: "Send User-Agent to server", - args: { name: "name" }, - }, - { - name: ["-v", "--verbose"], - description: "Make the operation more talkative", - }, - { name: ["-V", "--version"], description: "Show version number and quit" }, - { - name: ["-w", "--write-out"], - description: "Use output FORMAT after completion", - args: { name: "format" }, - }, - { - name: "--abstract-unix-socket", - description: "Connect via abstract Unix domain socket", - args: { name: "path" }, - }, - { - name: "--alt-svc", - description: "Name> Enable alt-svc with this cache file", - args: { name: "file", template: "filepaths" }, - }, - { name: "--anyauth", description: "Pick any authentication method" }, - { name: "--basic", description: "Use HTTP Basic Authentication" }, - { - name: "--cacert", - description: "CA certificate to verify peer against", - args: { name: "file", template: "filepaths" }, - }, - { - name: "--capath", - description: "CA directory to verify peer against", - args: { name: "dir", template: "folders" }, - }, - { - name: "--cert-status", - description: "Verify the status of the server certificate", - }, - { - name: "--cert-type", - description: "Certificate file type", - args: { - name: "type", - suggestions: [{ name: "DER" }, { name: "PEM" }, { name: "ENG" }], - }, - }, - { - name: "--ciphers", - description: "Of ciphers> SSL ciphers to use", - args: { name: "list" }, - }, - { name: "--compressed", description: "Request compressed response" }, - { name: "--compressed-ssh", description: "Enable SSH compression" }, - { - name: "--connect-timeout", - description: "Maximum time allowed for connection", - args: { name: "seconds" }, - }, - { - name: "--connect-to", - description: "Connect to host", - args: { name: "HOST1:PORT1:HOST2:PORT2" }, - }, - { - name: "--create-dirs", - description: "Create necessary local directory hierarchy", - }, - { name: "--crlf", description: "Convert LF to CRLF in upload" }, - { - name: "--crlfile", - description: "Get a CRL list in PEM format from the given file", - args: { name: "file", template: "filepaths" }, - }, - { - name: "--data-ascii", - description: "HTTP POST ASCII data", - args: { name: "data" }, - }, - { - name: "--data-binary", - description: "HTTP POST binary data", - args: { name: "data" }, - }, - { - name: "--data-raw", - description: "HTTP POST data, '@' allowed", - args: { name: "data" }, - }, - { - name: "--data-urlencode", - description: "HTTP POST data url encoded", - args: { name: "data" }, - }, - { - name: "--delegation", - description: "GSS-API delegation permission", - args: { name: "LEVEL" }, - }, - { name: "--digest", description: "Use HTTP Digest Authentication" }, - { name: "--disable-eprt", description: "Inhibit using EPRT or LPRT" }, - { name: "--disable-epsv", description: "Inhibit using EPSV" }, - { - name: "--disallow-username-in-url", - description: "Disallow username in url", - }, - { - name: "--dns-interface", - description: "Interface to use for DNS requests", - args: { name: "interface" }, - }, - { - name: "--dns-ipv4-addr", - description: "IPv4 address to use for DNS requests", - args: { name: "address" }, - }, - { - name: "--dns-ipv6-addr", - description: "IPv6 address to use for DNS requests", - args: { name: "address" }, - }, - { - name: "--dns-servers", - description: "DNS server addrs to use", - args: { name: "addresses" }, - }, - { - name: "--doh-url", - description: "Resolve host names over DOH", - args: { name: "URL" }, - }, - { - name: "--egd-file", - description: "EGD socket path for random data", - args: { name: "file", template: "filepaths" }, - }, - { - name: "--engine", - description: "Crypto engine to use", - args: { name: "name" }, - }, - { - name: "--etag-compare", - description: - "Make a conditional HTTP request for the ETag read from the given file", - args: { name: "file" }, - }, - { - name: "--etag-save", - description: "Save an HTTP ETag to the specified file", - args: { name: "file" }, - }, - { - name: "--expect100-timeout", - description: "How long to wait for 100-continue", - args: { name: "seconds" }, - }, - { - name: "--fail-early", - description: "Fail on first transfer error, do not continue", - }, - { - name: "--fail-with-body", - description: - "On HTTP errors, return an error and also output any HTML response", - }, - { name: "--false-start", description: "Enable TLS False Start" }, - { - name: "--form-string", - description: "Specify multipart MIME data", - args: { name: "string" }, - }, - { - name: "--ftp-account", - description: "Account data string", - args: { name: "data" }, - }, - { - name: "--ftp-alternative-to-user", - description: "String to replace USER [name]", - args: { name: "command" }, - }, - { - name: "--ftp-create-dirs", - description: "Create the remote dirs if not present", - }, - { - name: "--ftp-method", - description: "Control CWD usage", - args: { name: "method" }, - }, - { name: "--ftp-pasv", description: "Use PASV/EPSV instead of PORT" }, - { name: "--ftp-pret", description: "Send PRET before PASV" }, - { name: "--ftp-skip-pasv-ip", description: "Skip the IP address for PASV" }, - { name: "--ftp-ssl-ccc", description: "Send CCC after authenticating" }, - { - name: "--ftp-ssl-ccc-mode", - description: "Set CCC mode", - args: { - name: "mode", - suggestions: [{ name: "active" }, { name: "passive" }], - }, - }, - { - name: "--ftp-ssl-control", - description: "Require SSL/TLS for FTP login, clear for transfer", - }, - { - name: "--happy-eyeballs-timeout-ms", - description: - "How long to wait in milliseconds for IPv6 before trying IPv4", - args: { name: "milliseconds" }, - }, - { - name: "--haproxy-protocol", - description: "Send HAProxy PROXY protocol v1 header", - }, - { - name: "--hostpubmd5", - description: "Acceptable MD5 hash of the host public key", - args: { name: "md5" }, - }, - { name: "--http0.9", description: "Allow HTTP 0.9 responses" }, - { name: "--http1.1", description: "Use HTTP 1.1" }, - { name: "--http2", description: "Use HTTP 2" }, - { - name: "--http2-prior-knowledge", - description: "Use HTTP 2 without HTTP/1.1 Upgrade", - }, - { - name: "--ignore-content-length", - description: "Ignore the size of the remote resource", - }, - { - name: "--interface", - description: "Use network INTERFACE (or address)", - args: { name: "name" }, - }, - { - name: "--keepalive-time", - description: "Interval time for keepalive probes", - args: { name: "seconds" }, - }, - { - name: "--key", - description: "Private key file name", - args: { name: "key" }, - }, - { - name: "--key-type", - description: "Private key file type", - args: { - name: "type", - suggestions: [{ name: "DER" }, { name: "PEM" }, { name: "ENG" }], - }, - }, - { - name: "--krb", - description: "Enable Kerberos with security ", - args: { name: "level" }, - }, - { - name: "--libcurl", - description: "Dump libcurl equivalent code of this command line", - args: { name: "file", template: "filepaths" }, - }, - { - name: "--limit-rate", - description: "Limit transfer speed to RATE", - args: { name: "speed" }, - }, - { - name: "--local-port", - description: "Force use of RANGE for local port numbers", - args: { name: "num/range" }, - }, - { - name: "--location-trusted", - description: "Like --location, and send auth to other hosts", - }, - { - name: "--login-options", - description: "Server login options", - args: { name: "options" }, - }, - { - name: "--mail-auth", - description: "Originator address of the original email", - args: { name: "address" }, - }, - { - name: "--mail-from", - description: "Mail from this address", - args: { name: "address" }, - }, - { - name: "--mail-rcpt", - description: "Mail to this address", - args: { name: "address" }, - }, - { - name: "--max-filesize", - description: "Maximum file size to download", - args: { name: "bytes" }, - }, - { - name: "--max-redirs", - description: "Maximum number of redirects allowed", - args: { name: "num" }, - }, - { - name: "--metalink", - description: "Process given URLs as metalink XML file", - }, - { - name: "--negotiate", - description: "Use HTTP Negotiate (SPNEGO) authentication", - }, - { - name: "--netrc-file", - description: "Specify FILE for netrc", - args: { name: "filename", template: "filepaths" }, - }, - { name: "--netrc-optional", description: "Use either .netrc or URL" }, - { name: "--no-alpn", description: "Disable the ALPN TLS extension" }, - { - name: "--no-keepalive", - description: "Disable TCP keepalive on the connection", - }, - { name: "--no-npn", description: "Disable the NPN TLS extension" }, - { name: "--no-sessionid", description: "Disable SSL session-ID reusing" }, - { - name: "--noproxy", - description: "List of hosts which do not use proxy", - args: { name: "no-proxy-list" }, - }, - { name: "--ntlm", description: "Use HTTP NTLM authentication" }, - { - name: "--ntlm-wb", - description: "Use HTTP NTLM authentication with winbind", - }, - { - name: "--oauth2-bearer", - description: "OAuth 2 Bearer Token", - args: { name: "token" }, - }, - { - name: "--pass", - description: "Pass phrase for the private key", - args: { name: "phrase" }, - }, - { - name: "--path-as-is", - description: "Do not squash .. sequences in URL path", - }, - { - name: "--pinnedpubkey", - description: "FILE/HASHES Public key to verify peer against", - args: { name: "hashes" }, - }, - { - name: "--post301", - description: "Do not switch to GET after following a 301", - }, - { - name: "--post302", - description: "Do not switch to GET after following a 302", - }, - { - name: "--post303", - description: "Do not switch to GET after following a 303", - }, - { - name: "--preproxy", - description: "[protocol://]host[:port] Use this proxy first", - }, - { - name: "--proto", - description: "Enable/disable PROTOCOLS", - args: { name: "protocols" }, - }, - { - name: "--proto-default", - description: "Use PROTOCOL for any URL missing a scheme", - args: { name: "protocol" }, - }, - { - name: "--proto-redir", - description: "Enable/disable PROTOCOLS on redirect", - args: { name: "protocols" }, - }, - { - name: "--proxy-anyauth", - description: "Pick any proxy authentication method", - }, - { - name: "--proxy-basic", - description: "Use Basic authentication on the proxy", - }, - { - name: "--proxy-cacert", - description: "CA certificate to verify peer against for proxy", - args: { name: "file", template: "filepaths" }, - }, - { - name: "--proxy-capath", - description: "CA directory to verify peer against for proxy", - args: { name: "dir", template: "folders" }, - }, - { - name: "--proxy-cert", - description: "Set client certificate for proxy", - args: { name: "cert[:passwd]" }, - }, - { - name: "--proxy-cert-type", - description: "Client certificate type for HTTPS proxy", - args: { name: "type" }, - }, - { - name: "--proxy-ciphers", - description: "SSL ciphers to use for proxy", - args: { name: "list" }, - }, - { - name: "--proxy-crlfile", - description: "Set a CRL list for proxy", - args: { name: "file", template: "filepaths" }, - }, - { - name: "--proxy-digest", - description: "Use Digest authentication on the proxy", - }, - { - name: "--proxy-header", - description: "Pass custom header(s) to proxy", - args: { - name: "header/file", - suggestions: [ - { name: "Content-Type: application/json" }, - { name: "Content-Type: application/x-www-form-urlencoded" }, - ], - }, - }, - { - name: "--proxy-insecure", - description: "Do HTTPS proxy connections without verifying the proxy", - }, - { - name: "--proxy-key", - description: "Private key for HTTPS proxy", - args: { name: "key" }, - }, - { - name: "--proxy-key-type", - description: "Private key file type for proxy", - args: { name: "type" }, - }, - { - name: "--proxy-negotiate", - description: "Use HTTP Negotiate (SPNEGO) authentication on the proxy", - }, - { - name: "--proxy-ntlm", - description: "Use NTLM authentication on the proxy", - }, - { - name: "--proxy-pass", - description: "Pass phrase for the private key for HTTPS proxy", - args: { name: "phrase" }, - }, - { - name: "--proxy-pinnedpubkey", - description: "FILE/HASHES public key to verify proxy with", - args: { name: "hashes" }, - }, - { - name: "--proxy-service-name", - description: "SPNEGO proxy service name", - args: { name: "name" }, - }, - { - name: "--proxy-ssl-allow-beast", - description: "Allow security flaw for interop for HTTPS proxy", - }, - { - name: "--proxy-tls13-ciphers", - description: "List> TLS 1.3 proxy cipher suites", - args: { name: "ciphersuite" }, - }, - { - name: "--proxy-tlsauthtype", - description: "TLS authentication type for HTTPS proxy", - args: { name: "type" }, - }, - { - name: "--proxy-tlspassword", - description: "TLS password for HTTPS proxy", - args: { name: "string" }, - }, - { - name: "--proxy-tlsuser", - description: "TLS username for HTTPS proxy", - args: { name: "name" }, - }, - { name: "--proxy-tlsv1", description: "Use TLSv1 for HTTPS proxy" }, - { - name: "--proxy1.0", - description: "Use HTTP/1.0 proxy on given port", - args: { name: "host[:port]" }, - }, - { - name: "--pubkey", - description: "SSH Public key file name", - args: { name: "key", template: "filepaths" }, - }, - { - name: "--random-file", - description: "File for reading random data from", - args: { name: "file", template: "filepaths" }, - }, - { name: "--raw", description: 'Do HTTP "raw"; no transfer decoding' }, - { - name: "--remote-name-all", - description: "Use the remote file name for all URLs", - }, - { - name: "--request-target", - description: "Specify the target for this request", - }, - { - name: "--resolve", - description: "Resolve the host+port to this address", - args: { name: "host:port:address[,address]..." }, - }, - { - name: "--retry", - description: "Retry request if transient problems occur", - args: { name: "num" }, - }, - { - name: "--retry-connrefused", - description: "Retry on connection refused (use with --retry)", - }, - { - name: "--retry-delay", - description: "Wait time between retries", - args: { name: "seconds" }, - }, - { - name: "--retry-max-time", - description: "Retry only within this period", - args: { name: "seconds" }, - }, - { - name: "--sasl-ir", - description: "Enable initial response in SASL authentication", - }, - { - name: "--service-name", - description: "SPNEGO service name", - args: { name: "name" }, - }, - { - name: "--socks4", - description: "SOCKS4 proxy on given host + port", - args: { name: "host[:port]" }, - }, - { - name: "--socks4a", - description: "SOCKS4a proxy on given host + port", - args: { name: "host[:port]" }, - }, - { - name: "--socks5", - description: "SOCKS5 proxy on given host + port", - args: { name: "host[:port]" }, - }, - { - name: "--socks5-basic", - description: "Enable username/password auth for SOCKS5 proxies", - }, - { - name: "--socks5-gssapi", - description: "Enable GSS-API auth for SOCKS5 proxies", - }, - { - name: "--socks5-gssapi-nec", - description: "Compatibility with NEC SOCKS5 server", - }, - { - name: "--socks5-gssapi-service", - description: "SOCKS5 proxy service name for GSS-API", - args: { name: "name" }, - }, - { - name: "--socks5-hostname", - description: "SOCKS5 proxy, pass host name to proxy", - args: { name: "host[:port]" }, - }, - { name: "--ssl", description: "Try SSL/TLS" }, - { - name: "--ssl-auto-client-cert", - description: "Obtain and use a client certificate automatically", - }, - { - name: "--ssl-allow-beast", - description: "Allow security flaw to improve interop", - }, - { - name: "--ssl-no-revoke", - description: "Disable cert revocation checks (Schannel)", - }, - { name: "--ssl-reqd", description: "Require SSL/TLS" }, - { name: "--stderr", description: "Where to redirect stderr" }, - { - name: "--styled-output", - description: "Enable styled output for HTTP headers", - }, - { - name: "--suppress-connect-headers", - description: "Suppress proxy CONNECT response headers", - }, - { name: "--tcp-fastopen", description: "Use TCP Fast Open" }, - { name: "--tcp-nodelay", description: "Use the TCP_NODELAY option" }, - { - name: "--tftp-blksize", - description: "Set TFTP BLKSIZE option", - args: { name: "value" }, - }, - { name: "--tftp-no-options", description: "Do not send any TFTP options" }, - { - name: "--tls-max", - description: "Set maximum allowed TLS version", - args: { name: "VERSION" }, - }, - { - name: "--tls13-ciphers", - description: "Of TLS 1.3 ciphersuites> TLS 1.3 cipher suites to use", - args: { name: "list" }, - }, - { - name: "--tlsauthtype", - description: "TLS authentication type", - args: { name: "type" }, - }, - { name: "--tlspassword", description: "TLS password" }, - { name: "--tlsuser", description: "TLS user name", args: { name: "name" } }, - { name: "--tlsv1.0", description: "Use TLSv1.0 or greater" }, - { name: "--tlsv1.1", description: "Use TLSv1.1 or greater" }, - { name: "--tlsv1.2", description: "Use TLSv1.2 or greater" }, - { name: "--tlsv1.3", description: "Use TLSv1.3 or greater" }, - { - name: "--tr-encoding", - description: "Request compressed transfer encoding", - }, - { - name: "--trace", - description: "Write a debug trace to FILE", - args: { name: "file", template: "filepaths" }, - }, - { - name: "--trace-ascii", - description: "Like --trace, but without hex output", - args: { name: "file", template: "filepaths" }, - }, - { - name: "--trace-time", - description: "Add time stamps to trace/verbose output", - }, - { - name: "--unix-socket", - description: "Connect through this Unix domain socket", - args: { name: "path" }, - }, - { name: "--url", description: "URL to work with", args: { name: "url" } }, - { - name: "--xattr", - description: "Store metadata in extended file attributes", - }, - ], + name: "curl", + description: "Transfer a URL", + args: { name: "URL", template: "history" }, + options: [ + { + name: ["-a", "--append"], + description: "Append to target file when uploading", + }, + { + name: ["-E", "--cert"], + description: "Client certificate file and password", + args: { + name: "certificate[:password]", + generators: { + getQueryTerm: ":", + }, + }, + }, + { + name: ["-K", "--config"], + description: "Read config from a file", + args: { name: "file", template: "filepaths" }, + }, + { + name: ["-C", "--continue-at"], + description: "Resumed transfer offset", + args: { name: "offset" }, + }, + { + name: ["-b", "--cookie"], + description: "Send cookies from string/file", + args: { name: "data or filename", template: "filepaths" }, + }, + { + name: ["-c", "--cookie-jar"], + description: "Write cookies to after operation", + args: { name: "filename", template: "filepaths" }, + }, + { + name: ["-d", "--data"], + description: "HTTP POST data", + insertValue: "-d '{cursor}'", + args: { name: "data" }, + isRepeatable: true, + }, + { name: ["-q", "--disable"], description: "Disable .curlrc" }, + { + name: ["-D", "--dump-header"], + description: "Write the received headers to ", + args: { name: "filename", template: "filepaths" }, + }, + { + name: ["-f", "--fail"], + description: "Fail silently (no output at all) on HTTP errors", + }, + { + name: ["-F", "--form"], + description: "Specify multipart MIME data", + args: { name: "content" }, + isRepeatable: true, + }, + { + name: ["-P", "--ftp-port"], + description: "Use PORT instead of PASV", + args: { name: "address" }, + }, + { + name: ["-G", "--get"], + description: "Put the post data in the URL and use GET", + }, + { + name: ["-g", "--globoff"], + description: "Disable URL sequences and ranges using {} and []", + }, + { name: ["-I", "--head"], description: "Show document info only" }, + { + name: ["-H", "--header"], + description: "Pass custom header(s) to server", + args: { + name: "header/file", + suggestions: [ + { name: "Content-Type: application/json" }, + { name: "Content-Type: application/x-www-form-urlencoded" }, + ], + }, + }, + { name: ["-h", "--help"], description: "This help text" }, + { name: ["-0", "--http1.0"], description: "Use HTTP 1.0" }, + { + name: ["-i", "--include"], + description: "Include protocol response headers in the output", + }, + { + name: ["-k", "--insecure"], + description: "Allow insecure server connections when using SSL", + }, + { name: ["-4", "--ipv4"], description: "Resolve names to IPv4 addresses" }, + { name: ["-6", "--ipv6"], description: "Resolve names to IPv6 addresses" }, + { + name: ["-j", "--junk-session-cookies"], + description: "Ignore session cookies read from file", + }, + { name: ["-l", "--list-only"], description: "List only mode" }, + { name: ["-L", "--location"], description: "Follow redirects" }, + { name: ["-M", "--manual"], description: "Display the full manual" }, + { + name: ["-m", "--max-time"], + description: "Maximum time allowed for the transfer", + args: { name: "seconds" }, + }, + { + name: ["-n", "--netrc"], + description: "Must read .netrc for user name and password", + }, + { + name: ["-:", "--next"], + description: "Make next URL use its separate set of options", + }, + { + name: ["-N", "--no-buffer"], + description: "Disable buffering of the output stream", + }, + { + name: ["-o", "--output"], + description: "Write to file instead of stdout", + args: { name: "file", template: "filepaths" }, + }, + { + name: ["-#", "--progress-bar"], + description: "Display transfer progress as a bar", + }, + { + name: ["-x", "--proxy"], + description: "[protocol://]host[:port] Use this proxy", + }, + { + name: ["-U", "--proxy-user"], + description: "Proxy user and password", + args: { name: "user:password" }, + }, + { + name: ["-p", "--proxytunnel"], + description: "Operate through an HTTP proxy tunnel (using CONNECT)", + }, + { + name: ["-Q", "--quote"], + description: "Send command(s) to server before transfer", + }, + { + name: ["-r", "--range"], + description: "Retrieve only the bytes within RANGE", + args: { name: "range" }, + }, + { + name: ["-e", "--referer"], + description: "Referrer URL", + args: { name: "URL" }, + }, + { + name: ["-J", "--remote-header-name"], + description: "Use the header-provided filename", + }, + { + name: ["-O", "--remote-name"], + description: "Write output to a file named as the remote file", + }, + { + name: ["-R", "--remote-time"], + description: "Set the remote file's time on the local output", + }, + { + name: ["-X", "--request"], + description: "Specify request command to use", + args: { + name: "command", + suggestions: [ + { name: "GET" }, + { name: "HEAD" }, + { name: "POST" }, + { name: "PUT" }, + { name: "DELETE" }, + { name: "CONNECT" }, + { name: "OPTIONS" }, + { name: "TRACE" }, + { name: "PATCH" }, + ], + }, + }, + { + name: ["-S", "--show-error"], + description: "Show error even when -s is used", + }, + { name: ["-s", "--silent"], description: "Silent mode" }, + { + name: ["-Y", "--speed-limit"], + description: "Stop transfers slower than this", + args: { name: "speed" }, + }, + { + name: ["-y", "--speed-time"], + description: "Trigger 'speed-limit' abort after this time", + args: { name: "seconds" }, + }, + { name: ["-2", "--sslv2"], description: "Use SSLv2" }, + { name: ["-3", "--sslv3"], description: "Use SSLv3" }, + { + name: ["-t", "--telnet-option"], + description: "Set telnet option", + args: { name: "val" }, + }, + { + name: ["-z", "--time-cond"], + description: "Transfer based on a time condition", + args: { name: "time" }, + }, + { name: ["-1", "--tlsv1"], description: "Use TLSv1.0 or greater" }, + { + name: ["-T", "--upload-file"], + description: "Transfer local FILE to destination", + args: { name: "file", template: "filepaths" }, + }, + { name: ["-B", "--use-ascii"], description: "Use ASCII/text transfer" }, + { + name: ["-u", "--user"], + description: "Server user and password", + args: { name: "user:password" }, + }, + { + name: ["-A", "--user-agent"], + description: "Send User-Agent to server", + args: { name: "name" }, + }, + { + name: ["-v", "--verbose"], + description: "Make the operation more talkative", + }, + { name: ["-V", "--version"], description: "Show version number and quit" }, + { + name: ["-w", "--write-out"], + description: "Use output FORMAT after completion", + args: { name: "format" }, + }, + { + name: "--abstract-unix-socket", + description: "Connect via abstract Unix domain socket", + args: { name: "path" }, + }, + { + name: "--alt-svc", + description: "Name> Enable alt-svc with this cache file", + args: { name: "file", template: "filepaths" }, + }, + { name: "--anyauth", description: "Pick any authentication method" }, + { name: "--basic", description: "Use HTTP Basic Authentication" }, + { + name: "--cacert", + description: "CA certificate to verify peer against", + args: { name: "file", template: "filepaths" }, + }, + { + name: "--capath", + description: "CA directory to verify peer against", + args: { name: "dir", template: "folders" }, + }, + { + name: "--cert-status", + description: "Verify the status of the server certificate", + }, + { + name: "--cert-type", + description: "Certificate file type", + args: { + name: "type", + suggestions: [{ name: "DER" }, { name: "PEM" }, { name: "ENG" }], + }, + }, + { + name: "--ciphers", + description: "Of ciphers> SSL ciphers to use", + args: { name: "list" }, + }, + { name: "--compressed", description: "Request compressed response" }, + { name: "--compressed-ssh", description: "Enable SSH compression" }, + { + name: "--connect-timeout", + description: "Maximum time allowed for connection", + args: { name: "seconds" }, + }, + { + name: "--connect-to", + description: "Connect to host", + args: { name: "HOST1:PORT1:HOST2:PORT2" }, + }, + { + name: "--create-dirs", + description: "Create necessary local directory hierarchy", + }, + { name: "--crlf", description: "Convert LF to CRLF in upload" }, + { + name: "--crlfile", + description: "Get a CRL list in PEM format from the given file", + args: { name: "file", template: "filepaths" }, + }, + { + name: "--data-ascii", + description: "HTTP POST ASCII data", + args: { name: "data" }, + }, + { + name: "--data-binary", + description: "HTTP POST binary data", + args: { name: "data" }, + }, + { + name: "--data-raw", + description: "HTTP POST data, '@' allowed", + args: { name: "data" }, + }, + { + name: "--data-urlencode", + description: "HTTP POST data url encoded", + args: { name: "data" }, + }, + { + name: "--delegation", + description: "GSS-API delegation permission", + args: { name: "LEVEL" }, + }, + { name: "--digest", description: "Use HTTP Digest Authentication" }, + { name: "--disable-eprt", description: "Inhibit using EPRT or LPRT" }, + { name: "--disable-epsv", description: "Inhibit using EPSV" }, + { + name: "--disallow-username-in-url", + description: "Disallow username in url", + }, + { + name: "--dns-interface", + description: "Interface to use for DNS requests", + args: { name: "interface" }, + }, + { + name: "--dns-ipv4-addr", + description: "IPv4 address to use for DNS requests", + args: { name: "address" }, + }, + { + name: "--dns-ipv6-addr", + description: "IPv6 address to use for DNS requests", + args: { name: "address" }, + }, + { + name: "--dns-servers", + description: "DNS server addrs to use", + args: { name: "addresses" }, + }, + { + name: "--doh-url", + description: "Resolve host names over DOH", + args: { name: "URL" }, + }, + { + name: "--egd-file", + description: "EGD socket path for random data", + args: { name: "file", template: "filepaths" }, + }, + { + name: "--engine", + description: "Crypto engine to use", + args: { name: "name" }, + }, + { + name: "--etag-compare", + description: + "Make a conditional HTTP request for the ETag read from the given file", + args: { name: "file" }, + }, + { + name: "--etag-save", + description: "Save an HTTP ETag to the specified file", + args: { name: "file" }, + }, + { + name: "--expect100-timeout", + description: "How long to wait for 100-continue", + args: { name: "seconds" }, + }, + { + name: "--fail-early", + description: "Fail on first transfer error, do not continue", + }, + { + name: "--fail-with-body", + description: + "On HTTP errors, return an error and also output any HTML response", + }, + { name: "--false-start", description: "Enable TLS False Start" }, + { + name: "--form-string", + description: "Specify multipart MIME data", + args: { name: "string" }, + }, + { + name: "--ftp-account", + description: "Account data string", + args: { name: "data" }, + }, + { + name: "--ftp-alternative-to-user", + description: "String to replace USER [name]", + args: { name: "command" }, + }, + { + name: "--ftp-create-dirs", + description: "Create the remote dirs if not present", + }, + { + name: "--ftp-method", + description: "Control CWD usage", + args: { name: "method" }, + }, + { name: "--ftp-pasv", description: "Use PASV/EPSV instead of PORT" }, + { name: "--ftp-pret", description: "Send PRET before PASV" }, + { name: "--ftp-skip-pasv-ip", description: "Skip the IP address for PASV" }, + { name: "--ftp-ssl-ccc", description: "Send CCC after authenticating" }, + { + name: "--ftp-ssl-ccc-mode", + description: "Set CCC mode", + args: { + name: "mode", + suggestions: [{ name: "active" }, { name: "passive" }], + }, + }, + { + name: "--ftp-ssl-control", + description: "Require SSL/TLS for FTP login, clear for transfer", + }, + { + name: "--happy-eyeballs-timeout-ms", + description: + "How long to wait in milliseconds for IPv6 before trying IPv4", + args: { name: "milliseconds" }, + }, + { + name: "--haproxy-protocol", + description: "Send HAProxy PROXY protocol v1 header", + }, + { + name: "--hostpubmd5", + description: "Acceptable MD5 hash of the host public key", + args: { name: "md5" }, + }, + { name: "--http0.9", description: "Allow HTTP 0.9 responses" }, + { name: "--http1.1", description: "Use HTTP 1.1" }, + { name: "--http2", description: "Use HTTP 2" }, + { + name: "--http2-prior-knowledge", + description: "Use HTTP 2 without HTTP/1.1 Upgrade", + }, + { + name: "--ignore-content-length", + description: "Ignore the size of the remote resource", + }, + { + name: "--interface", + description: "Use network INTERFACE (or address)", + args: { name: "name" }, + }, + { + name: "--keepalive-time", + description: "Interval time for keepalive probes", + args: { name: "seconds" }, + }, + { + name: "--key", + description: "Private key file name", + args: { name: "key" }, + }, + { + name: "--key-type", + description: "Private key file type", + args: { + name: "type", + suggestions: [{ name: "DER" }, { name: "PEM" }, { name: "ENG" }], + }, + }, + { + name: "--krb", + description: "Enable Kerberos with security ", + args: { name: "level" }, + }, + { + name: "--libcurl", + description: "Dump libcurl equivalent code of this command line", + args: { name: "file", template: "filepaths" }, + }, + { + name: "--limit-rate", + description: "Limit transfer speed to RATE", + args: { name: "speed" }, + }, + { + name: "--local-port", + description: "Force use of RANGE for local port numbers", + args: { name: "num/range" }, + }, + { + name: "--location-trusted", + description: "Like --location, and send auth to other hosts", + }, + { + name: "--login-options", + description: "Server login options", + args: { name: "options" }, + }, + { + name: "--mail-auth", + description: "Originator address of the original email", + args: { name: "address" }, + }, + { + name: "--mail-from", + description: "Mail from this address", + args: { name: "address" }, + }, + { + name: "--mail-rcpt", + description: "Mail to this address", + args: { name: "address" }, + }, + { + name: "--max-filesize", + description: "Maximum file size to download", + args: { name: "bytes" }, + }, + { + name: "--max-redirs", + description: "Maximum number of redirects allowed", + args: { name: "num" }, + }, + { + name: "--metalink", + description: "Process given URLs as metalink XML file", + }, + { + name: "--negotiate", + description: "Use HTTP Negotiate (SPNEGO) authentication", + }, + { + name: "--netrc-file", + description: "Specify FILE for netrc", + args: { name: "filename", template: "filepaths" }, + }, + { name: "--netrc-optional", description: "Use either .netrc or URL" }, + { name: "--no-alpn", description: "Disable the ALPN TLS extension" }, + { + name: "--no-keepalive", + description: "Disable TCP keepalive on the connection", + }, + { name: "--no-npn", description: "Disable the NPN TLS extension" }, + { name: "--no-sessionid", description: "Disable SSL session-ID reusing" }, + { + name: "--noproxy", + description: "List of hosts which do not use proxy", + args: { name: "no-proxy-list" }, + }, + { name: "--ntlm", description: "Use HTTP NTLM authentication" }, + { + name: "--ntlm-wb", + description: "Use HTTP NTLM authentication with winbind", + }, + { + name: "--oauth2-bearer", + description: "OAuth 2 Bearer Token", + args: { name: "token" }, + }, + { + name: "--pass", + description: "Pass phrase for the private key", + args: { name: "phrase" }, + }, + { + name: "--path-as-is", + description: "Do not squash .. sequences in URL path", + }, + { + name: "--pinnedpubkey", + description: "FILE/HASHES Public key to verify peer against", + args: { name: "hashes" }, + }, + { + name: "--post301", + description: "Do not switch to GET after following a 301", + }, + { + name: "--post302", + description: "Do not switch to GET after following a 302", + }, + { + name: "--post303", + description: "Do not switch to GET after following a 303", + }, + { + name: "--preproxy", + description: "[protocol://]host[:port] Use this proxy first", + }, + { + name: "--proto", + description: "Enable/disable PROTOCOLS", + args: { name: "protocols" }, + }, + { + name: "--proto-default", + description: "Use PROTOCOL for any URL missing a scheme", + args: { name: "protocol" }, + }, + { + name: "--proto-redir", + description: "Enable/disable PROTOCOLS on redirect", + args: { name: "protocols" }, + }, + { + name: "--proxy-anyauth", + description: "Pick any proxy authentication method", + }, + { + name: "--proxy-basic", + description: "Use Basic authentication on the proxy", + }, + { + name: "--proxy-cacert", + description: "CA certificate to verify peer against for proxy", + args: { name: "file", template: "filepaths" }, + }, + { + name: "--proxy-capath", + description: "CA directory to verify peer against for proxy", + args: { name: "dir", template: "folders" }, + }, + { + name: "--proxy-cert", + description: "Set client certificate for proxy", + args: { name: "cert[:passwd]" }, + }, + { + name: "--proxy-cert-type", + description: "Client certificate type for HTTPS proxy", + args: { name: "type" }, + }, + { + name: "--proxy-ciphers", + description: "SSL ciphers to use for proxy", + args: { name: "list" }, + }, + { + name: "--proxy-crlfile", + description: "Set a CRL list for proxy", + args: { name: "file", template: "filepaths" }, + }, + { + name: "--proxy-digest", + description: "Use Digest authentication on the proxy", + }, + { + name: "--proxy-header", + description: "Pass custom header(s) to proxy", + args: { + name: "header/file", + suggestions: [ + { name: "Content-Type: application/json" }, + { name: "Content-Type: application/x-www-form-urlencoded" }, + ], + }, + }, + { + name: "--proxy-insecure", + description: "Do HTTPS proxy connections without verifying the proxy", + }, + { + name: "--proxy-key", + description: "Private key for HTTPS proxy", + args: { name: "key" }, + }, + { + name: "--proxy-key-type", + description: "Private key file type for proxy", + args: { name: "type" }, + }, + { + name: "--proxy-negotiate", + description: "Use HTTP Negotiate (SPNEGO) authentication on the proxy", + }, + { + name: "--proxy-ntlm", + description: "Use NTLM authentication on the proxy", + }, + { + name: "--proxy-pass", + description: "Pass phrase for the private key for HTTPS proxy", + args: { name: "phrase" }, + }, + { + name: "--proxy-pinnedpubkey", + description: "FILE/HASHES public key to verify proxy with", + args: { name: "hashes" }, + }, + { + name: "--proxy-service-name", + description: "SPNEGO proxy service name", + args: { name: "name" }, + }, + { + name: "--proxy-ssl-allow-beast", + description: "Allow security flaw for interop for HTTPS proxy", + }, + { + name: "--proxy-tls13-ciphers", + description: "List> TLS 1.3 proxy cipher suites", + args: { name: "ciphersuite" }, + }, + { + name: "--proxy-tlsauthtype", + description: "TLS authentication type for HTTPS proxy", + args: { name: "type" }, + }, + { + name: "--proxy-tlspassword", + description: "TLS password for HTTPS proxy", + args: { name: "string" }, + }, + { + name: "--proxy-tlsuser", + description: "TLS username for HTTPS proxy", + args: { name: "name" }, + }, + { name: "--proxy-tlsv1", description: "Use TLSv1 for HTTPS proxy" }, + { + name: "--proxy1.0", + description: "Use HTTP/1.0 proxy on given port", + args: { name: "host[:port]" }, + }, + { + name: "--pubkey", + description: "SSH Public key file name", + args: { name: "key", template: "filepaths" }, + }, + { + name: "--random-file", + description: "File for reading random data from", + args: { name: "file", template: "filepaths" }, + }, + { name: "--raw", description: 'Do HTTP "raw"; no transfer decoding' }, + { + name: "--remote-name-all", + description: "Use the remote file name for all URLs", + }, + { + name: "--request-target", + description: "Specify the target for this request", + }, + { + name: "--resolve", + description: "Resolve the host+port to this address", + args: { name: "host:port:address[,address]..." }, + }, + { + name: "--retry", + description: "Retry request if transient problems occur", + args: { name: "num" }, + }, + { + name: "--retry-connrefused", + description: "Retry on connection refused (use with --retry)", + }, + { + name: "--retry-delay", + description: "Wait time between retries", + args: { name: "seconds" }, + }, + { + name: "--retry-max-time", + description: "Retry only within this period", + args: { name: "seconds" }, + }, + { + name: "--sasl-ir", + description: "Enable initial response in SASL authentication", + }, + { + name: "--service-name", + description: "SPNEGO service name", + args: { name: "name" }, + }, + { + name: "--socks4", + description: "SOCKS4 proxy on given host + port", + args: { name: "host[:port]" }, + }, + { + name: "--socks4a", + description: "SOCKS4a proxy on given host + port", + args: { name: "host[:port]" }, + }, + { + name: "--socks5", + description: "SOCKS5 proxy on given host + port", + args: { name: "host[:port]" }, + }, + { + name: "--socks5-basic", + description: "Enable username/password auth for SOCKS5 proxies", + }, + { + name: "--socks5-gssapi", + description: "Enable GSS-API auth for SOCKS5 proxies", + }, + { + name: "--socks5-gssapi-nec", + description: "Compatibility with NEC SOCKS5 server", + }, + { + name: "--socks5-gssapi-service", + description: "SOCKS5 proxy service name for GSS-API", + args: { name: "name" }, + }, + { + name: "--socks5-hostname", + description: "SOCKS5 proxy, pass host name to proxy", + args: { name: "host[:port]" }, + }, + { name: "--ssl", description: "Try SSL/TLS" }, + { + name: "--ssl-auto-client-cert", + description: "Obtain and use a client certificate automatically", + }, + { + name: "--ssl-allow-beast", + description: "Allow security flaw to improve interop", + }, + { + name: "--ssl-no-revoke", + description: "Disable cert revocation checks (Schannel)", + }, + { name: "--ssl-reqd", description: "Require SSL/TLS" }, + { name: "--stderr", description: "Where to redirect stderr" }, + { + name: "--styled-output", + description: "Enable styled output for HTTP headers", + }, + { + name: "--suppress-connect-headers", + description: "Suppress proxy CONNECT response headers", + }, + { name: "--tcp-fastopen", description: "Use TCP Fast Open" }, + { name: "--tcp-nodelay", description: "Use the TCP_NODELAY option" }, + { + name: "--tftp-blksize", + description: "Set TFTP BLKSIZE option", + args: { name: "value" }, + }, + { name: "--tftp-no-options", description: "Do not send any TFTP options" }, + { + name: "--tls-max", + description: "Set maximum allowed TLS version", + args: { name: "VERSION" }, + }, + { + name: "--tls13-ciphers", + description: "Of TLS 1.3 ciphersuites> TLS 1.3 cipher suites to use", + args: { name: "list" }, + }, + { + name: "--tlsauthtype", + description: "TLS authentication type", + args: { name: "type" }, + }, + { name: "--tlspassword", description: "TLS password" }, + { name: "--tlsuser", description: "TLS user name", args: { name: "name" } }, + { name: "--tlsv1.0", description: "Use TLSv1.0 or greater" }, + { name: "--tlsv1.1", description: "Use TLSv1.1 or greater" }, + { name: "--tlsv1.2", description: "Use TLSv1.2 or greater" }, + { name: "--tlsv1.3", description: "Use TLSv1.3 or greater" }, + { + name: "--tr-encoding", + description: "Request compressed transfer encoding", + }, + { + name: "--trace", + description: "Write a debug trace to FILE", + args: { name: "file", template: "filepaths" }, + }, + { + name: "--trace-ascii", + description: "Like --trace, but without hex output", + args: { name: "file", template: "filepaths" }, + }, + { + name: "--trace-time", + description: "Add time stamps to trace/verbose output", + }, + { + name: "--unix-socket", + description: "Connect through this Unix domain socket", + args: { name: "path" }, + }, + { name: "--url", description: "URL to work with", args: { name: "url" } }, + { + name: "--xattr", + description: "Store metadata in extended file attributes", + }, + ], }; export default completionSpec; diff --git a/extensions/terminal-suggest/src/completions/upstream/df.ts b/extensions/terminal-suggest/src/completions/upstream/df.ts index e7e005464ea9..42f577d89e77 100644 --- a/extensions/terminal-suggest/src/completions/upstream/df.ts +++ b/extensions/terminal-suggest/src/completions/upstream/df.ts @@ -1,65 +1,65 @@ const completionSpec: Fig.Spec = { - name: "df", - description: "Display free disk space", - args: { - name: "file or filesystem", - }, - options: [ - { - name: "-a", - description: "Show all mount points", - }, - { - name: ["-b", "-P"], - description: "Use 512-byte blocks (default)", - exclusiveOn: ["-g", "-k", "-m"], - }, - { - name: "-g", - description: "Use 1073741824-byte (1-Gbyte) blocks", - exclusiveOn: ["-b", "-P", "-m", "-k"], - }, - { - name: "-m", - description: "Use 1048576-byte (1-Mbyte) blocks", - exclusiveOn: ["-b", "-P", "-g", "-k"], - }, - { - name: "-k", - description: "Use 1024-byte (1-Kbyte) blocks", - exclusiveOn: ["-b", "-P", "-g", "-m"], - }, - { - name: "-H", - description: '"Human-readable" output, uses base 10 unit suffixes', - exclusiveOn: ["-h"], - }, - { - name: "-h", - description: '"Human-readable" output, uses base 2 unit suffixes', - exclusiveOn: ["-H"], - }, - { - name: "-i", - description: "Include the number of free inodes", - }, - { - name: "-l", - description: "Only display information about locally-mounted filesystems", - }, - { - name: "-n", - description: "Print out the previously obtained statistics", - }, - { - name: "-T", - description: - "Only print out statistics for filesystems of the specified types (comma separated)", - args: { - name: "filesystem", - }, - }, - ], + name: "df", + description: "Display free disk space", + args: { + name: "file or filesystem", + }, + options: [ + { + name: "-a", + description: "Show all mount points", + }, + { + name: ["-b", "-P"], + description: "Use 512-byte blocks (default)", + exclusiveOn: ["-g", "-k", "-m"], + }, + { + name: "-g", + description: "Use 1073741824-byte (1-Gbyte) blocks", + exclusiveOn: ["-b", "-P", "-m", "-k"], + }, + { + name: "-m", + description: "Use 1048576-byte (1-Mbyte) blocks", + exclusiveOn: ["-b", "-P", "-g", "-k"], + }, + { + name: "-k", + description: "Use 1024-byte (1-Kbyte) blocks", + exclusiveOn: ["-b", "-P", "-g", "-m"], + }, + { + name: "-H", + description: '"Human-readable" output, uses base 10 unit suffixes', + exclusiveOn: ["-h"], + }, + { + name: "-h", + description: '"Human-readable" output, uses base 2 unit suffixes', + exclusiveOn: ["-H"], + }, + { + name: "-i", + description: "Include the number of free inodes", + }, + { + name: "-l", + description: "Only display information about locally-mounted filesystems", + }, + { + name: "-n", + description: "Print out the previously obtained statistics", + }, + { + name: "-T", + description: + "Only print out statistics for filesystems of the specified types (comma separated)", + args: { + name: "filesystem", + }, + }, + ], }; export default completionSpec; diff --git a/extensions/terminal-suggest/src/completions/upstream/du.ts b/extensions/terminal-suggest/src/completions/upstream/du.ts index 833fa52630f7..ff3d6e421d2e 100644 --- a/extensions/terminal-suggest/src/completions/upstream/du.ts +++ b/extensions/terminal-suggest/src/completions/upstream/du.ts @@ -1,93 +1,93 @@ const completionSpec: Fig.Spec = { - name: "du", - description: "Display disk usage statistics", - options: [ - { - name: "-a", - description: "Display an entry for each file in a file hierarchy", - exclusiveOn: ["-s", "-d"], - }, - { - name: "-c", - description: "Display a grand total", - }, - { - name: "-H", - description: - "Symbolic links on the command line are followed, symbolic links in file hierarchies are not followed", - exclusiveOn: ["-L", "-P"], - }, - { - name: "-h", - description: - '"Human-readable" output. Use unit suffixes: Byte, Kilobyte, Megabyte, Gigabyte, Terabyte and Petabyte', - exclusiveOn: ["-k", "-m", "-g"], - }, - { - name: "-g", - description: "Display block counts in 1073741824-byte (1-Gbyte) blocks", - exclusiveOn: ["-k", "-m", "-h"], - }, - { - name: "-k", - description: "Display block counts in 1024-byte (1-Kbyte) blocks", - exclusiveOn: ["-g", "-m", "-h"], - }, - { - name: "-m", - description: "Display block counts in 1048576-byte (1-Mbyte) blocks", - exclusiveOn: ["-g", "-k", "-h"], - }, - { - name: "-I", - description: "Ignore files and directories matching the specified mask", - args: { - name: "mask", - }, - }, - { - name: "-L", - description: - "Symbolic links on the command line and in file hierarchies are followed", - exclusiveOn: ["-H", "-P"], - }, - { - name: "-r", - description: - "Generate messages about directories that cannot be read, files that cannot be opened, and so on. This is the default case. This option exists solely for conformance with X/Open Portability Guide Issue 4 (``XPG4'')", - }, - { - name: "-P", - description: "No symbolic links are followed. This is the default", - exclusiveOn: ["-H", "-L"], - }, - { - name: "-d", - description: - "Display an entry for all files and directories depth directories deep", - exclusiveOn: ["-a", "-s"], - args: { - name: "depth", - suggestions: ["0", "1", "2"], - }, - }, - { - name: "-s", - description: - "Display an entry for each specified file. (Equivalent to -d 0)", - exclusiveOn: ["-a", "-d"], - }, - { - name: "-x", - description: - "Display an entry for each specified file. (Equivalent to -d 0)", - }, - ], - args: { - isOptional: true, - name: "files", - isVariadic: true, - template: ["filepaths", "folders"], - }, + name: "du", + description: "Display disk usage statistics", + options: [ + { + name: "-a", + description: "Display an entry for each file in a file hierarchy", + exclusiveOn: ["-s", "-d"], + }, + { + name: "-c", + description: "Display a grand total", + }, + { + name: "-H", + description: + "Symbolic links on the command line are followed, symbolic links in file hierarchies are not followed", + exclusiveOn: ["-L", "-P"], + }, + { + name: "-h", + description: + '"Human-readable" output. Use unit suffixes: Byte, Kilobyte, Megabyte, Gigabyte, Terabyte and Petabyte', + exclusiveOn: ["-k", "-m", "-g"], + }, + { + name: "-g", + description: "Display block counts in 1073741824-byte (1-Gbyte) blocks", + exclusiveOn: ["-k", "-m", "-h"], + }, + { + name: "-k", + description: "Display block counts in 1024-byte (1-Kbyte) blocks", + exclusiveOn: ["-g", "-m", "-h"], + }, + { + name: "-m", + description: "Display block counts in 1048576-byte (1-Mbyte) blocks", + exclusiveOn: ["-g", "-k", "-h"], + }, + { + name: "-I", + description: "Ignore files and directories matching the specified mask", + args: { + name: "mask", + }, + }, + { + name: "-L", + description: + "Symbolic links on the command line and in file hierarchies are followed", + exclusiveOn: ["-H", "-P"], + }, + { + name: "-r", + description: + "Generate messages about directories that cannot be read, files that cannot be opened, and so on. This is the default case. This option exists solely for conformance with X/Open Portability Guide Issue 4 (``XPG4'')", + }, + { + name: "-P", + description: "No symbolic links are followed. This is the default", + exclusiveOn: ["-H", "-L"], + }, + { + name: "-d", + description: + "Display an entry for all files and directories depth directories deep", + exclusiveOn: ["-a", "-s"], + args: { + name: "depth", + suggestions: ["0", "1", "2"], + }, + }, + { + name: "-s", + description: + "Display an entry for each specified file. (Equivalent to -d 0)", + exclusiveOn: ["-a", "-d"], + }, + { + name: "-x", + description: + "Display an entry for each specified file. (Equivalent to -d 0)", + }, + ], + args: { + isOptional: true, + name: "files", + isVariadic: true, + template: ["filepaths", "folders"], + }, }; export default completionSpec; diff --git a/extensions/terminal-suggest/src/completions/upstream/echo.ts b/extensions/terminal-suggest/src/completions/upstream/echo.ts index 8ca21b858b39..7b1452e23316 100644 --- a/extensions/terminal-suggest/src/completions/upstream/echo.ts +++ b/extensions/terminal-suggest/src/completions/upstream/echo.ts @@ -1,42 +1,42 @@ const environmentVariableGenerator: Fig.Generator = { - custom: async (tokens, _, context) => { - if (tokens.length < 3 || tokens[tokens.length - 1].startsWith("$")) { - return Object.keys(context.environmentVariables).map((suggestion) => ({ - name: `$${suggestion}`, - type: "arg", - description: "Environment Variable", - })); - } else { - return []; - } - }, - trigger: "$", + custom: async (tokens, _, context) => { + if (tokens.length < 3 || tokens[tokens.length - 1].startsWith("$")) { + return Object.keys(context.environmentVariables).map((suggestion) => ({ + name: `$${suggestion}`, + type: "arg", + description: "Environment Variable", + })); + } else { + return []; + } + }, + trigger: "$", }; const completionSpec: Fig.Spec = { - name: "echo", - description: "Write arguments to the standard output", - args: { - name: "string", - isVariadic: true, - optionsCanBreakVariadicArg: false, - suggestCurrentToken: true, - generators: environmentVariableGenerator, - }, - options: [ - { - name: "-n", - description: "Do not print the trailing newline character", - }, - { - name: "-e", - description: "Interpret escape sequences", - }, - { - name: "-E", - description: "Disable escape sequences", - }, - ], + name: "echo", + description: "Write arguments to the standard output", + args: { + name: "string", + isVariadic: true, + optionsCanBreakVariadicArg: false, + suggestCurrentToken: true, + generators: environmentVariableGenerator, + }, + options: [ + { + name: "-n", + description: "Do not print the trailing newline character", + }, + { + name: "-e", + description: "Interpret escape sequences", + }, + { + name: "-E", + description: "Disable escape sequences", + }, + ], }; export default completionSpec; diff --git a/extensions/terminal-suggest/src/completions/upstream/find.ts b/extensions/terminal-suggest/src/completions/upstream/find.ts index 5b5245cb5fdd..08b4d6a5393b 100644 --- a/extensions/terminal-suggest/src/completions/upstream/find.ts +++ b/extensions/terminal-suggest/src/completions/upstream/find.ts @@ -1,71 +1,71 @@ const completionSpec: Fig.Spec = { - name: "find", - description: "Walk a file hierarchy", - args: [ - { - name: "path", - isOptional: true, - isVariadic: true, - template: ["folders"], - }, - { - // TODO Suggestions for primaries and operands. See `man find` - name: "expression", - description: "Composition of primaries and operands", - isOptional: true, - isVariadic: true, - }, - ], - options: [ - { - name: "-E", - description: - "Interpret regular expressions followed by -regex and -iregex primaries as extended", - }, - { - name: "-H", - description: - "Cause the file information and file type returned for each symbolic link specified to be those referenced by the link", - exclusiveOn: ["-L", "-P"], - }, - { - name: "-L", - description: - "Cause the file information and file type returned for each symbolic link to be those of the file referenced by the link", - exclusiveOn: ["-H", "-P"], - }, - { - name: "-P", - description: - "Cause the file information and file type returned for each symbolic link to be those for the link itself", - exclusiveOn: ["-H", "-L"], - }, - { - name: "-X", - description: "Permit find to be safely used in conjunction with xargs", - }, - { - name: "-d", - description: "Cause find to perform a depth-first traversal", - }, - { - name: "-f", - description: "Specify a file hierarch for find to traverse", - args: { - name: "path", - }, - }, - { - name: "-s", - description: - "Cause find to traverse the file hierarchies in lexicographical order", - }, - { - name: "-x", - description: - "Prevent find from descending into directories that have a device number different than that of the file from which the descent began", - }, - ], + name: "find", + description: "Walk a file hierarchy", + args: [ + { + name: "path", + isOptional: true, + isVariadic: true, + template: ["folders"], + }, + { + // TODO Suggestions for primaries and operands. See `man find` + name: "expression", + description: "Composition of primaries and operands", + isOptional: true, + isVariadic: true, + }, + ], + options: [ + { + name: "-E", + description: + "Interpret regular expressions followed by -regex and -iregex primaries as extended", + }, + { + name: "-H", + description: + "Cause the file information and file type returned for each symbolic link specified to be those referenced by the link", + exclusiveOn: ["-L", "-P"], + }, + { + name: "-L", + description: + "Cause the file information and file type returned for each symbolic link to be those of the file referenced by the link", + exclusiveOn: ["-H", "-P"], + }, + { + name: "-P", + description: + "Cause the file information and file type returned for each symbolic link to be those for the link itself", + exclusiveOn: ["-H", "-L"], + }, + { + name: "-X", + description: "Permit find to be safely used in conjunction with xargs", + }, + { + name: "-d", + description: "Cause find to perform a depth-first traversal", + }, + { + name: "-f", + description: "Specify a file hierarch for find to traverse", + args: { + name: "path", + }, + }, + { + name: "-s", + description: + "Cause find to traverse the file hierarchies in lexicographical order", + }, + { + name: "-x", + description: + "Prevent find from descending into directories that have a device number different than that of the file from which the descent began", + }, + ], }; export default completionSpec; diff --git a/extensions/terminal-suggest/src/completions/upstream/git.ts b/extensions/terminal-suggest/src/completions/upstream/git.ts index 62d90d636b47..8339af128e46 100644 --- a/extensions/terminal-suggest/src/completions/upstream/git.ts +++ b/extensions/terminal-suggest/src/completions/upstream/git.ts @@ -1,4 +1,6 @@ -// import { ai } from "../../fig/ai/ai"; +// If running under NodeJS, URL is a global class (since v10) but for older compatibility, import may be required +// import { URL } from "url"; +function ai(...args: any[]): undefined { return undefined; } const filterMessages = (out: string): string => { return out.startsWith("warning:") || out.startsWith("error:") @@ -29,7 +31,7 @@ const postProcessTrackedFiles: Fig.Generator["postProcess"] = ( try { ext = file.split(".").slice(-1)[0]; - } catch (e) { } + } catch (e) {} if (file.endsWith("/")) { ext = "folder"; @@ -53,69 +55,67 @@ interface PostProcessBranchesOptions { const postProcessBranches = (options: PostProcessBranchesOptions = {}): Fig.Generator["postProcess"] => - (out): (Fig.Suggestion | null)[] => { - const { insertWithoutRemotes = false } = options; + (out) => { + const { insertWithoutRemotes = false } = options; - const output = filterMessages(out); + const output = filterMessages(out); - if (output.startsWith("fatal:")) { - return []; - } + if (output.startsWith("fatal:")) { + return []; + } - const seen = new Set(); - return output - .split("\n") - .filter((line) => !line.trim().startsWith("HEAD")) - .map((branch) => { - let name = branch.trim(); - const parts = branch.match(/\S+/g); - if (!parts) { - return null; - } - if (parts.length > 1) { - if (parts[0] === "*") { - // We are in a detached HEAD state - if (branch.includes("HEAD detached")) { - return null; - } - // Current branch - return { - name: branch.replace("*", "").trim(), - description: "Current branch", - priority: 100, - icon: "⭐️", - }; - } else if (parts[0] === "+") { - // Branch checked out in another worktree. - name = branch.replace("+", "").trim(); + const seen = new Set(); + return output + .split("\n") + .filter((line) => !line.trim().startsWith("HEAD")) + .map((branch) => { + let name = branch.trim(); + const parts = branch.match(/\S+/g); + if (parts && parts.length > 1) { + if (parts[0] === "*") { + // We are in a detached HEAD state + if (branch.includes("HEAD detached")) { + return null; } + // Current branch + return { + name: branch.replace("*", "").trim(), + description: "Current branch", + priority: 100, + icon: "⭐️", + }; + } else if (parts[0] === "+") { + // Branch checked out in another worktree. + name = branch.replace("+", "").trim(); } + } - let description = "Branch"; + let description = "Branch"; - if (insertWithoutRemotes && name.startsWith("remotes/")) { - name = name.slice(name.indexOf("/", 8) + 1); - description = "Remote branch"; - } + if (insertWithoutRemotes && name.startsWith("remotes/")) { + name = name.slice(name.indexOf("/", 8) + 1); + description = "Remote branch"; + } - const space = name.indexOf(" "); - if (space !== -1) { - name = name.slice(0, space); - } + const space = name.indexOf(" "); + if (space !== -1) { + name = name.slice(0, space); + } - return { - name, - description, - icon: "fig://icon?type=git", - priority: 75, - }; - }) - .filter((suggestion) => { - if (!suggestion || seen.has(suggestion.name)) return false; - seen.add(suggestion.name); - return true; - }); - }; + return { + name, + description, + icon: "fig://icon?type=git", + priority: 75, + }; + }) + .filter((suggestion) => { + if (!suggestion) return false; + if (seen.has(suggestion.name)) return false; + seen.add(suggestion.name); + return true; + }); + }; export const gitGenerators: Record = { // Commit history @@ -298,27 +298,36 @@ export const gitGenerators: Record = { remotes: { script: ["git", "--no-optional-locks", "remote", "-v"], postProcess: function (out) { - const remoteURLs = out.split("\n").reduce>((dict, line) => { - const pair = line.split("\t"); - const remote = pair[0]; - const url = pair[1].split(" ")[0]; + // Helper to extract hostname from remote URL (HTTP/S or SSH-type) + function getHostnameFromRemoteURL(url: string): string { + try { + // Handle HTTP/HTTPS/SSH URL using URL constructor. + if (/^https?:\/\//i.test(url) || /^ssh:\/\//i.test(url)) { + return new URL(url).hostname; + } + // Handle git SSH format: git@host:path.git + const sshMatch = url.match(/^([^@]+@)?([^:]+):/); + if (sshMatch) { + return sshMatch[2]; + } + } catch (e) {} + return ""; + } + const remoteURLs = out + .split("\n") + .reduce>((dict, line) => { + const pair = line.split("\t"); + const remote = pair[0]; + const url = pair[1].split(" ")[0]; - dict[remote] = url; - return dict; - }, {}); + dict[remote] = url; + return dict; + }, {}); return Object.keys(remoteURLs).map((remote) => { const url = remoteURLs[remote]; let icon = "box"; - if (url.includes("github.com")) { - icon = "github"; - } - if (url.includes("gitlab.com")) { - icon = "gitlab"; - } - - if (url.includes("heroku.com")) { icon = "heroku"; } return { @@ -426,7 +435,7 @@ export const gitGenerators: Record = { let ext = ""; try { ext = file.split(".").slice(-1)[0]; - } catch (e) { } + } catch (e) {} if (file.endsWith("/")) { ext = "folder"; @@ -4026,7 +4035,7 @@ const daemonServices: Fig.Suggestion[] = [ const completionSpec: Fig.Spec = { name: "git", - description: "Distributed version control system", + description: "The stupid content tracker", generateSpec: async (_, executeShellCommand) => { const { stdout } = await executeShellCommand({ command: "git", @@ -4401,35 +4410,34 @@ const completionSpec: Fig.Spec = { description: "Use the given message as the commit message", args: { name: "message", - // generators: ai({ - // name: "git commit -m", - // prompt: async ({ executeCommand }) => { - // const { stdout } = await executeCommand({ - // command: "git", - // args: [ - // "log", - // "--pretty=format:%s", - // "--abbrev-commit", - // "--max-count=20", - // ], - // }); + generators: ai({ + name: "git commit -m", + prompt: async ({ executeCommand }: any) => { + const { stdout } = await executeCommand({ + command: "git", + args: [ + "log", + "--pretty=format:%s", + "--abbrev-commit", + "--max-count=20", + ], + }); - // return ( - // 'Generate a git commit message summary based on this git diff, the "summary" must be no more ' + - // "than 70-75 characters, and it must describe both what the patch changes, as well as why the " + - // `patch might be necessary.\n\nHere are some examples from the repo:\n${stdout}` - // ); - // }, - // message: async ({ executeCommand }) => - // ( - // await executeCommand({ - // command: "git", - // args: ["diff", "--staged"], - // }) - // ).stdout, - // splitOn: "\n", - // }), - // }, + return ( + 'Generate a git commit message summary based on this git diff, the "summary" must be no more ' + + "than 70-75 characters, and it must describe both what the patch changes, as well as why the " + + `patch might be necessary.\n\nHere are some examples from the repo:\n${stdout}` + ); + }, + message: async ({ executeCommand }: any) => + ( + await executeCommand({ + command: "git", + args: ["diff", "--staged"], + }) + ).stdout, + splitOn: "\n", + }), }, }, { diff --git a/extensions/terminal-suggest/src/completions/upstream/grep.ts b/extensions/terminal-suggest/src/completions/upstream/grep.ts index a7c15281424b..a897d463a0bc 100644 --- a/extensions/terminal-suggest/src/completions/upstream/grep.ts +++ b/extensions/terminal-suggest/src/completions/upstream/grep.ts @@ -1,344 +1,344 @@ const completionSpec: Fig.Spec = { - name: "grep", - description: - "Matches patterns in input text. Supports simple patterns and regular expressions", - args: [ - { - name: "search pattern", - suggestCurrentToken: true, - }, - { - name: "file", - template: "filepaths", - }, - ], - options: [ - { - name: "--help", - description: - "Print a usage message briefly summarizing these command-line options and the bug-reporting address, then exit", - }, - { - name: ["-E", "--extended-regexp"], - description: - "Interpret PATTERN as an extended regular expression (-E is specified by POSIX.)", - }, - { - name: ["-F", "--fixed-string"], - description: - "Interpret PATTERN as a list of fixed strings, separated by newlines, any of which is to be matched. (-F is specified by POSIX.)", - }, - { - name: ["-G", "--basic-regexp"], - description: - "Interpret PATTERN as a basic regular expression (BRE, see below). This is the default", - }, - { - name: ["-e", "--regexp"], - description: - "Use PATTERN as the pattern. This can be used to specify multiple search patterns, or to protect a pattern beginning with a hyphen (-). (-e is specified by POSIX.)", - args: { - name: "pattern", - }, - }, - { - name: ["-i", "--ignore-case", "-y"], - description: - "Ignore case distinctions in both the PATTERN and the input files. (-i is specified by POSIX.)", - }, - { - name: ["-v", "--invert-match"], - description: - "Invert the sense of matching, to select non-matching lines. (-v is specified by POSIX.)", - }, - { - name: ["-w", "--word-regexp"], - description: - "Select only those lines containing matches that form whole words. The test is that the matching substring must either be at the beginning of the line, or preceded by a non-word constituent character. Similarly, it must be either at the end of the line or followed by a non-word constituent character. Word-constituent characters are letters, digits, and the underscore", - }, - { - name: ["-x", "--line-regexp"], - description: - "Select only those matches that exactly match the whole line. (-x is specified by POSIX.)", - }, - { - name: ["-c", "--count"], - description: - "Suppress normal output; instead print a count of matching lines for each input file. With the -v, --invert-match option, count non-matching lines. (-c is specified by POSIX.)", - }, - { - name: "--color", - description: - "Surround the matched (non-empty) strings, matching lines, context lines, file names, line numbers, byte offsets, and separators (for fields and groups of context lines) with escape sequences to display them in color on the terminal. The colors are defined by the environment variable GREP_COLORS. The deprecated environment variable GREP_COLOR is still supported, but its setting does not have priority", - args: { - name: "WHEN", - default: "auto", - suggestions: ["never", "always", "auto"], - }, - }, - { - name: ["-L", "--files-without-match"], - exclusiveOn: ["-l", "--files-with-matches"], - description: - "Suppress normal output; instead print the name of each input file from which no output would normally have been printed. The scanning will stop on the first match", - }, - { - name: ["-l", "--files-with-matches"], - exclusiveOn: ["-L", "--files-without-match"], - description: - "Suppress normal output; instead print the name of each input file from which output would normally have been printed. The scanning will stop on the first match. (-l is specified by POSIX.)", - }, - { - name: ["-m", "--max-count"], - description: - "Stop reading a file after NUM matching lines. If the input is standard input from a regular file, and NUM matching lines are output, grep ensures that the standard input is positioned to just after the last matching line before exiting, regardless of the presence of trailing context lines. This enables a calling process to resume a search. When grep stops after NUM matching lines, it outputs any trailing context lines. When the -c or --count option is also used, grep does not output a count greater than NUM. When the -v or --invert-match option is also used, grep stops after outputting NUM non-matching lines", - args: { - name: "NUM", - }, - }, - { - name: ["-o", "--only-matching"], - description: - "Print only the matched (non-empty) parts of a matching line, with each such part on a separate output line", - }, - { - name: ["-q", "--quiet", "--silent"], - description: - "Quiet; do not write anything to standard output. Exit immediately with zero status if any match is found, even if an error was detected. Also see the -s or --no-messages option. (-q is specified by POSIX.)", - }, - { - name: ["-s", "--no-messages"], - description: - "Suppress error messages about nonexistent or unreadable files. Portability note: unlike GNU grep, 7th Edition Unix grep did not conform to POSIX, because it lacked -q and its -s option behaved like GNU grep's -q option. USG -style grep also lacked -q but its -s option behaved like GNU grep. Portable shell scripts should avoid both -q and -s and should redirect standard and error output to /dev/null instead. (-s is specified by POSIX.)", - }, - { - name: ["-b", "--byte-offset"], - description: - "Print the 0-based byte offset within the input file before each line of output. If -o (--only-matching) is specified, print the offset of the matching part itself", - }, - { - name: ["-H", "--with-filename"], - description: - "Print the file name for each match. This is the default when there is more than one file to search", - }, - { - name: ["-h", "--no-filename"], - description: - "Suppress the prefixing of file names on output. This is the default when there is only one file (or only standard input) to search", - }, - { - name: "--label", - description: - "Display input actually coming from standard input as input coming from file LABEL. This is especially useful when implementing tools like zgrep, e.g., gzip -cd foo.gz | grep --label=foo -H something", - args: { - name: "LABEL", - }, - }, - { - name: ["-n", "--line-number"], - description: - "Prefix each line of output with the 1-based line number within its input file. (-n is specified by POSIX.)", - }, - { - name: ["-T", "--initial-tab"], - description: - "Make sure that the first character of actual line content lies on a tab stop, so that the alignment of tabs looks normal. This is useful with options that prefix their output to the actual content: -H,-n, and -b. In order to improve the probability that lines from a single file will all start at the same column, this also causes the line number and byte offset (if present) to be printed in a minimum size field width", - }, - { - name: ["-u", "--unix-byte-offsets"], - description: - "Report Unix-style byte offsets. This switch causes grep to report byte offsets as if the file were a Unix-style text file, i.e., with CR characters stripped off. This will produce results identical to running grep on a Unix machine. This option has no effect unless -b option is also used; it has no effect on platforms other than MS-DOS and MS -Windows", - }, - { - name: "--null", - description: - "Output a zero byte (the ASCII NUL character) instead of the character that normally follows a file name. For example, grep -lZ outputs a zero byte after each file name instead of the usual newline. This option makes the output unambiguous, even in the presence of file names containing unusual characters like newlines. This option can be used with commands like find -print0, perl -0, sort -z, and xargs -0 to process arbitrary file names, even those that contain newline characters", - }, - { - name: ["-A", "--after-context"], - description: "Print num lines of trailing context after each match", - args: { - name: "NUM", - }, - }, - { - name: ["-B", "--before-context"], - description: - "Print num lines of leading context before each match. See also the -A and -C options", - args: { - name: "NUM", - }, - }, - { - name: ["-C", "--context"], - description: - "Print NUM lines of output context. Places a line containing a group separator (--) between contiguous groups of matches. With the -o or --only-matching option, this has no effect and a warning is given", - args: { - name: "NUM", - }, - }, - { - name: ["-a", "--text"], - description: - "Treat all files as ASCII text. Normally grep will simply print ``Binary file ... matches'' if files contain binary characters. Use of this option forces grep to output lines matching the specified pattern", - }, - { - name: "--binary-files", - description: "Controls searching and printing of binary files", - args: { - name: "value", - default: "binary", - suggestions: [ - { - name: "binary", - description: "Search binary files but do not print them", - }, - { - name: "without-match", - description: "Do not search binary files", - }, - { - name: "text", - description: "Treat all files as text", - }, - ], - }, - }, - { - name: ["-D", "--devices"], - description: "Specify the demanded action for devices, FIFOs and sockets", - args: { - name: "action", - default: "read", - suggestions: [ - { - name: "read", - description: "Read as if they were normal files", - }, - { - name: "skip", - description: "Devices will be silently skipped", - }, - ], - }, - }, - { - name: ["-d", "--directories"], - description: "Specify the demanded action for directories", - args: { - name: "action", - default: "read", - suggestions: [ - { - name: "read", - description: - "Directories are read in the same manner as normal files", - }, - { - name: "skip", - description: "Silently ignore the directories", - }, - { - name: "recurse", - description: "Read directories recursively", - }, - ], - }, - }, - { - name: "--exclude", - description: - "Note that --exclude patterns take priority over --include patterns, and if no --include pattern is specified, all files are searched that are not excluded. Patterns are matched to the full path specified, not only to the filename component", - args: { - name: "GLOB", - isOptional: true, - }, - }, - { - name: "--exclude-dir", - description: - "If -R is specified, only directories matching the given filename pattern are searched. Note that --exclude-dir patterns take priority over --include-dir patterns", - isRepeatable: true, - args: { - name: "dir", - template: "folders", - isOptional: true, - }, - }, - { - name: "-I", - description: - "Ignore binary files. This option is equivalent to --binary-file=without-match option", - }, - { - name: "--include", - description: - "If specified, only files matching the given filename pattern are searched. Note that --exclude patterns take priority over --include patterns. Patterns are matched to the full path specified, not only to the filename component", - args: { - name: "GLOB", - isOptional: true, - }, - }, - { - name: "--include-dir", - description: - "If -R is specified, only directories matching the given filename pattern are searched. Note that --exclude-dir patterns take priority over --include-dir patterns", - args: { - name: "dir", - template: "folders", - isOptional: true, - }, - }, - { - name: ["-R", "-r", "--recursive"], - description: "Recursively search subdirectories listed", - }, - { - name: "--line-buffered", - description: - "Force output to be line buffered. By default, output is line buffered when standard output is a terminal and block buffered otherwise", - }, - { - name: ["-U", "--binary"], - description: "Search binary files, but do not attempt to print them", - }, - { - name: ["-J", "-bz2decompress"], - description: - "Decompress the bzip2(1) compressed file before looking for the text", - }, - { - name: ["-V", "--version"], - description: "Print version number of grep to the standard output stream", - }, - { - name: ["-P", "--perl-regexp"], - description: "Interpret pattern as a Perl regular expression", - }, - { - name: ["-f", "--file"], - description: - "Obtain patterns from FILE, one per line. The empty file contains zero patterns, and therefore matches nothing. (-f is specified by POSIX.)", - args: { - name: "FILE", - template: "filepaths", - }, - }, - ], - additionalSuggestions: [ - { - name: "-RIn", - description: - "Search for a pattern [R]ecursively in the current directory, showing matching line [n]umbers, [I]gnoring non-text files", - insertValue: "-RI{cursor}", - }, - { - name: "-Hn", - description: - "Print file name with the corresponding line number (n) for each match", - insertValue: "-H{cursor}", - }, - ], + name: "grep", + description: + "Matches patterns in input text. Supports simple patterns and regular expressions", + args: [ + { + name: "search pattern", + suggestCurrentToken: true, + }, + { + name: "file", + template: "filepaths", + }, + ], + options: [ + { + name: "--help", + description: + "Print a usage message briefly summarizing these command-line options and the bug-reporting address, then exit", + }, + { + name: ["-E", "--extended-regexp"], + description: + "Interpret PATTERN as an extended regular expression (-E is specified by POSIX.)", + }, + { + name: ["-F", "--fixed-string"], + description: + "Interpret PATTERN as a list of fixed strings, separated by newlines, any of which is to be matched. (-F is specified by POSIX.)", + }, + { + name: ["-G", "--basic-regexp"], + description: + "Interpret PATTERN as a basic regular expression (BRE, see below). This is the default", + }, + { + name: ["-e", "--regexp"], + description: + "Use PATTERN as the pattern. This can be used to specify multiple search patterns, or to protect a pattern beginning with a hyphen (-). (-e is specified by POSIX.)", + args: { + name: "pattern", + }, + }, + { + name: ["-i", "--ignore-case", "-y"], + description: + "Ignore case distinctions in both the PATTERN and the input files. (-i is specified by POSIX.)", + }, + { + name: ["-v", "--invert-match"], + description: + "Invert the sense of matching, to select non-matching lines. (-v is specified by POSIX.)", + }, + { + name: ["-w", "--word-regexp"], + description: + "Select only those lines containing matches that form whole words. The test is that the matching substring must either be at the beginning of the line, or preceded by a non-word constituent character. Similarly, it must be either at the end of the line or followed by a non-word constituent character. Word-constituent characters are letters, digits, and the underscore", + }, + { + name: ["-x", "--line-regexp"], + description: + "Select only those matches that exactly match the whole line. (-x is specified by POSIX.)", + }, + { + name: ["-c", "--count"], + description: + "Suppress normal output; instead print a count of matching lines for each input file. With the -v, --invert-match option, count non-matching lines. (-c is specified by POSIX.)", + }, + { + name: "--color", + description: + "Surround the matched (non-empty) strings, matching lines, context lines, file names, line numbers, byte offsets, and separators (for fields and groups of context lines) with escape sequences to display them in color on the terminal. The colors are defined by the environment variable GREP_COLORS. The deprecated environment variable GREP_COLOR is still supported, but its setting does not have priority", + args: { + name: "WHEN", + default: "auto", + suggestions: ["never", "always", "auto"], + }, + }, + { + name: ["-L", "--files-without-match"], + exclusiveOn: ["-l", "--files-with-matches"], + description: + "Suppress normal output; instead print the name of each input file from which no output would normally have been printed. The scanning will stop on the first match", + }, + { + name: ["-l", "--files-with-matches"], + exclusiveOn: ["-L", "--files-without-match"], + description: + "Suppress normal output; instead print the name of each input file from which output would normally have been printed. The scanning will stop on the first match. (-l is specified by POSIX.)", + }, + { + name: ["-m", "--max-count"], + description: + "Stop reading a file after NUM matching lines. If the input is standard input from a regular file, and NUM matching lines are output, grep ensures that the standard input is positioned to just after the last matching line before exiting, regardless of the presence of trailing context lines. This enables a calling process to resume a search. When grep stops after NUM matching lines, it outputs any trailing context lines. When the -c or --count option is also used, grep does not output a count greater than NUM. When the -v or --invert-match option is also used, grep stops after outputting NUM non-matching lines", + args: { + name: "NUM", + }, + }, + { + name: ["-o", "--only-matching"], + description: + "Print only the matched (non-empty) parts of a matching line, with each such part on a separate output line", + }, + { + name: ["-q", "--quiet", "--silent"], + description: + "Quiet; do not write anything to standard output. Exit immediately with zero status if any match is found, even if an error was detected. Also see the -s or --no-messages option. (-q is specified by POSIX.)", + }, + { + name: ["-s", "--no-messages"], + description: + "Suppress error messages about nonexistent or unreadable files. Portability note: unlike GNU grep, 7th Edition Unix grep did not conform to POSIX, because it lacked -q and its -s option behaved like GNU grep's -q option. USG -style grep also lacked -q but its -s option behaved like GNU grep. Portable shell scripts should avoid both -q and -s and should redirect standard and error output to /dev/null instead. (-s is specified by POSIX.)", + }, + { + name: ["-b", "--byte-offset"], + description: + "Print the 0-based byte offset within the input file before each line of output. If -o (--only-matching) is specified, print the offset of the matching part itself", + }, + { + name: ["-H", "--with-filename"], + description: + "Print the file name for each match. This is the default when there is more than one file to search", + }, + { + name: ["-h", "--no-filename"], + description: + "Suppress the prefixing of file names on output. This is the default when there is only one file (or only standard input) to search", + }, + { + name: "--label", + description: + "Display input actually coming from standard input as input coming from file LABEL. This is especially useful when implementing tools like zgrep, e.g., gzip -cd foo.gz | grep --label=foo -H something", + args: { + name: "LABEL", + }, + }, + { + name: ["-n", "--line-number"], + description: + "Prefix each line of output with the 1-based line number within its input file. (-n is specified by POSIX.)", + }, + { + name: ["-T", "--initial-tab"], + description: + "Make sure that the first character of actual line content lies on a tab stop, so that the alignment of tabs looks normal. This is useful with options that prefix their output to the actual content: -H,-n, and -b. In order to improve the probability that lines from a single file will all start at the same column, this also causes the line number and byte offset (if present) to be printed in a minimum size field width", + }, + { + name: ["-u", "--unix-byte-offsets"], + description: + "Report Unix-style byte offsets. This switch causes grep to report byte offsets as if the file were a Unix-style text file, i.e., with CR characters stripped off. This will produce results identical to running grep on a Unix machine. This option has no effect unless -b option is also used; it has no effect on platforms other than MS-DOS and MS -Windows", + }, + { + name: "--null", + description: + "Output a zero byte (the ASCII NUL character) instead of the character that normally follows a file name. For example, grep -lZ outputs a zero byte after each file name instead of the usual newline. This option makes the output unambiguous, even in the presence of file names containing unusual characters like newlines. This option can be used with commands like find -print0, perl -0, sort -z, and xargs -0 to process arbitrary file names, even those that contain newline characters", + }, + { + name: ["-A", "--after-context"], + description: "Print num lines of trailing context after each match", + args: { + name: "NUM", + }, + }, + { + name: ["-B", "--before-context"], + description: + "Print num lines of leading context before each match. See also the -A and -C options", + args: { + name: "NUM", + }, + }, + { + name: ["-C", "--context"], + description: + "Print NUM lines of output context. Places a line containing a group separator (--) between contiguous groups of matches. With the -o or --only-matching option, this has no effect and a warning is given", + args: { + name: "NUM", + }, + }, + { + name: ["-a", "--text"], + description: + "Treat all files as ASCII text. Normally grep will simply print ``Binary file ... matches'' if files contain binary characters. Use of this option forces grep to output lines matching the specified pattern", + }, + { + name: "--binary-files", + description: "Controls searching and printing of binary files", + args: { + name: "value", + default: "binary", + suggestions: [ + { + name: "binary", + description: "Search binary files but do not print them", + }, + { + name: "without-match", + description: "Do not search binary files", + }, + { + name: "text", + description: "Treat all files as text", + }, + ], + }, + }, + { + name: ["-D", "--devices"], + description: "Specify the demanded action for devices, FIFOs and sockets", + args: { + name: "action", + default: "read", + suggestions: [ + { + name: "read", + description: "Read as if they were normal files", + }, + { + name: "skip", + description: "Devices will be silently skipped", + }, + ], + }, + }, + { + name: ["-d", "--directories"], + description: "Specify the demanded action for directories", + args: { + name: "action", + default: "read", + suggestions: [ + { + name: "read", + description: + "Directories are read in the same manner as normal files", + }, + { + name: "skip", + description: "Silently ignore the directories", + }, + { + name: "recurse", + description: "Read directories recursively", + }, + ], + }, + }, + { + name: "--exclude", + description: + "Note that --exclude patterns take priority over --include patterns, and if no --include pattern is specified, all files are searched that are not excluded. Patterns are matched to the full path specified, not only to the filename component", + args: { + name: "GLOB", + isOptional: true, + }, + }, + { + name: "--exclude-dir", + description: + "If -R is specified, only directories matching the given filename pattern are searched. Note that --exclude-dir patterns take priority over --include-dir patterns", + isRepeatable: true, + args: { + name: "dir", + template: "folders", + isOptional: true, + }, + }, + { + name: "-I", + description: + "Ignore binary files. This option is equivalent to --binary-file=without-match option", + }, + { + name: "--include", + description: + "If specified, only files matching the given filename pattern are searched. Note that --exclude patterns take priority over --include patterns. Patterns are matched to the full path specified, not only to the filename component", + args: { + name: "GLOB", + isOptional: true, + }, + }, + { + name: "--include-dir", + description: + "If -R is specified, only directories matching the given filename pattern are searched. Note that --exclude-dir patterns take priority over --include-dir patterns", + args: { + name: "dir", + template: "folders", + isOptional: true, + }, + }, + { + name: ["-R", "-r", "--recursive"], + description: "Recursively search subdirectories listed", + }, + { + name: "--line-buffered", + description: + "Force output to be line buffered. By default, output is line buffered when standard output is a terminal and block buffered otherwise", + }, + { + name: ["-U", "--binary"], + description: "Search binary files, but do not attempt to print them", + }, + { + name: ["-J", "-bz2decompress"], + description: + "Decompress the bzip2(1) compressed file before looking for the text", + }, + { + name: ["-V", "--version"], + description: "Print version number of grep to the standard output stream", + }, + { + name: ["-P", "--perl-regexp"], + description: "Interpret pattern as a Perl regular expression", + }, + { + name: ["-f", "--file"], + description: + "Obtain patterns from FILE, one per line. The empty file contains zero patterns, and therefore matches nothing. (-f is specified by POSIX.)", + args: { + name: "FILE", + template: "filepaths", + }, + }, + ], + additionalSuggestions: [ + { + name: "-RIn", + description: + "Search for a pattern [R]ecursively in the current directory, showing matching line [n]umbers, [I]gnoring non-text files", + insertValue: "-RI{cursor}", + }, + { + name: "-Hn", + description: + "Print file name with the corresponding line number (n) for each match", + insertValue: "-H{cursor}", + }, + ], }; export default completionSpec; diff --git a/extensions/terminal-suggest/src/completions/upstream/head.ts b/extensions/terminal-suggest/src/completions/upstream/head.ts index bc837e09dad1..a7ae0a7aedec 100644 --- a/extensions/terminal-suggest/src/completions/upstream/head.ts +++ b/extensions/terminal-suggest/src/completions/upstream/head.ts @@ -1,35 +1,35 @@ const completionSpec: Fig.Spec = { - name: "head", - description: "Output the first part of files", - args: { - name: "file", - template: "filepaths", - }, - options: [ - { - name: ["-c", "--bytes"], - description: "Print the first [numBytes] bytes of each file", - args: { name: "numBytes" }, - }, - { - name: ["-n", "--lines"], - description: "Print the first [numLines] lines instead of the first 10", - args: { name: "numLines" }, - }, - { - name: ["-q", "--quiet", "--silent"], - description: "Never print headers giving file names", - }, - { - name: ["-v", "--verbose"], - description: "Always print headers giving file names", - }, - { name: "--help", description: "Display this help and exit" }, - { - name: "--version", - description: "Output version information and exit", - }, - ], + name: "head", + description: "Output the first part of files", + args: { + name: "file", + template: "filepaths", + }, + options: [ + { + name: ["-c", "--bytes"], + description: "Print the first [numBytes] bytes of each file", + args: { name: "numBytes" }, + }, + { + name: ["-n", "--lines"], + description: "Print the first [numLines] lines instead of the first 10", + args: { name: "numLines" }, + }, + { + name: ["-q", "--quiet", "--silent"], + description: "Never print headers giving file names", + }, + { + name: ["-v", "--verbose"], + description: "Always print headers giving file names", + }, + { name: "--help", description: "Display this help and exit" }, + { + name: "--version", + description: "Output version information and exit", + }, + ], }; export default completionSpec; diff --git a/extensions/terminal-suggest/src/completions/upstream/killall.ts b/extensions/terminal-suggest/src/completions/upstream/killall.ts index c22e55dd6a4f..c963a97177fa 100644 --- a/extensions/terminal-suggest/src/completions/upstream/killall.ts +++ b/extensions/terminal-suggest/src/completions/upstream/killall.ts @@ -1,153 +1,153 @@ // Linux incompatible const signals = [ - "hup", - "int", - "quit", - "ill", - "trap", - "abrt", - "emt", - "fpe", - "kill", - "bus", - "segv", - "sys", - "pipe", - "alrm", - // This is the default signal - // "term", - "urg", - "stop", - "tstp", - "cont", - "chld", - "ttin", - "ttou", - "io", - "xcpu", - "xfsz", - "vtalrm", - "prof", - "winch", - "info", - "usr1", - "usr2", + "hup", + "int", + "quit", + "ill", + "trap", + "abrt", + "emt", + "fpe", + "kill", + "bus", + "segv", + "sys", + "pipe", + "alrm", + // This is the default signal + // "term", + "urg", + "stop", + "tstp", + "cont", + "chld", + "ttin", + "ttou", + "io", + "xcpu", + "xfsz", + "vtalrm", + "prof", + "winch", + "info", + "usr1", + "usr2", ]; const completionSpec: Fig.Spec = { - name: "killall", - description: "Kill processes by name", - args: { - name: "process_name", - isVariadic: true, - generators: { - // All processes, only display the path - script: ["bash", "-c", "ps -A -o comm | sort -u"], - postProcess: (out) => - out - .trim() - .split("\n") - .map((path) => { - const appExtIndex = path.indexOf(".app/"); - const isApp = appExtIndex !== -1; - const name = path.slice(path.lastIndexOf("/") + 1); - const nameChars = new Set(name); - const badChars = ["(", "_", "."]; - return { - name, - description: path, - priority: - !badChars.some((char) => nameChars.has(char)) && isApp - ? 51 - : 40, - icon: isApp - ? "fig://" + path.slice(0, appExtIndex + 4) - : "fig://icon?type=gear", - }; - }), - }, - }, - options: [ - { - name: "-d", - description: "Be verbose (dry run) and display number of user processes", - }, - { - name: "-e", - description: - "Use the effective user ID instead of the real user ID for matching processes with -u", - }, - { - name: "-help", - description: "Display help and exit", - }, - { - name: "-I", - description: "Request confirmation before killing each process", - }, - { - name: "-l", - description: "List the names of the available signals and exit", - }, - { - name: "-m", - description: "Match the process name as a regular expression", - }, - { - name: "-v", - description: "Be verbose", - }, - { - name: "-s", - description: "Be verbose (dry run)", - }, - ...signals.map((signal) => ({ - name: "-SIG" + signal.toUpperCase(), - description: `Send ${signal.toUpperCase()} instead of TERM`, - })), - { - name: "-u", - description: - "Limit potentially matching processes to those belonging to the user", - args: { - name: "user", - generators: { - script: ["bash", "-c", "dscl . -list /Users | grep -v '^_'"], - postProcess: (out) => - out - .trim() - .split("\n") - .map((username) => ({ - name: username, - icon: "fig://template?badge=👤", - })), - }, - }, - }, - { - name: "-t", - description: - "Limit matching processes to those running on the specified TTY", - args: { - name: "tty", - }, - }, - { - name: "-c", - description: "Limit matching processes to those matching the given name", - args: { - name: "name", - }, - }, - { - name: "-q", - description: "Suppress error message if no processes are matched", - }, - { - name: "-z", - description: "Do not skip zombies", - }, - ], + name: "killall", + description: "Kill processes by name", + args: { + name: "process_name", + isVariadic: true, + generators: { + // All processes, only display the path + script: ["bash", "-c", "ps -A -o comm | sort -u"], + postProcess: (out) => + out + .trim() + .split("\n") + .map((path) => { + const appExtIndex = path.indexOf(".app/"); + const isApp = appExtIndex !== -1; + const name = path.slice(path.lastIndexOf("/") + 1); + const nameChars = new Set(name); + const badChars = ["(", "_", "."]; + return { + name, + description: path, + priority: + !badChars.some((char) => nameChars.has(char)) && isApp + ? 51 + : 40, + icon: isApp + ? "fig://" + path.slice(0, appExtIndex + 4) + : "fig://icon?type=gear", + }; + }), + }, + }, + options: [ + { + name: "-d", + description: "Be verbose (dry run) and display number of user processes", + }, + { + name: "-e", + description: + "Use the effective user ID instead of the real user ID for matching processes with -u", + }, + { + name: "-help", + description: "Display help and exit", + }, + { + name: "-I", + description: "Request confirmation before killing each process", + }, + { + name: "-l", + description: "List the names of the available signals and exit", + }, + { + name: "-m", + description: "Match the process name as a regular expression", + }, + { + name: "-v", + description: "Be verbose", + }, + { + name: "-s", + description: "Be verbose (dry run)", + }, + ...signals.map((signal) => ({ + name: "-SIG" + signal.toUpperCase(), + description: `Send ${signal.toUpperCase()} instead of TERM`, + })), + { + name: "-u", + description: + "Limit potentially matching processes to those belonging to the user", + args: { + name: "user", + generators: { + script: ["bash", "-c", "dscl . -list /Users | grep -v '^_'"], + postProcess: (out) => + out + .trim() + .split("\n") + .map((username) => ({ + name: username, + icon: "fig://template?badge=👤", + })), + }, + }, + }, + { + name: "-t", + description: + "Limit matching processes to those running on the specified TTY", + args: { + name: "tty", + }, + }, + { + name: "-c", + description: "Limit matching processes to those matching the given name", + args: { + name: "name", + }, + }, + { + name: "-q", + description: "Suppress error message if no processes are matched", + }, + { + name: "-z", + description: "Do not skip zombies", + }, + ], }; export default completionSpec; diff --git a/extensions/terminal-suggest/src/completions/upstream/less.ts b/extensions/terminal-suggest/src/completions/upstream/less.ts index f8994808a44d..f50abbd4db85 100644 --- a/extensions/terminal-suggest/src/completions/upstream/less.ts +++ b/extensions/terminal-suggest/src/completions/upstream/less.ts @@ -1,19 +1,19 @@ const completionSpec: Fig.Spec = { - name: "less", - description: "Opposite of more", - args: { - isVariadic: true, - template: "filepaths", - }, - options: [ - { - name: ["-?", "--help"], - description: - 'This option displays a summary of the commands accepted by less (the same as the h command). (Depending on how your shell interprets the question mark, it may be necessary to quote the question mark, thus: "-?"', - }, - { - name: ["-a", "--search-skip-screen"], - description: `By default, forward searches start at the top of the displayed + name: "less", + description: "Opposite of more", + args: { + isVariadic: true, + template: "filepaths", + }, + options: [ + { + name: ["-?", "--help"], + description: + 'This option displays a summary of the commands accepted by less (the same as the h command). (Depending on how your shell interprets the question mark, it may be necessary to quote the question mark, thus: "-?"', + }, + { + name: ["-a", "--search-skip-screen"], + description: `By default, forward searches start at the top of the displayed screen and backwards searches start at the bottom of the displayed screen (except for repeated searches invoked by the n or N commands, which start after or before the "target" line @@ -21,10 +21,10 @@ respectively; see the -j option for more about the target line). The -a option causes forward searches to instead start at the bottom of the screen and backward searches to start at the top of the screen, thus skipping all lines displayed on the screen`, - }, - { - name: ["-A", "--SEARCH-SKIP-SCREEN"], - description: `Causes all forward searches (not just non-repeated searches) to + }, + { + name: ["-A", "--SEARCH-SKIP-SCREEN"], + description: `Causes all forward searches (not just non-repeated searches) to start just after the target line, and all backward searches to start just before the target line. Thus, forward searches will skip part of the displayed screen (from the first line up to and @@ -32,23 +32,23 @@ including the target line). Similarly backwards searches will skip the displayed screen from the last line up to and including the target line. This was the default behavior in less versions prior to 441`, - }, + }, - { - name: ["-b", "--buffers"], - args: { name: "n" }, - description: `Specifies the amount of buffer space less will use for each + { + name: ["-b", "--buffers"], + args: { name: "n" }, + description: `Specifies the amount of buffer space less will use for each file, in units of kilobytes (1024 bytes). By default 64 KB of buffer space is used for each file (unless the file is a pipe; see the -B option). The -b option specifies instead that n kilobytes of buffer space should be used for each file. If n is -1, buffer space is unlimited; that is, the entire file can be read into memory`, - }, + }, - { - name: ["-B", "--auto-buffers"], - description: `By default, when data is read from a pipe, buffers are allocated + { + name: ["-B", "--auto-buffers"], + description: `By default, when data is read from a pipe, buffers are allocated automatically as needed. If a large amount of data is read from the pipe, this can cause a large amount of memory to be allocated. The -B option disables this automatic allocation of @@ -57,33 +57,33 @@ specified by the -b option) is used for the pipe. Warning: use of -B can result in erroneous display, since only the most recently viewed part of the piped data is kept in memory; any earlier data is lost`, - }, + }, - { - name: ["-c", "--clear-screen"], - description: `Causes full screen repaints to be painted from the top line + { + name: ["-c", "--clear-screen"], + description: `Causes full screen repaints to be painted from the top line down. By default, full screen repaints are done by scrolling from the bottom of the screen`, - }, + }, - { - name: ["-C", "--CLEAR-SCREEN"], - description: `Same as -c, for compatibility with older versions of less`, - }, + { + name: ["-C", "--CLEAR-SCREEN"], + description: `Same as -c, for compatibility with older versions of less`, + }, - { - name: ["-d", "--dumb"], - description: `The -d option suppresses the error message normally displayed if + { + name: ["-d", "--dumb"], + description: `The -d option suppresses the error message normally displayed if the terminal is dumb; that is, lacks some important capability, such as the ability to clear the screen or scroll backward. The -d option does not otherwise change the behavior of less on a dumb terminal`, - }, + }, - { - name: ["-D", "--color"], - args: { name: "xcolor" }, - description: `Changes the color of different parts of the displayed text. x + { + name: ["-D", "--color"], + args: { name: "xcolor" }, + description: `Changes the color of different parts of the displayed text. x is a single character which selects the type of text whose color is being set: B Binary characters. @@ -144,80 +144,80 @@ color is set to that of normal text. On MS-DOS versions of less, 8-bit color is not supported; instead, decimal values are interpreted as 4-bit CHAR_INFO.Attributes values (see https://docs.microsoft.com/en-us/windows/console/char-info-str)`, - }, + }, - { - name: ["-e", "--quit-at-eof"], - description: `Causes less to automatically exit the second time it reaches + { + name: ["-e", "--quit-at-eof"], + description: `Causes less to automatically exit the second time it reaches end-of-file. By default, the only way to exit less is via the "q" command`, - }, + }, - { - name: ["-E", "--QUIT-AT-EOF"], - description: `Causes less to automatically exit the first time it reaches end- + { + name: ["-E", "--QUIT-AT-EOF"], + description: `Causes less to automatically exit the first time it reaches end- of-file`, - }, + }, - { - name: ["-f", "--force"], - description: `Forces non-regular files to be opened. (A non-regular file is a + { + name: ["-f", "--force"], + description: `Forces non-regular files to be opened. (A non-regular file is a directory or a device special file.) Also suppresses the warning message when a binary file is opened. By default, less will refuse to open non-regular files. Note that some operating systems will not allow directories to be read, even if -f is set`, - }, + }, - { - name: ["-F", "--quit-if-one-screen"], - description: `Causes less to automatically exit if the entire file can be + { + name: ["-F", "--quit-if-one-screen"], + description: `Causes less to automatically exit if the entire file can be displayed on the first screen`, - }, + }, - { - name: ["-g", "--hilite-search"], - description: `Normally, less will highlight ALL strings which match the last + { + name: ["-g", "--hilite-search"], + description: `Normally, less will highlight ALL strings which match the last search command. The -g option changes this behavior to highlight only the particular string which was found by the last search command. This can cause less to run somewhat faster than the default`, - }, + }, - { - name: ["-G", "--HILITE-SEARCH"], - description: `The -G option suppresses all highlighting of strings found by + { + name: ["-G", "--HILITE-SEARCH"], + description: `The -G option suppresses all highlighting of strings found by search commands`, - }, + }, - { - name: ["-h", "--max-back-scroll"], - args: { name: "n" }, - description: `Specifies a maximum number of lines to scroll backward. If it + { + name: ["-h", "--max-back-scroll"], + args: { name: "n" }, + description: `Specifies a maximum number of lines to scroll backward. If it is necessary to scroll backward more than n lines, the screen is repainted in a forward direction instead. (If the terminal does not have the ability to scroll backward, -h0 is implied.)`, - }, + }, - { - name: ["-i", "--ignore-case"], - description: `Causes searches to ignore case; that is, uppercase and lowercase + { + name: ["-i", "--ignore-case"], + description: `Causes searches to ignore case; that is, uppercase and lowercase are considered identical. This option is ignored if any uppercase letters appear in the search pattern; in other words, if a pattern contains uppercase letters, then that search does not ignore case`, - }, + }, - { - name: ["-I", "--IGNORE-CASE"], - description: `Like -i, but searches ignore case even if the pattern contains + { + name: ["-I", "--IGNORE-CASE"], + description: `Like -i, but searches ignore case even if the pattern contains uppercase letters`, - }, + }, - { - name: ["-j", "--jump-target"], - args: { name: "n" }, - description: `Specifies a line on the screen where the "target" line is to be + { + name: ["-j", "--jump-target"], + args: { name: "n" }, + description: `Specifies a line on the screen where the "target" line is to be positioned. The target line is the line specified by any command to search for a pattern, jump to a line number, jump to a file percentage or jump to a tag. The screen line may be @@ -240,57 +240,57 @@ fourth line on the screen, so forward searches begin at the fifth line on the screen. However nonrepeated searches (invoked with "/" or "?") always begin at the start or end of the current screen respectively`, - }, + }, - { - name: ["-J", "--status-column"], - description: `Displays a status column at the left edge of the screen. The + { + name: ["-J", "--status-column"], + description: `Displays a status column at the left edge of the screen. The status column shows the lines that matched the current search, and any lines that are marked (via the m or M command)`, - }, + }, - { - name: ["-k", "--lesskey-file"], - args: { name: "filename", template: "filepaths" }, - description: `Causes less to open and interpret the named file as a lesskey(1) + { + name: ["-k", "--lesskey-file"], + args: { name: "filename", template: "filepaths" }, + description: `Causes less to open and interpret the named file as a lesskey(1) file. Multiple -k options may be specified. If the LESSKEY or LESSKEY_SYSTEM environment variable is set, or if a lesskey file is found in a standard place (see KEY BINDINGS), it is also used as a lesskey file`, - }, + }, - { - name: ["-K", "--quit-on-intr"], - description: `Causes less to exit immediately (with status 2) when an + { + name: ["-K", "--quit-on-intr"], + description: `Causes less to exit immediately (with status 2) when an interrupt character (usually ^C) is typed. Normally, an interrupt character causes less to stop whatever it is doing and return to its command prompt. Note that use of this option makes it impossible to return to the command prompt from the "F" command`, - }, + }, - { - name: ["-L", "--no-lessopen"], - description: `Ignore the LESSOPEN environment variable (see the INPUT + { + name: ["-L", "--no-lessopen"], + description: `Ignore the LESSOPEN environment variable (see the INPUT PREPROCESSOR section below). This option can be set from within less, but it will apply only to files opened subsequently, not to the file which is currently open`, - }, + }, - { - name: ["-m", "--long-prompt"], - description: `Causes less to prompt verbosely (like more), with the percent + { + name: ["-m", "--long-prompt"], + description: `Causes less to prompt verbosely (like more), with the percent into the file. By default, less prompts with a colon`, - }, + }, - { - name: ["-M", "--LONG-PROMPT"], - description: `Causes less to prompt even more verbosely than more`, - }, + { + name: ["-M", "--LONG-PROMPT"], + description: `Causes less to prompt even more verbosely than more`, + }, - { - name: ["-n", "--line-numbers"], - description: `Suppresses line numbers. The default (to use line numbers) may + { + name: ["-n", "--line-numbers"], + description: `Suppresses line numbers. The default (to use line numbers) may cause less to run more slowly in some cases, especially with a very large input file. Suppressing line numbers with the -n option will avoid this problem. Using line numbers means: the @@ -298,46 +298,46 @@ line number will be displayed in the verbose prompt and in the = command, and the v command will pass the current line number to the editor (see also the discussion of LESSEDIT in PROMPTS below)`, - }, + }, - { - name: ["-N", "--LINE-NUMBERS"], - description: `Causes a line number to be displayed at the beginning of each + { + name: ["-N", "--LINE-NUMBERS"], + description: `Causes a line number to be displayed at the beginning of each line in the display`, - }, + }, - { - name: ["-o", "--log-file"], - args: { name: "filename", template: "filepaths" }, - description: `Causes less to copy its input to the named file as it is being + { + name: ["-o", "--log-file"], + args: { name: "filename", template: "filepaths" }, + description: `Causes less to copy its input to the named file as it is being viewed. This applies only when the input file is a pipe, not an ordinary file. If the file already exists, less will ask for confirmation before overwriting it`, - }, + }, - { - name: ["-O", "--LOG-FILE"], - args: { name: "filename", template: "filepaths" }, - description: `The -O option is like -o, but it will overwrite an existing file + { + name: ["-O", "--LOG-FILE"], + args: { name: "filename", template: "filepaths" }, + description: `The -O option is like -o, but it will overwrite an existing file without asking for confirmation. If no log file has been specified, the -o and -O options can be used from within less to specify a log file. Without a file name, they will simply report the name of the log file. The "s" command is equivalent to specifying -o from within less`, - }, + }, - { - name: ["-p", "--pattern"], - args: { name: "pattern" }, - description: `The -p option on the command line is equivalent to specifying + { + name: ["-p", "--pattern"], + args: { name: "pattern" }, + description: `The -p option on the command line is equivalent to specifying +/pattern; that is, it tells less to start at the first occurrence of pattern in the file`, - }, + }, - { - name: ["-P", "--prompt"], - args: { name: "prompt" }, - description: `Provides a way to tailor the three prompt styles to your own + { + name: ["-P", "--prompt"], + args: { name: "prompt" }, + description: `Provides a way to tailor the three prompt styles to your own preference. This option would normally be put in the LESS environment variable, rather than being typed in with each less command. Such an option must either be the last option in the @@ -352,28 +352,28 @@ that string. F command). All prompt strings consist of a sequence of letters and special escape sequences. See the section on PROMPTS for more details`, - }, + }, - { - name: ["-q", "--quiet", "--silent"], - description: `Causes moderately "quiet" operation: the terminal bell is not + { + name: ["-q", "--quiet", "--silent"], + description: `Causes moderately "quiet" operation: the terminal bell is not rung if an attempt is made to scroll past the end of the file or before the beginning of the file. If the terminal has a "visual bell", it is used instead. The bell will be rung on certain other errors, such as typing an invalid character. The default is to ring the terminal bell in all such cases`, - }, + }, - { - name: ["-Q", "--QUIET", "--SILENT"], - description: `Causes totally "quiet" operation: the terminal bell is never + { + name: ["-Q", "--QUIET", "--SILENT"], + description: `Causes totally "quiet" operation: the terminal bell is never rung. If the terminal has a "visual bell", it is used in all cases where the terminal bell would have been rung`, - }, + }, - { - name: ["-r", "--raw-control-chars"], - description: `Causes "raw" control characters to be displayed. The default is + { + name: ["-r", "--raw-control-chars"], + description: `Causes "raw" control characters to be displayed. The default is to display control characters using the caret notation; for example, a control-A (octal 001) is displayed as "^A". Warning: when the -r option is used, less cannot keep track of the actual @@ -382,11 +382,11 @@ responds to each type of control character). Thus, various display problems may result, such as long lines being split in the wrong place. USE OF THE -r OPTION IS NOT RECOMMENDED`, - }, + }, - { - name: ["-R", "--RAW-CONTROL-CHARS"], - description: `Like -r, but only ANSI "color" escape sequences and OSC 8 + { + name: ["-R", "--RAW-CONTROL-CHARS"], + description: `Like -r, but only ANSI "color" escape sequences and OSC 8 hyperlink sequences are output in "raw" form. Unlike -r, the screen appearance is maintained correctly, provided that there are no escape sequences in the file other than these types of @@ -408,27 +408,27 @@ escape sequence. And you can make less think that characters other than the standard ones may appear between the ESC and the m by setting the environment variable LESSANSIMIDCHARS to the list of characters which can appear`, - }, + }, - { - name: ["-s", "--squeeze-blank-lines"], - description: `Causes consecutive blank lines to be squeezed into a single + { + name: ["-s", "--squeeze-blank-lines"], + description: `Causes consecutive blank lines to be squeezed into a single blank line. This is useful when viewing nroff output`, - }, + }, - { - name: ["-S", "--chop-long-lines"], - description: `Causes lines longer than the screen width to be chopped + { + name: ["-S", "--chop-long-lines"], + description: `Causes lines longer than the screen width to be chopped (truncated) rather than wrapped. That is, the portion of a long line that does not fit in the screen width is not displayed until you press RIGHT-ARROW. The default is to wrap long lines; that is, display the remainder on the next line`, - }, + }, - { - name: ["-t", "--tag"], - args: { name: "tag" }, - description: `The -t option, followed immediately by a TAG, will edit the file + { + name: ["-t", "--tag"], + args: { name: "tag" }, + description: `The -t option, followed immediately by a TAG, will edit the file containing that tag. For this to work, tag information must be available; for example, there may be a file in the current directory called "tags", which was previously built by ctags(1) @@ -439,24 +439,24 @@ the tag. (See http://www.gnu.org/software/global/global.html). The -t option may also be specified from within less (using the - command) as a way of examining a new file. The command ":t" is equivalent to specifying -t from within less`, - }, + }, - { - name: ["-T", "--tag-file"], - args: { name: "tagsfile" }, - description: `Specifies a tags file to be used instead of "tags"`, - }, + { + name: ["-T", "--tag-file"], + args: { name: "tagsfile" }, + description: `Specifies a tags file to be used instead of "tags"`, + }, - { - name: ["-u", "--underline-special"], - description: `Causes backspaces and carriage returns to be treated as + { + name: ["-u", "--underline-special"], + description: `Causes backspaces and carriage returns to be treated as printable characters; that is, they are sent to the terminal when they appear in the input`, - }, + }, - { - name: ["-U", "--UNDERLINE-SPECIAL"], - description: `Causes backspaces, tabs, carriage returns and "formatting + { + name: ["-U", "--UNDERLINE-SPECIAL"], + description: `Causes backspaces, tabs, carriage returns and "formatting characters" (as defined by Unicode) to be treated as control characters; that is, they are handled as specified by the -r option. @@ -473,16 +473,16 @@ specified by the -r option. Unicode formatting characters, such as the Byte Order Mark, are sent to the terminal. Text which is overstruck or underlined can be searched for if neither -u nor -U is in effect`, - }, + }, - { - name: ["-V", "--version"], - description: `Displays the version number of less`, - }, + { + name: ["-V", "--version"], + description: `Displays the version number of less`, + }, - { - name: ["-w", "--hilite-unread"], - description: `Temporarily highlights the first "new" line after a forward + { + name: ["-w", "--hilite-unread"], + description: `Temporarily highlights the first "new" line after a forward movement of a full page. The first "new" line is the line immediately following the line previously at the bottom of the screen. Also highlights the target line after a g or p command. @@ -490,47 +490,47 @@ The highlight is removed at the next command which causes movement. The entire line is highlighted, unless the -J option is in effect, in which case only the status column is highlighted`, - }, + }, - { - name: ["-W", "--HILITE-UNREAD"], - description: `Like -w, but temporarily highlights the first new line after any + { + name: ["-W", "--HILITE-UNREAD"], + description: `Like -w, but temporarily highlights the first new line after any forward movement command larger than one line`, - }, + }, - { - name: ["-x", "--tabs="], - args: { name: "n,..." }, - description: `Sets tab stops. If only one n is specified, tab stops are set + { + name: ["-x", "--tabs="], + args: { name: "n,..." }, + description: `Sets tab stops. If only one n is specified, tab stops are set at multiples of n. If multiple values separated by commas are specified, tab stops are set at those positions, and then continue with the same spacing as the last two. For example, -x9,17 will set tabs at positions 9, 17, 25, 33, etc. The default for n is 8`, - }, + }, - { - name: ["-X", "--no-init"], - description: `Disables sending the termcap initialization and deinitialization + { + name: ["-X", "--no-init"], + description: `Disables sending the termcap initialization and deinitialization strings to the terminal. This is sometimes desirable if the deinitialization string does something unnecessary, like clearing the screen`, - }, + }, - { - name: ["-y", "--max-forw-scroll"], - args: { name: "n" }, - description: `Specifies a maximum number of lines to scroll forward. If it is + { + name: ["-y", "--max-forw-scroll"], + args: { name: "n" }, + description: `Specifies a maximum number of lines to scroll forward. If it is necessary to scroll forward more than n lines, the screen is repainted instead. The -c or -C option may be used to repaint from the top of the screen if desired. By default, any forward movement causes scrolling`, - }, + }, - { - name: ["-z", "--window"], - args: { name: "n" }, - description: `Changes the default scrolling window size to n lines. The + { + name: ["-z", "--window"], + args: { name: "n" }, + description: `Changes the default scrolling window size to n lines. The default is one screenful. The z and w commands can also be used to change the window size. The "z" may be omitted for compatibility with some versions of more. If the number n is @@ -538,11 +538,11 @@ negative, it indicates n lines less than the current screen size. For example, if the screen is 24 lines, -z-4 sets the scrolling window to 20 lines. If the screen is resized to 40 lines, the scrolling window automatically changes to 36 lines`, - }, + }, - { - name: "--quotes", - description: `Changes the filename quoting character. This may be necessary + { + name: "--quotes", + description: `Changes the filename quoting character. This may be necessary if you are trying to name a file which contains both spaces and quote characters. Followed by a single character, this changes the quote character to that character. Filenames containing a @@ -554,18 +554,18 @@ by the open quote character and followed by the close quote character. Note that even after the quote characters are changed, this option remains -" (a dash followed by a double quote)`, - }, + }, - { - name: ["-~", "--tilde"], - description: `Normally lines after end of file are displayed as a single tilde + { + name: ["-~", "--tilde"], + description: `Normally lines after end of file are displayed as a single tilde (~). This option causes lines after end of file to be displayed as blank lines`, - }, + }, - { - name: ["-#", "--shift"], - description: `Specifies the default number of positions to scroll horizontally + { + name: ["-#", "--shift"], + description: `Specifies the default number of positions to scroll horizontally in the RIGHTARROW and LEFTARROW commands. If the number specified is zero, it sets the default number of positions to one half of the screen width. Alternately, the number may be @@ -576,11 +576,11 @@ specified as a fraction, the actual number of scroll positions is recalculated if the terminal window is resized, so that the actual scroll remains at the specified fraction of the screen width`, - }, + }, - { - name: "--follow-name", - description: `Normally, if the input file is renamed while an F command is + { + name: "--follow-name", + description: `Normally, if the input file is renamed while an F command is executing, less will continue to display the contents of the original file despite its name change. If --follow-name is specified, during an F command less will periodically attempt to @@ -588,89 +588,89 @@ reopen the file by name. If the reopen succeeds and the file is a different file from the original (which means that a new file has been created with the same name as the original (now renamed) file), less will display the contents of that new file`, - }, - { - name: "--incsearch", - description: `Subsequent search commands will be "incremental"; that is, less + }, + { + name: "--incsearch", + description: `Subsequent search commands will be "incremental"; that is, less will advance to the next line containing the search pattern as each character of the pattern is typed in`, - }, + }, - { - name: "--line-num-width", - description: `Sets the minimum width of the line number field when the -N + { + name: "--line-num-width", + description: `Sets the minimum width of the line number field when the -N option is in effect. The default is 7 characters`, - }, - { - name: "--mouse", - description: `Enables mouse input: scrolling the mouse wheel down moves + }, + { + name: "--mouse", + description: `Enables mouse input: scrolling the mouse wheel down moves forward in the file, scrolling the mouse wheel up moves backwards in the file, and clicking the mouse sets the "#" mark to the line where the mouse is clicked. The number of lines to scroll when the wheel is moved can be set by the --wheel-lines option. Mouse input works only on terminals which support X11 mouse reporting, and on the Windows version of less`, - }, - { - name: "--MOUSE", - description: `Like --mouse, except the direction scrolled on mouse wheel + }, + { + name: "--MOUSE", + description: `Like --mouse, except the direction scrolled on mouse wheel movement is reversed`, - }, - { - name: "--no-keypad", - description: `Disables sending the keypad initialization and deinitialization + }, + { + name: "--no-keypad", + description: `Disables sending the keypad initialization and deinitialization strings to the terminal. This is sometimes useful if the keypad strings make the numeric keypad behave in an undesirable manner`, - }, - { - name: "--no-histdups", - description: `This option changes the behavior so that if a search string or + }, + { + name: "--no-histdups", + description: `This option changes the behavior so that if a search string or file name is typed in, and the same string is already in the history list, the existing copy is removed from the history list before the new one is added. Thus, a given string will appear only once in the history list. Normally, a string may appear multiple times`, - }, - { - name: "--rscroll", - description: `This option changes the character used to mark truncated lines. + }, + { + name: "--rscroll", + description: `This option changes the character used to mark truncated lines. It may begin with a two-character attribute indicator like LESSBINFMT does. If there is no attribute indicator, standout is used. If set to "-", truncated lines are not marked`, - }, - { - name: "--save-marks", - description: `Save marks in the history file, so marks are retained across + }, + { + name: "--save-marks", + description: `Save marks in the history file, so marks are retained across different invocations of less`, - }, - { - name: "--status-col-width", - description: `Sets the width of the status column when the -J option is in + }, + { + name: "--status-col-width", + description: `Sets the width of the status column when the -J option is in effect. The default is 2 characters`, - }, - { - name: "--use-backslash", - description: `This option changes the interpretations of options which follow + }, + { + name: "--use-backslash", + description: `This option changes the interpretations of options which follow this one. After the --use-backslash option, any backslash in an option string is removed and the following character is taken literally. This allows a dollar sign to be included in option strings`, - }, - { - name: "--use-color", - description: `Enables the colored text in various places. The -D option can + }, + { + name: "--use-color", + description: `Enables the colored text in various places. The -D option can be used to change the colors. Colored text works only if the terminal supports ANSI color escape sequences (as defined in ECMA-48 SGR; see https://www.ecma-international.org/publications-and- standards/standards/ecma-48)`, - }, - { - name: "--wheel-lines", - args: { name: "n" }, - description: `Set the number of lines to scroll when the mouse wheel is rolled`, - }, - ], + }, + { + name: "--wheel-lines", + args: { name: "n" }, + description: `Set the number of lines to scroll when the mouse wheel is rolled`, + }, + ], }; export default completionSpec; diff --git a/extensions/terminal-suggest/src/completions/upstream/mkdir.ts b/extensions/terminal-suggest/src/completions/upstream/mkdir.ts index 90a6530c3bda..2f9ff425d834 100644 --- a/extensions/terminal-suggest/src/completions/upstream/mkdir.ts +++ b/extensions/terminal-suggest/src/completions/upstream/mkdir.ts @@ -1,37 +1,37 @@ const completionSpec: Fig.Spec = { - name: "mkdir", - description: "Make directories", - args: { - name: "directory name", - template: "folders", - suggestCurrentToken: true, - }, - options: [ - { - name: ["-m", "--mode"], - description: "Set file mode (as in chmod), not a=rwx - umask", - args: { name: "MODE" }, - }, - { - name: ["-p", "--parents"], - description: "No error if existing, make parent directories as needed", - }, - { - name: ["-v", "--verbose"], - description: "Print a message for each created directory", - }, - { - name: ["-Z", "--context"], - description: - "Set the SELinux security context of each created directory to CTX", - args: { name: "CTX" }, - }, - { name: "--help", description: "Display this help and exit" }, - { - name: "--version", - description: "Output version information and exit", - }, - ], + name: "mkdir", + description: "Make directories", + args: { + name: "directory name", + template: "folders", + suggestCurrentToken: true, + }, + options: [ + { + name: ["-m", "--mode"], + description: "Set file mode (as in chmod), not a=rwx - umask", + args: { name: "MODE" }, + }, + { + name: ["-p", "--parents"], + description: "No error if existing, make parent directories as needed", + }, + { + name: ["-v", "--verbose"], + description: "Print a message for each created directory", + }, + { + name: ["-Z", "--context"], + description: + "Set the SELinux security context of each created directory to CTX", + args: { name: "CTX" }, + }, + { name: "--help", description: "Display this help and exit" }, + { + name: "--version", + description: "Output version information and exit", + }, + ], }; export default completionSpec; diff --git a/extensions/terminal-suggest/src/completions/upstream/more.ts b/extensions/terminal-suggest/src/completions/upstream/more.ts index a70c8ee9078d..6a9d6e820661 100644 --- a/extensions/terminal-suggest/src/completions/upstream/more.ts +++ b/extensions/terminal-suggest/src/completions/upstream/more.ts @@ -1,55 +1,55 @@ const completionSpec: Fig.Spec = { - name: "more", - description: "Opposite of less", - options: [ - { - name: ["-d", "--silent"], - description: - "Prompt with '[Press space to continue, 'q' to quit.]', and display '[Press 'h' for instructions.]' instead of ringing the bell when an illegal key is pressed", - }, - { - name: ["-l", "--logical"], - description: "Do not pause after any line containing a ^L (form feed)", - }, - { - name: ["-f", "--no-pause"], - description: "Count logical lines, rather than screen lines", - }, - { - name: ["-p", "--print-over"], - description: "Instead, clear the whole screen and then display the text", - }, - { - name: ["-c", "--clean-print"], - description: - "Instead, paint each screen from the top, clearing the remainder of each line as it is displayed", - }, - { - name: ["-s", "--squeeze"], - description: "Squeeze multiple blank lines into one", - }, - { - name: ["-u", "--plain"], - description: "Silently ignored as backwards compatibility", - }, - { - name: ["-n", "--lines"], - description: "Specify the number of lines per screenful", - args: { name: "n" }, - }, - { - name: "--help", - description: "Display help text", - }, - { - name: ["-V", "--version"], - description: "Display version information", - }, - ], - args: { - isVariadic: true, - template: "filepaths", - }, + name: "more", + description: "Opposite of less", + options: [ + { + name: ["-d", "--silent"], + description: + "Prompt with '[Press space to continue, 'q' to quit.]', and display '[Press 'h' for instructions.]' instead of ringing the bell when an illegal key is pressed", + }, + { + name: ["-l", "--logical"], + description: "Do not pause after any line containing a ^L (form feed)", + }, + { + name: ["-f", "--no-pause"], + description: "Count logical lines, rather than screen lines", + }, + { + name: ["-p", "--print-over"], + description: "Instead, clear the whole screen and then display the text", + }, + { + name: ["-c", "--clean-print"], + description: + "Instead, paint each screen from the top, clearing the remainder of each line as it is displayed", + }, + { + name: ["-s", "--squeeze"], + description: "Squeeze multiple blank lines into one", + }, + { + name: ["-u", "--plain"], + description: "Silently ignored as backwards compatibility", + }, + { + name: ["-n", "--lines"], + description: "Specify the number of lines per screenful", + args: { name: "n" }, + }, + { + name: "--help", + description: "Display help text", + }, + { + name: ["-V", "--version"], + description: "Display version information", + }, + ], + args: { + isVariadic: true, + template: "filepaths", + }, }; export default completionSpec; diff --git a/extensions/terminal-suggest/src/completions/upstream/mv.ts b/extensions/terminal-suggest/src/completions/upstream/mv.ts index bbf98bf49031..2109b0dd0fff 100644 --- a/extensions/terminal-suggest/src/completions/upstream/mv.ts +++ b/extensions/terminal-suggest/src/completions/upstream/mv.ts @@ -1,40 +1,40 @@ const completionSpec: Fig.Spec = { - name: "mv", - description: "Move & rename files and folders", - args: [ - { - name: "source", - isVariadic: true, - template: ["filepaths", "folders"], - }, - { - name: "target", - template: ["filepaths", "folders"], - }, - ], - options: [ - { - name: "-f", - description: - "Do not prompt for confirmation before overwriting the destination path", - exclusiveOn: ["-i", "-n"], - }, - { - name: "-i", - description: - "Cause mv to write a prompt to standard error before moving a file that would overwrite an existing file", - exclusiveOn: ["-f", "-n"], - }, - { - name: "-n", - description: "Do not overwrite existing file", - exclusiveOn: ["-f", "-i"], - }, - { - name: "-v", - description: "Cause mv to be verbose, showing files after they are moved", - }, - ], + name: "mv", + description: "Move & rename files and folders", + args: [ + { + name: "source", + isVariadic: true, + template: ["filepaths", "folders"], + }, + { + name: "target", + template: ["filepaths", "folders"], + }, + ], + options: [ + { + name: "-f", + description: + "Do not prompt for confirmation before overwriting the destination path", + exclusiveOn: ["-i", "-n"], + }, + { + name: "-i", + description: + "Cause mv to write a prompt to standard error before moving a file that would overwrite an existing file", + exclusiveOn: ["-f", "-n"], + }, + { + name: "-n", + description: "Do not overwrite existing file", + exclusiveOn: ["-f", "-i"], + }, + { + name: "-v", + description: "Cause mv to be verbose, showing files after they are moved", + }, + ], }; export default completionSpec; diff --git a/extensions/terminal-suggest/src/completions/upstream/nano.ts b/extensions/terminal-suggest/src/completions/upstream/nano.ts index 6064072e8731..caa84c34d658 100644 --- a/extensions/terminal-suggest/src/completions/upstream/nano.ts +++ b/extensions/terminal-suggest/src/completions/upstream/nano.ts @@ -1,9 +1,9 @@ const completionSpec: Fig.Spec = { - name: "nano", - description: "Nano's ANOther editor, an enhanced free Pico clone", - args: { - template: "filepaths", - }, + name: "nano", + description: "Nano's ANOther editor, an enhanced free Pico clone", + args: { + template: "filepaths", + }, }; export default completionSpec; diff --git a/extensions/terminal-suggest/src/completions/upstream/node.ts b/extensions/terminal-suggest/src/completions/upstream/node.ts index bc261c5affe4..181104d80bd7 100644 --- a/extensions/terminal-suggest/src/completions/upstream/node.ts +++ b/extensions/terminal-suggest/src/completions/upstream/node.ts @@ -1,4 +1,4 @@ -// import { filepaths } from "@fig/autocomplete-generators"; +import { filepaths } from '../../fig/autocomplete-tools/generators' const completionSpec: Fig.Subcommand = { name: "node", @@ -6,11 +6,11 @@ const completionSpec: Fig.Subcommand = { args: { name: "node script", isScript: true, - template: 'folders', - // generators: filepaths({ - // extensions: ["mjs", "js", "cjs"], - // editFileSuggestions: { priority: 76 }, - // }), + template: 'filepaths', + generators: filepaths({ + extensions: ["mjs", "js", "cjs"], + editFileSuggestions: { priority: 76 }, + }), }, options: [ { @@ -83,423 +83,422 @@ const completionSpec: Fig.Subcommand = { "Follows symlinks to directories when examining source code and templates for translation strings", }, ], - // generateSpec: async (tokens, executeShellCommand) => { - // const isAdonisJsonPresentCommand = "test -f .adonisrc.json"; - // if ( - // ( - // await executeShellCommand({ - // command: "bash", - // args: ["-c", "isAdonisJsonPresentCommand"], - // }) - // ).status === 0 - // ) { - // return { - // name: "node", - // subcommands: [ - // { - // name: "ace", - // description: "Run AdonisJS command-line", - // options: [ - // { - // name: ["-h", "--help"], - // description: "Display AdonisJS Ace help", - // }, - // { - // name: ["-v", "--version"], - // description: "Display AdonisJS version", - // }, - // ], - // subcommands: [ - // { - // name: "build", - // description: - // "Compile project from Typescript to Javascript. Also compiles the frontend assets if using webpack encore", - // options: [ - // { - // name: ["-prod", "--production"], - // description: "Build for production", - // }, - // { - // name: "--assets", - // description: - // "Build frontend assets when webpack encore is installed", - // }, - // { - // name: "--no-assets", - // description: "Disable building assets", - // }, - // { - // name: "--ignore-ts-errors", - // description: - // "Ignore typescript errors and complete the build process", - // }, - // { - // name: "--tsconfig", - // description: - // "Path to the TypeScript project configuration file", - // args: { - // name: "path", - // description: "Path to tsconfig.json", - // }, - // }, - // { - // name: "--encore-args", - // requiresSeparator: true, - // insertValue: "--encore-args='{cursor}'", - // description: - // "CLI options to pass to the encore command line", - // }, - // { - // name: "--client", - // args: { - // name: "name", - // }, - // description: - // "Select the package manager to decide which lock file to copy to the build folder", - // }, - // ], - // }, - // { - // name: ["configure", "invoke"], - // description: "Configure a given AdonisJS package", - // args: { - // name: "name", - // description: "Name of the package you want to configure", - // }, - // subcommands: [ - // { - // name: "@adonisjs/auth", - // description: "Trigger auto configuring auth package", - // }, - // { - // name: "@adonisjs/shield", - // description: "Trigger auto configuring shield package", - // }, - // { - // name: "@adonisjs/redis", - // description: "Trigger auto configuring redis package", - // }, - // { - // name: "@adonisjs/mail", - // description: "Trigger auto configuring mail package", - // }, - // ], - // }, - // { - // name: "repl", - // description: "Start a new REPL session", - // }, - // { - // name: "serve", - // description: - // "Start the AdonisJS HTTP server, along with the file watcher. Also starts the webpack dev server when webpack encore is installed", - // options: [ - // { - // name: "--assets", - // description: - // "Start webpack dev server when encore is installed", - // }, - // { - // name: "--no-assets", - // description: "Disable webpack dev server", - // }, - // { - // name: ["-w", "--watch"], - // description: - // "Watch for file changes and re-start the HTTP server on change", - // }, - // { - // name: ["-p", "--poll"], - // description: - // "Detect file changes by polling files instead of listening to filesystem events", - // }, - // { - // name: "--node-args", - // requiresSeparator: true, - // insertValue: "--node-args='{cursor}'", - // description: "CLI options to pass to the node command line", - // }, - // { - // name: "--encore-args", - // requiresSeparator: true, - // insertValue: "--encore-args='{cursor}'", - // description: - // "CLI options to pass to the encore command line", - // }, - // ], - // }, - // { - // name: "db:seed", - // description: "Execute database seeder files", - // options: [ - // { - // name: ["-c", "--connection"], - // description: - // "Define a custom database connection for the seeders", - // args: { - // name: "name", - // }, - // }, - // { - // name: ["-i", "--interactive"], - // description: "Run seeders in interactive mode", - // }, - // { - // name: ["-f", "--files"], - // args: { - // name: "file", - // isVariadic: true, - // template: "filepaths", - // }, - // description: - // "Define a custom set of seeders files names to run", - // }, - // ], - // }, - // { - // name: "dump:rcfile", - // description: - // "Dump contents of .adonisrc.json file along with defaults", - // }, - // { - // name: "generate:key", - // description: "Generate a new APP_KEY secret", - // }, - // { - // name: "generate:manifest", - // description: - // "Generate ace commands manifest file. Manifest file speeds up commands lookup", - // }, - // { - // name: "list:routes", - // description: "List application routes", - // }, - // { - // name: "make:command", - // description: "Make a new ace command", - // }, - // { - // name: "make:controller", - // description: "Make a new HTTP controller", - // args: { - // name: "name", - // description: "Name of the controller class", - // }, - // options: [ - // { - // name: ["-r", "--resource"], - // description: - // "Add resourceful methods to the controller class", - // }, - // { - // name: ["-e", "--exact"], - // description: - // "Create the controller with the exact name as provided", - // }, - // ], - // }, - // { - // name: "make:exception", - // description: "Make a new custom exception class", - // }, - // { - // name: "make:listener", - // description: "Make a new event listener class", - // }, - // { - // name: "make:mailer", - // description: "Make a new mailer class", - // args: { - // name: "name", - // description: "Mailer class name", - // }, - // }, - // { - // name: "make:middleware", - // description: "Make a new middleware", - // args: { - // name: "name", - // description: "Middleware class name", - // }, - // }, - // { - // name: "make:migration", - // description: "Make a new migration file", - // args: { - // name: "name", - // description: "Name of the migration file", - // }, - // options: [ - // { - // name: "--connection", - // description: - // "The connection flag is used to lookup the directory for the migration file", - // args: { - // name: "name", - // }, - // }, - // { - // name: "--folder", - // description: "Pre-select a migration directory", - // args: { - // name: "name", - // template: "filepaths", - // }, - // }, - // { - // name: "--create", - // description: - // "Define the table name for creating a new table", - // args: { - // name: "name", - // }, - // }, - // { - // name: "--table", - // description: - // "Define the table name for altering an existing table", - // args: { - // name: "name", - // }, - // }, - // ], - // }, - // { - // name: "make:model", - // description: "Make a new Lucid model", - // args: { - // name: "name", - // description: "Name of the model class", - // }, - // options: [ - // { - // name: ["-m", "--migration"], - // description: "Generate the migration for the model", - // }, - // { - // name: ["-c", "--controller"], - // description: "Generate the controller for the model", - // }, - // ], - // }, - // { - // name: "make:prldfile", - // description: "Make a new preload file", - // subcommands: [ - // { - // name: "events", - // description: "Make events preload file", - // }, - // ], - // }, - // { - // name: "make:provider", - // description: "Make a new provider class", - // }, - // { - // name: "make:seeder", - // description: "Make a new Seeder file", - // args: { - // name: "name", - // description: "Name of the seeder class", - // }, - // }, - // { - // name: "make:validator", - // description: "Make a new validator", - // args: { - // name: "name", - // description: "Name of the validator class", - // }, - // options: [ - // { - // name: ["-e", "--exact"], - // description: - // "Create the validator with the exact name as provided", - // }, - // ], - // }, - // { - // name: "make:view", - // description: "Make a new view template", - // args: { - // name: "name", - // description: "Name of the view", - // }, - // options: [ - // { - // name: ["-e", "--exact"], - // description: - // "Create the template file with the exact name as provided", - // }, - // ], - // }, - // { - // name: "migration:rollback", - // description: "Rollback migrations to a given batch number", - // options: [ - // { - // name: ["-c", "--connection"], - // description: "Define a custom database connection", - // args: { - // name: "name", - // }, - // }, - // { - // name: "--force", - // description: - // "Explicitly force to run migrations in production", - // isDangerous: true, - // }, - // { - // name: "--dry-run", - // description: - // "Print SQL queries, instead of running the migrations", - // }, - // { - // name: "--batch", - // args: { - // name: "number", - // description: "Use 0 to rollback to initial state", - // }, - // description: "Define custom batch number for rollback", - // }, - // ], - // }, - // { - // name: "migration:run", - // description: "Run pending migrations", - // options: [ - // { - // name: ["-c", "--connection"], - // description: "Define a custom database connection", - // args: { - // name: "name", - // }, - // }, - // { - // name: "--force", - // description: - // "Explicitly force to run migrations in production", - // isDangerous: true, - // }, - // { - // name: "--dry-run", - // description: - // "Print SQL queries, instead of running the migrations", - // }, - // ], - // }, - // { - // name: "migration:status", - // description: "Check migrations current status", - // }, - // ], - // }, - // ], - // }; - // } - // }, + generateSpec: async (tokens, executeShellCommand) => { + if ( + ( + await executeShellCommand({ + command: "bash", + args: ["-c", "isAdonisJsonPresentCommand"], + }) + ).status === 0 + ) { + return { + name: "node", + subcommands: [ + { + name: "ace", + description: "Run AdonisJS command-line", + options: [ + { + name: ["-h", "--help"], + description: "Display AdonisJS Ace help", + }, + { + name: ["-v", "--version"], + description: "Display AdonisJS version", + }, + ], + subcommands: [ + { + name: "build", + description: + "Compile project from Typescript to Javascript. Also compiles the frontend assets if using webpack encore", + options: [ + { + name: ["-prod", "--production"], + description: "Build for production", + }, + { + name: "--assets", + description: + "Build frontend assets when webpack encore is installed", + }, + { + name: "--no-assets", + description: "Disable building assets", + }, + { + name: "--ignore-ts-errors", + description: + "Ignore typescript errors and complete the build process", + }, + { + name: "--tsconfig", + description: + "Path to the TypeScript project configuration file", + args: { + name: "path", + description: "Path to tsconfig.json", + }, + }, + { + name: "--encore-args", + requiresSeparator: true, + insertValue: "--encore-args='{cursor}'", + description: + "CLI options to pass to the encore command line", + }, + { + name: "--client", + args: { + name: "name", + }, + description: + "Select the package manager to decide which lock file to copy to the build folder", + }, + ], + }, + { + name: ["configure", "invoke"], + description: "Configure a given AdonisJS package", + args: { + name: "name", + description: "Name of the package you want to configure", + }, + subcommands: [ + { + name: "@adonisjs/auth", + description: "Trigger auto configuring auth package", + }, + { + name: "@adonisjs/shield", + description: "Trigger auto configuring shield package", + }, + { + name: "@adonisjs/redis", + description: "Trigger auto configuring redis package", + }, + { + name: "@adonisjs/mail", + description: "Trigger auto configuring mail package", + }, + ], + }, + { + name: "repl", + description: "Start a new REPL session", + }, + { + name: "serve", + description: + "Start the AdonisJS HTTP server, along with the file watcher. Also starts the webpack dev server when webpack encore is installed", + options: [ + { + name: "--assets", + description: + "Start webpack dev server when encore is installed", + }, + { + name: "--no-assets", + description: "Disable webpack dev server", + }, + { + name: ["-w", "--watch"], + description: + "Watch for file changes and re-start the HTTP server on change", + }, + { + name: ["-p", "--poll"], + description: + "Detect file changes by polling files instead of listening to filesystem events", + }, + { + name: "--node-args", + requiresSeparator: true, + insertValue: "--node-args='{cursor}'", + description: "CLI options to pass to the node command line", + }, + { + name: "--encore-args", + requiresSeparator: true, + insertValue: "--encore-args='{cursor}'", + description: + "CLI options to pass to the encore command line", + }, + ], + }, + { + name: "db:seed", + description: "Execute database seeder files", + options: [ + { + name: ["-c", "--connection"], + description: + "Define a custom database connection for the seeders", + args: { + name: "name", + }, + }, + { + name: ["-i", "--interactive"], + description: "Run seeders in interactive mode", + }, + { + name: ["-f", "--files"], + args: { + name: "file", + isVariadic: true, + template: "filepaths", + }, + description: + "Define a custom set of seeders files names to run", + }, + ], + }, + { + name: "dump:rcfile", + description: + "Dump contents of .adonisrc.json file along with defaults", + }, + { + name: "generate:key", + description: "Generate a new APP_KEY secret", + }, + { + name: "generate:manifest", + description: + "Generate ace commands manifest file. Manifest file speeds up commands lookup", + }, + { + name: "list:routes", + description: "List application routes", + }, + { + name: "make:command", + description: "Make a new ace command", + }, + { + name: "make:controller", + description: "Make a new HTTP controller", + args: { + name: "name", + description: "Name of the controller class", + }, + options: [ + { + name: ["-r", "--resource"], + description: + "Add resourceful methods to the controller class", + }, + { + name: ["-e", "--exact"], + description: + "Create the controller with the exact name as provided", + }, + ], + }, + { + name: "make:exception", + description: "Make a new custom exception class", + }, + { + name: "make:listener", + description: "Make a new event listener class", + }, + { + name: "make:mailer", + description: "Make a new mailer class", + args: { + name: "name", + description: "Mailer class name", + }, + }, + { + name: "make:middleware", + description: "Make a new middleware", + args: { + name: "name", + description: "Middleware class name", + }, + }, + { + name: "make:migration", + description: "Make a new migration file", + args: { + name: "name", + description: "Name of the migration file", + }, + options: [ + { + name: "--connection", + description: + "The connection flag is used to lookup the directory for the migration file", + args: { + name: "name", + }, + }, + { + name: "--folder", + description: "Pre-select a migration directory", + args: { + name: "name", + template: "filepaths", + }, + }, + { + name: "--create", + description: + "Define the table name for creating a new table", + args: { + name: "name", + }, + }, + { + name: "--table", + description: + "Define the table name for altering an existing table", + args: { + name: "name", + }, + }, + ], + }, + { + name: "make:model", + description: "Make a new Lucid model", + args: { + name: "name", + description: "Name of the model class", + }, + options: [ + { + name: ["-m", "--migration"], + description: "Generate the migration for the model", + }, + { + name: ["-c", "--controller"], + description: "Generate the controller for the model", + }, + ], + }, + { + name: "make:prldfile", + description: "Make a new preload file", + subcommands: [ + { + name: "events", + description: "Make events preload file", + }, + ], + }, + { + name: "make:provider", + description: "Make a new provider class", + }, + { + name: "make:seeder", + description: "Make a new Seeder file", + args: { + name: "name", + description: "Name of the seeder class", + }, + }, + { + name: "make:validator", + description: "Make a new validator", + args: { + name: "name", + description: "Name of the validator class", + }, + options: [ + { + name: ["-e", "--exact"], + description: + "Create the validator with the exact name as provided", + }, + ], + }, + { + name: "make:view", + description: "Make a new view template", + args: { + name: "name", + description: "Name of the view", + }, + options: [ + { + name: ["-e", "--exact"], + description: + "Create the template file with the exact name as provided", + }, + ], + }, + { + name: "migration:rollback", + description: "Rollback migrations to a given batch number", + options: [ + { + name: ["-c", "--connection"], + description: "Define a custom database connection", + args: { + name: "name", + }, + }, + { + name: "--force", + description: + "Explicitly force to run migrations in production", + isDangerous: true, + }, + { + name: "--dry-run", + description: + "Print SQL queries, instead of running the migrations", + }, + { + name: "--batch", + args: { + name: "number", + description: "Use 0 to rollback to initial state", + }, + description: "Define custom batch number for rollback", + }, + ], + }, + { + name: "migration:run", + description: "Run pending migrations", + options: [ + { + name: ["-c", "--connection"], + description: "Define a custom database connection", + args: { + name: "name", + }, + }, + { + name: "--force", + description: + "Explicitly force to run migrations in production", + isDangerous: true, + }, + { + name: "--dry-run", + description: + "Print SQL queries, instead of running the migrations", + }, + ], + }, + { + name: "migration:status", + description: "Check migrations current status", + }, + ], + }, + ], + }; + } + }, }; export default completionSpec; diff --git a/extensions/terminal-suggest/src/completions/upstream/npm.ts b/extensions/terminal-suggest/src/completions/upstream/npm.ts index b48d8126444c..aa142e056611 100644 --- a/extensions/terminal-suggest/src/completions/upstream/npm.ts +++ b/extensions/terminal-suggest/src/completions/upstream/npm.ts @@ -16,80 +16,82 @@ const atsInStr = (s: string) => (s.match(/@/g) || []).length; export const createNpmSearchHandler = (keywords?: string[]) => - async ( - context: string[], - executeShellCommand: Fig.ExecuteCommandFunction, - shellContext: Fig.ShellContext - ): Promise => { - const searchTerm = context[context.length - 1]; - if (searchTerm === "") { - return []; - } - // Add optional keyword parameter - const keywordParameter = - keywords && keywords.length > 0 ? `+keywords:${keywords.join(",")}` : ""; + async ( + context: string[], + executeShellCommand: Fig.ExecuteCommandFunction, + shellContext: Fig.ShellContext + ): Promise => { + const searchTerm = context[context.length - 1]; + if (searchTerm === "") { + return []; + } + // Add optional keyword parameter + const keywordParameter = + keywords && keywords.length > 0 ? `+keywords:${keywords.join(",")}` : ""; - const queryPackagesUrl = keywordParameter - ? `https://api.npms.io/v2/search?size=20&q=${searchTerm}${keywordParameter}` - : `https://api.npms.io/v2/search/suggestions?q=${searchTerm}&size=20`; + const queryPackagesUrl = keywordParameter + ? `https://api.npms.io/v2/search?size=20&q=${searchTerm}${keywordParameter}` + : `https://api.npms.io/v2/search/suggestions?q=${searchTerm}&size=20`; - // Query the API with the package name - const queryPackages = [ - "-s", - "-H", - "Accept: application/json", - queryPackagesUrl, - ]; - // We need to remove the '@' at the end of the searchTerm before querying versions - const queryVersions = [ - "-s", - "-H", - "Accept: application/vnd.npm.install-v1+json", - `https://registry.npmjs.org/${searchTerm.slice(0, -1)}`, - ]; - // If the end of our token is '@', then we want to generate version suggestions - // Otherwise, we want packages - const out = (query: string) => - executeShellCommand({ - command: "curl", - args: query[query.length - 1] === "@" ? queryVersions : queryPackages, - }); - // If our token starts with '@', then a 2nd '@' tells us we want - // versions. - // Otherwise, '@' anywhere else in the string will indicate the same. - const shouldGetVersion = searchTerm.startsWith("@") - ? atsInStr(searchTerm) > 1 - : searchTerm.includes("@"); + // Query the API with the package name + const queryPackages = [ + "-s", + "-H", + "Accept: application/json", + queryPackagesUrl, + ]; + // We need to remove the '@' at the end of the searchTerm before querying versions + const queryVersions = [ + "-s", + "-H", + "Accept: application/vnd.npm.install-v1+json", + `https://registry.npmjs.org/${searchTerm.slice(0, -1)}`, + ]; + // If the end of our token is '@', then we want to generate version suggestions + // Otherwise, we want packages + const out = (query: string) => + executeShellCommand({ + command: "curl", + args: query[query.length - 1] === "@" ? queryVersions : queryPackages, + }); + // If our token starts with '@', then a 2nd '@' tells us we want + // versions. + // Otherwise, '@' anywhere else in the string will indicate the same. + const shouldGetVersion = searchTerm.startsWith("@") + ? atsInStr(searchTerm) > 1 + : searchTerm.includes("@"); - try { - const data = JSON.parse((await out(searchTerm)).stdout); - if (shouldGetVersion) { - // create dist tags suggestions - const versions = Object.entries(data["dist-tags"] || {}).map( - ([key, value]) => ({ - name: key, - description: value, - }) - ) as Fig.Suggestion[]; - // create versions - versions.push( - ...Object.keys(data.versions) - .map((version) => ({ name: version }) as Fig.Suggestion) - .reverse() - ); - return versions; - } + try { + const data = JSON.parse((await out(searchTerm)).stdout); + if (shouldGetVersion) { + // create dist tags suggestions + const versions = Object.entries(data["dist-tags"] || {}).map( + ([key, value]) => ({ + name: key, + description: value, + }) + ) as Fig.Suggestion[]; + // create versions + versions.push( + ...Object.keys(data.versions) + .map((version) => ({ name: version }) as Fig.Suggestion) + .reverse() + ); + return versions; + } - const results = keywordParameter ? data.results : data; - return results.map((item: any) => ({ + const results = keywordParameter ? data.results : data; + return results.map( + (item: { package: { name: string; description: string } }) => ({ name: item.package.name, description: item.package.description, - })) as Fig.Suggestion[]; - } catch (error) { - console.error({ error }); - return []; - } - }; + }) + ) as Fig.Suggestion[]; + } catch (error) { + console.error({ error }); + return []; + } + }; // GENERATORS export const npmSearchGenerator: Fig.Generator = { diff --git a/extensions/terminal-suggest/src/completions/upstream/npx.ts b/extensions/terminal-suggest/src/completions/upstream/npx.ts deleted file mode 100644 index 4143ed790d1b..000000000000 --- a/extensions/terminal-suggest/src/completions/upstream/npx.ts +++ /dev/null @@ -1,317 +0,0 @@ -// import autocannon from "./autocannon"; - -export const npxSuggestions: Fig.Suggestion[] = [ - // { - // name: autocannon.name, - // ...("icon" in autocannon && { icon: autocannon.icon }), - // }, - { - name: "vite", - icon: "https://vitejs.dev/logo.svg", - }, - { - name: "babel", - icon: "https://raw.githubusercontent.com/babel/logo/master/babel.png", - }, - { - name: "create-react-native-app", - icon: "https://reactnative.dev/img/pwa/manifest-icon-512.png", - }, - { - name: "react-native", - icon: "https://reactnative.dev/img/pwa/manifest-icon-512.png", - }, - { - name: "tailwindcss", - icon: "https://tailwindcss.com/favicons/favicon-32x32.png", - }, - { - name: "next", - icon: "https://nextjs.org/static/favicon/favicon-16x16.png", - }, - { - name: "nuxi", - icon: "https://raw.githubusercontent.com/nuxt/framework/main/docs/public/icon.png", - }, - { - name: "gltfjsx", - icon: "https://raw.githubusercontent.com/pmndrs/branding/master/logo.svg", - }, - { - name: "prisma", - icon: "https://raw.githubusercontent.com/prisma/docs/main/src/images/favicon-16x16.png", - }, - { - name: "eslint", - icon: "https://raw.githubusercontent.com/eslint/eslint.org/main/src/static/icon-512.png", - }, - { - name: "prettier", - icon: "https://prettier.io/icon.png", - }, - { - name: "tsc", - icon: "https://upload.wikimedia.org/wikipedia/commons/thumb/4/4c/Typescript_logo_2020.svg/240px-Typescript_logo_2020.svg.png", - }, - { - name: "typeorm", - icon: "https://avatars.githubusercontent.com/u/20165699?s=200&v=4", - }, - // { - // name: "fig-teams", - // icon: "https://fig.io/icons/fig-light.png", - // }, - { - name: "@withfig/autocomplete-tools", - icon: "https://fig.io/icons/fig-light.png", - }, - { - name: "create-completion-spec", - icon: "https://fig.io/icons/fig-light.png", - }, - { - name: "@fig/publish-spec-to-team", - icon: "https://fig.io/icons/fig-light.png", - }, - { - name: "fig-teams@latest", - icon: "https://fig.io/icons/fig-light.png", - }, - { - name: "create-next-app", - icon: "https://nextjs.org/static/favicon/favicon-16x16.png", - }, - { - name: "create-t3-app", - icon: "https://create.t3.gg/favicon.svg", - }, - { - name: "create-discord-bot", - icon: "https://discordjs.dev/favicon-32x32.png", - }, - { - name: "create-video", - icon: "https://raw.githubusercontent.com/remotion-dev/remotion/main/packages/docs/static/img/logo-small.png", - }, - { - name: "remotion", - icon: "https://raw.githubusercontent.com/remotion-dev/remotion/main/packages/docs/static/img/logo-small.png", - }, - { - name: "create-remix", - icon: "https://remix.run/favicon-light.1.png", - }, - { - name: "remix", - icon: "https://remix.run/favicon-light.1.png", - }, - { - name: "playwright", - icon: "https://playwright.dev/img/playwright-logo.svg", - }, - { - name: "ignite-cli", - icon: "🔥", - }, - { - name: "vsce", - }, - { - name: "degit", - icon: "fig://icon?type=git", - }, - { - name: "@preset/cli", - icon: "https://raw.githubusercontent.com/preset/preset/main/.github/assets/logo.svg", - }, - { - name: "mikro-orm", - icon: "https://raw.githubusercontent.com/mikro-orm/mikro-orm/master/docs/static/img/favicon.ico", - }, - { - name: "pod-install", - }, - { - name: "capacitor", - icon: "https://capacitorjs.com/docs/img/meta/favicon.png", - }, - { - name: "cap", - icon: "https://capacitorjs.com/docs/img/meta/favicon.png", - }, - { - name: "@magnolia/cli", - icon: "https://avatars.githubusercontent.com/u/25686615?s=200&v=4", - }, - { - name: "stencil", - icon: "https://stenciljs.com/assets/icon/favicon.ico", - }, - { - name: "swagger-typescript-api", - icon: "https://static1.smartbear.co/swagger/media/assets/swagger_fav.png", - }, - { - name: "sta", - icon: "https://static1.smartbear.co/swagger/media/assets/swagger_fav.png", - }, - { - name: "@wordpress/create-block", - icon: "https://s1.wp.com/i/webclip.png", - }, - { - name: "astro", - icon: "https://astro.build/favicon.svg", - }, - { - name: "ampx", - icon: "https://raw.githubusercontent.com/aws-amplify/docs/refs/heads/main/public/favicon.ico", - }, -]; - -const completionSpec: Fig.Spec = { - name: "npx", - description: "Execute binaries from npm packages", - args: { - name: "command", - isCommand: true, - generators: { - script: [ - "bash", - "-c", - "until [[ -d node_modules/ ]] || [[ $PWD = '/' ]]; do cd ..; done; ls -1 node_modules/.bin/", - ], - postProcess: function (out) { - const cli = [...npxSuggestions].reduce( - (acc: any, { name }) => [...acc, name], - [] - ); - return out - .split("\n") - .filter((name) => !cli.includes(name)) - .map((name) => ({ - name, - icon: "fig://icon?type=command", - loadSpec: name, - })); - }, - }, - suggestions: [...npxSuggestions], - isOptional: true, - }, - - options: [ - { - name: ["--package", "-p"], - description: "Package to be installed", - args: { - name: "package", - }, - }, - { - name: "--cache", - args: { - name: "path", - template: "filepaths", - }, - description: "Location of the npm cache", - }, - { - name: "--always-spawn", - description: "Always spawn a child process to execute the command", - }, - { - name: "-y", - description: "Execute npx command without prompting for confirmation", - }, - { - description: "Skip installation if a package is missing", - name: "--no-install", - }, - { - args: { - name: "path", - template: "filepaths", - }, - description: "Path to user npmrc", - name: "--userconfig", - }, - { - name: ["--call", "-c"], - args: { - name: "script", - }, - description: "Execute string as if inside `npm run-script`", - }, - { - name: ["--shell", "-s"], - description: "Shell to execute the command with, if any", - args: { - name: "shell", - suggestions: [ - { - name: "bash", - }, - { - name: "fish", - }, - { - name: "zsh", - }, - ], - }, - }, - { - args: { - name: "shell-fallback", - suggestions: [ - { - name: "bash", - }, - { - name: "fish", - }, - { - name: "zsh", - }, - ], - }, - name: "--shell-auto-fallback", - description: - 'Generate shell code to use npx as the "command not found" fallback', - }, - { - name: "--ignore-existing", - description: - "Ignores existing binaries in $PATH, or in the localproject. This forces npx to do a temporary install and use the latest version", - }, - { - name: ["--quiet", "-q"], - description: - "Suppress output from npx itself. Subcommands will not be affected", - }, - { - name: "--npm", - args: { - name: "path to binary", - template: "filepaths", - }, - description: "Npm binary to use for internal operations", - }, - { - args: {}, - description: "Extra node argument when calling a node binary", - name: ["--node-arg", "-n"], - }, - { - description: "Show version number", - name: ["--version", "-v"], - }, - { - description: "Show help", - name: ["--help", "-h"], - }, - ], -}; - -export default completionSpec; diff --git a/extensions/terminal-suggest/src/completions/upstream/nvm.ts b/extensions/terminal-suggest/src/completions/upstream/nvm.ts index 843ffef57de6..6adafbcb1745 100644 --- a/extensions/terminal-suggest/src/completions/upstream/nvm.ts +++ b/extensions/terminal-suggest/src/completions/upstream/nvm.ts @@ -28,10 +28,6 @@ const args: Fig.Arg = { isVariadic: true, }; -// const pattern: Fig.Arg = { -// name: "pattern", -// }; - const name: Fig.Arg = { name: "name", }; diff --git a/extensions/terminal-suggest/src/completions/upstream/pnpm.ts b/extensions/terminal-suggest/src/completions/upstream/pnpm.ts index 1710549572f1..c3be72676017 100644 --- a/extensions/terminal-suggest/src/completions/upstream/pnpm.ts +++ b/extensions/terminal-suggest/src/completions/upstream/pnpm.ts @@ -25,7 +25,7 @@ const searchBranches: Fig.Generator = { if (parts[0] == "*") { // Current branch. return { - name: elm.replace("*", "").trim(), + name: elm.replace(/^\*\s*/, "").trim(), description: "Current branch", icon: "⭐️", }; @@ -990,7 +990,8 @@ const completionSpec: Fig.Spec = { const { script, postProcess } = dependenciesGenerator as Fig.Generator & { script: string[]; }; - if (!postProcess) { + + if (postProcess === undefined) { return undefined; } @@ -1002,13 +1003,12 @@ const completionSpec: Fig.Spec = { }) ).stdout, tokens - )?.map((e: any) => e.name as string); - if (!packages) { - return undefined; - } + ) + ?.filter((e) => e !== null) + .map(({ name }) => name as string); const subcommands = packages - .filter((name) => nodeClis.has(name)) + ?.filter((name) => nodeClis.has(name)) .map((name) => ({ name, loadSpec: name, diff --git a/extensions/terminal-suggest/src/completions/upstream/ps.ts b/extensions/terminal-suggest/src/completions/upstream/ps.ts index 37c34815a8c8..17dd6623679b 100644 --- a/extensions/terminal-suggest/src/completions/upstream/ps.ts +++ b/extensions/terminal-suggest/src/completions/upstream/ps.ts @@ -1,153 +1,153 @@ const completionSpec: Fig.Spec = { - name: "ps", - description: "Report a snapshot of the current processes", - options: [ - { name: ["-A", "-e"], description: "Select all processes" }, - { - name: "-a", - description: "Select all processes except both session leaders", - args: { name: "getsid" }, - }, - { - name: "-d", - description: "Select all processes except session leaders", - }, - { - name: "--deselect", - description: - "Select all processes except those that fulfill the specified conditions", - }, - { - name: "-N", - description: - "Select all processes except those that fulfill the specified conditions (negates the selection)", - }, - { - name: "--pid", - description: "Select by process ID", - args: { name: "pidlist" }, - }, - { - name: "--ppid", - description: - "Select by parent process ID. This selects the processes with a parent process ID in pidlist", - args: { name: "pidlist" }, - }, - { - name: "--sid", - description: "Select by session ID", - args: { name: "sesslist" }, - }, - { - name: "--tty", - description: "Select by terminal", - args: { name: "ttylist" }, - }, - { - name: "U", - description: "Select by effective user ID (EUID) or name", - args: { name: "userlist" }, - }, - { - name: "-U", - description: "Select by real user ID (RUID) or name", - args: { name: "userlist" }, - }, - { - name: "-u", - description: "Select by effective user ID (EUID) or name", - args: { name: "userlist" }, - }, - { - name: "--User", - description: "Select by real user ID (RUID) or name", - args: { name: "userlist" }, - }, - { - name: "--user", - description: "Select by effective user ID (EUID) or name", - args: { name: "userlist" }, - }, - { - name: "-c", - description: "Show different scheduler information for the -l option", - }, - { - name: "--context", - description: "Display security context format (for SE Linux)", - }, - { name: "-f", description: "Do full-format listing" }, - { name: "-F", description: "Extra full format" }, - { - name: ["--format", "-o", "o"], - description: "", - args: { name: "format" }, - isRepeatable: true, - }, - { name: ["-M", "Z"], description: "(for SE Linux)" }, - { name: ["-y", "-l"], description: "" }, - { - name: "--cols", - description: "Set screen width", - args: { name: "n" }, - }, - { - name: "--columns", - description: "Set screen width", - args: { name: "n" }, - }, - { - name: "--cumulative", - description: - "Include some dead child process data (as a sum with the parent)", - }, - { name: "--forest", description: "ASCII art process tree" }, - { name: "-H", description: "Show process hierarchy (forest)" }, - { - name: "--headers", - description: "Repeat header lines, one per page of output", - }, - { - name: "-n", - description: "Set namelist file", - args: { name: "namelist" }, - }, - { - name: "--lines", - description: "Set screen height", - args: { name: "n" }, - }, - { - name: ["--no-headers", "--no-heading"], - description: "Print no header line at all", - }, - { - name: "--rows", - description: "Set screen height", - args: { name: "n" }, - }, - { - name: "--sort", - description: "Specify sorting order", - args: { name: "spec" }, - }, - { - name: "--width", - description: "Set screen width", - args: { name: "n" }, - }, - { - name: "-L", - description: "Show threads, possibly with LWP and NLWP columns", - }, - { - name: "-T", - description: "Show threads, possibly with SPID column", - }, - { name: "--help", description: "Print a help message" }, - { name: "--info", description: "Print debugging info" }, - { name: "--version", description: "Print the procps version" }, - ], + name: "ps", + description: "Report a snapshot of the current processes", + options: [ + { name: ["-A", "-e"], description: "Select all processes" }, + { + name: "-a", + description: "Select all processes except both session leaders", + args: { name: "getsid" }, + }, + { + name: "-d", + description: "Select all processes except session leaders", + }, + { + name: "--deselect", + description: + "Select all processes except those that fulfill the specified conditions", + }, + { + name: "-N", + description: + "Select all processes except those that fulfill the specified conditions (negates the selection)", + }, + { + name: "--pid", + description: "Select by process ID", + args: { name: "pidlist" }, + }, + { + name: "--ppid", + description: + "Select by parent process ID. This selects the processes with a parent process ID in pidlist", + args: { name: "pidlist" }, + }, + { + name: "--sid", + description: "Select by session ID", + args: { name: "sesslist" }, + }, + { + name: "--tty", + description: "Select by terminal", + args: { name: "ttylist" }, + }, + { + name: "U", + description: "Select by effective user ID (EUID) or name", + args: { name: "userlist" }, + }, + { + name: "-U", + description: "Select by real user ID (RUID) or name", + args: { name: "userlist" }, + }, + { + name: "-u", + description: "Select by effective user ID (EUID) or name", + args: { name: "userlist" }, + }, + { + name: "--User", + description: "Select by real user ID (RUID) or name", + args: { name: "userlist" }, + }, + { + name: "--user", + description: "Select by effective user ID (EUID) or name", + args: { name: "userlist" }, + }, + { + name: "-c", + description: "Show different scheduler information for the -l option", + }, + { + name: "--context", + description: "Display security context format (for SE Linux)", + }, + { name: "-f", description: "Do full-format listing" }, + { name: "-F", description: "Extra full format" }, + { + name: ["--format", "-o", "o"], + description: "", + args: { name: "format" }, + isRepeatable: true, + }, + { name: ["-M", "Z"], description: "(for SE Linux)" }, + { name: ["-y", "-l"], description: "" }, + { + name: "--cols", + description: "Set screen width", + args: { name: "n" }, + }, + { + name: "--columns", + description: "Set screen width", + args: { name: "n" }, + }, + { + name: "--cumulative", + description: + "Include some dead child process data (as a sum with the parent)", + }, + { name: "--forest", description: "ASCII art process tree" }, + { name: "-H", description: "Show process hierarchy (forest)" }, + { + name: "--headers", + description: "Repeat header lines, one per page of output", + }, + { + name: "-n", + description: "Set namelist file", + args: { name: "namelist" }, + }, + { + name: "--lines", + description: "Set screen height", + args: { name: "n" }, + }, + { + name: ["--no-headers", "--no-heading"], + description: "Print no header line at all", + }, + { + name: "--rows", + description: "Set screen height", + args: { name: "n" }, + }, + { + name: "--sort", + description: "Specify sorting order", + args: { name: "spec" }, + }, + { + name: "--width", + description: "Set screen width", + args: { name: "n" }, + }, + { + name: "-L", + description: "Show threads, possibly with LWP and NLWP columns", + }, + { + name: "-T", + description: "Show threads, possibly with SPID column", + }, + { name: "--help", description: "Print a help message" }, + { name: "--info", description: "Print debugging info" }, + { name: "--version", description: "Print the procps version" }, + ], }; export default completionSpec; diff --git a/extensions/terminal-suggest/src/completions/upstream/pwd.ts b/extensions/terminal-suggest/src/completions/upstream/pwd.ts index 42126db30b7d..11c390ad031d 100644 --- a/extensions/terminal-suggest/src/completions/upstream/pwd.ts +++ b/extensions/terminal-suggest/src/completions/upstream/pwd.ts @@ -1,16 +1,16 @@ const completionSpec: Fig.Spec = { - name: "pwd", - description: "Return working directory name", - options: [ - { - name: "-L", - description: "Display the logical current working directory", - }, - { - name: "-P", - description: "Display the physical current working directory", - }, - ], + name: "pwd", + description: "Return working directory name", + options: [ + { + name: "-L", + description: "Display the logical current working directory", + }, + { + name: "-P", + description: "Display the physical current working directory", + }, + ], }; export default completionSpec; diff --git a/extensions/terminal-suggest/src/completions/upstream/python.ts b/extensions/terminal-suggest/src/completions/upstream/python.ts index c9efa79499d2..97fdb841c234 100644 --- a/extensions/terminal-suggest/src/completions/upstream/python.ts +++ b/extensions/terminal-suggest/src/completions/upstream/python.ts @@ -1,4 +1,4 @@ -// import { filepaths } from "@fig/autocomplete-generators"; +import { filepaths } from '../../fig/autocomplete-tools/generators' const completionSpec: Fig.Spec = { name: "python", @@ -22,10 +22,10 @@ const completionSpec: Fig.Spec = { args: { name: "python script", template: "filepaths", - // generators: filepaths({ - // extensions: ["py"], - // editFileSuggestions: { priority: 76 }, - // }), + generators: filepaths({ + extensions: ["py"], + editFileSuggestions: { priority: 76 }, + }), }, options: [ { diff --git a/extensions/terminal-suggest/src/completions/upstream/python3.ts b/extensions/terminal-suggest/src/completions/upstream/python3.ts new file mode 100644 index 000000000000..7e08e0a20f90 --- /dev/null +++ b/extensions/terminal-suggest/src/completions/upstream/python3.ts @@ -0,0 +1,170 @@ +import { filepaths } from '../../helpers/filepaths'; + +const completionSpec: Fig.Spec = { + name: "python3", + description: "Run the python interpreter", + generateSpec: async (tokens, executeShellCommand) => { + const isDjangoManagePyFilePresentCommand = "cat manage.py | grep -q django"; + + if ( + ( + await executeShellCommand({ + command: "bash", + args: ["-c", isDjangoManagePyFilePresentCommand], + }) + ).status === 0 + ) { + return { + name: "python3", + subcommands: [{ name: "manage.py", loadSpec: "django-admin" }], + }; + } + }, + args: { + name: "python script", + isScript: true, + generators: filepaths({ + extensions: ["py"], + editFileSuggestions: { priority: 76 }, + }), + }, + options: [ + { + name: "-c", + insertValue: "-c '{cursor}'", + description: + "Execute the Python code in command. command can be one or more statements separated by newlines, with significant leading whitespace as in normal module code", + args: { + name: "command", + isCommand: true, + }, + }, + { + name: "-m", + insertValue: "-m '{cursor}'", + description: + "Search sys.path for the named module and execute its contents as the __main__ module", + args: { + name: "command", + isCommand: true, + }, + }, + { + name: ["-?", "-h", "--help"], + description: "Print a short description of all command line options", + }, + { + name: ["-V", "--version"], + description: "Print the Python version number and exit", + }, + { + name: "-b", + description: + "Issue a warning when comparing bytes or bytearray with str or bytes with int. Issue an error when the option is given twice (-bb)", + }, + { + name: "-B", + description: + "If given, Python won’t try to write .pyc files on the import of source modules", + }, + { + name: "--check-hash-based-pycs", + description: + "Control the validation behavior of hash-based .pyc files. See Cached bytecode invalidation", + args: { + suggestions: [ + { name: "default" }, + { name: "always" }, + { name: "never" }, + ], + }, + }, + { + name: "-d", + description: + "Turn on parser debugging output (for expert only, depending on compilation options)", + }, + { + name: "-E", + description: + "Ignore all PYTHON* environment variables, e.g. PYTHONPATH and PYTHONHOME, that might be set", + }, + { + name: "-i", + description: + "When a script is passed as first argument or the -c option is used, enter interactive mode after executing the script or the command, even when sys.stdin does not appear to be a terminal", + }, + { + name: "-I", + description: + "Run Python in isolated mode. This also implies -E and -s. In isolated mode sys.path contains neither the script’s directory nor the user’s site-packages directory", + }, + { + name: "-O", + description: + "Remove assert statements and any code conditional on the value of __debug__", + }, + { + name: "-OO", + description: "Do -O and also discard docstrings", + }, + { + name: "-g", + description: + "Don’t display the copyright and version messages even in interactive mode", + }, + { + name: "-R", + description: + "Turn on hash randomization. This option only has an effect if the PYTHONHASHSEED environment variable is set to 0, since hash randomization is enabled by default", + }, + { + name: "-s", + description: "Don’t add the user site-packages directory to sys.path", + }, + { + name: "-S", + description: + "Disable the import of the module site and the site-dependent manipulations of sys.path that it entails", + }, + { + name: "-u", + description: + "Force the stdout and stderr streams to be unbuffered. This option has no effect on the stdin stream", + }, + { + name: "-v", + description: + "Print a message each time a module is initialized, showing the place (filename or built-in module) from which it is loaded", + }, + { + name: "-W", + description: + "Warning control. Python’s warning machinery by default prints warning messages to sys.stderr", + args: {}, + }, + { + name: "-x", + description: + "Skip the first line of the source, allowing use of non-Unix forms of #!cmd. This is intended for a DOS specific hack only", + }, + { + name: "-X", + description: "Reserved for various implementation-specific options", + args: { + suggestions: [ + { name: "faulthandler" }, + { name: "showrefcount" }, + { name: "tracemalloc" }, + { name: "showalloccount" }, + { name: "importtime" }, + { name: "dev" }, + { name: "utf8" }, + { name: "pycache_prefix=PATH" }, + ], + }, + }, + ], +}; + +export default completionSpec; diff --git a/extensions/terminal-suggest/src/completions/upstream/rm.ts b/extensions/terminal-suggest/src/completions/upstream/rm.ts index 7b52f909527a..9aaf18553fea 100644 --- a/extensions/terminal-suggest/src/completions/upstream/rm.ts +++ b/extensions/terminal-suggest/src/completions/upstream/rm.ts @@ -1,43 +1,43 @@ const completionSpec: Fig.Spec = { - name: "rm", - description: "Remove directory entries", - args: { - isVariadic: true, - template: ["folders", "filepaths"], - }, + name: "rm", + description: "Remove directory entries", + args: { + isVariadic: true, + template: ["folders", "filepaths"], + }, - options: [ - { - name: ["-r", "-R"], - description: - "Recursive. Attempt to remove the file hierarchy rooted in each file argument", - isDangerous: true, - }, - { - name: "-P", - description: "Overwrite regular files before deleting them", - isDangerous: true, - }, - { - name: "-d", - description: - "Attempt to remove directories as well as other types of files", - }, - { - name: "-f", - description: - "⚠️ Attempt to remove the files without prompting for confirmation", - isDangerous: true, - }, - { - name: "-i", - description: "Request confirmation before attempting to remove each file", - }, - { - name: "-v", - description: "Be verbose when deleting files", - }, - ], + options: [ + { + name: ["-r", "-R"], + description: + "Recursive. Attempt to remove the file hierarchy rooted in each file argument", + isDangerous: true, + }, + { + name: "-P", + description: "Overwrite regular files before deleting them", + isDangerous: true, + }, + { + name: "-d", + description: + "Attempt to remove directories as well as other types of files", + }, + { + name: "-f", + description: + "⚠️ Attempt to remove the files without prompting for confirmation", + isDangerous: true, + }, + { + name: "-i", + description: "Request confirmation before attempting to remove each file", + }, + { + name: "-v", + description: "Be verbose when deleting files", + }, + ], }; export default completionSpec; diff --git a/extensions/terminal-suggest/src/completions/upstream/rmdir.ts b/extensions/terminal-suggest/src/completions/upstream/rmdir.ts index 92790e75d0dd..430b9a678eff 100644 --- a/extensions/terminal-suggest/src/completions/upstream/rmdir.ts +++ b/extensions/terminal-suggest/src/completions/upstream/rmdir.ts @@ -1,18 +1,18 @@ const completionSpec: Fig.Spec = { - name: "rmdir", - description: "Remove directories", - args: { - isVariadic: true, - template: "folders", - }, + name: "rmdir", + description: "Remove directories", + args: { + isVariadic: true, + template: "folders", + }, - options: [ - { - name: "-p", - description: "Remove each directory of path", - isDangerous: true, - }, - ], + options: [ + { + name: "-p", + description: "Remove each directory of path", + isDangerous: true, + }, + ], }; export default completionSpec; diff --git a/extensions/terminal-suggest/src/completions/upstream/scp.ts b/extensions/terminal-suggest/src/completions/upstream/scp.ts index 93da019b6867..3971e94d46fd 100644 --- a/extensions/terminal-suggest/src/completions/upstream/scp.ts +++ b/extensions/terminal-suggest/src/completions/upstream/scp.ts @@ -1,226 +1,226 @@ import { knownHosts, configHosts } from "./ssh"; const completionSpec: Fig.Spec = { - name: "scp", - description: "Copies files or directories between hosts on a network", - args: [ - { - name: "sources", - description: "File or directory, local or remote ([user@]host:[path])", - isVariadic: true, - generators: [ - knownHosts, - configHosts, - { template: ["history", "filepaths", "folders"] }, - ], - }, - { - name: "target", - description: "File or directory, local or remote ([user@]host:[path])", - generators: [ - knownHosts, - configHosts, - { template: ["history", "filepaths", "folders"] }, - ], - }, - ], - options: [ - { - name: "-3", - description: `Copies between two remote hosts are transferred through the local + name: "scp", + description: "Copies files or directories between hosts on a network", + args: [ + { + name: "sources", + description: "File or directory, local or remote ([user@]host:[path])", + isVariadic: true, + generators: [ + knownHosts, + configHosts, + { template: ["history", "filepaths", "folders"] }, + ], + }, + { + name: "target", + description: "File or directory, local or remote ([user@]host:[path])", + generators: [ + knownHosts, + configHosts, + { template: ["history", "filepaths", "folders"] }, + ], + }, + ], + options: [ + { + name: "-3", + description: `Copies between two remote hosts are transferred through the local host. Without this option the data is copied directly between the two remote hosts. Note that this option disables the progress meter and selects batch mode for the second host, since scp cannot ask for passwords or passphrases for both hosts`, - }, - { - name: "-4", - description: "Forces scp to use IPv4 addresses only", - }, - { - name: "-6", - description: "Forces scp to use IPv6 addresses only", - }, - { - name: "-A", - description: - "Allows forwarding of ssh-agent(1) to the remote system. The default is not to forward an authentication agent", - }, - { - name: "-B", - description: - "Selects batch mode (prevents asking for passwords or passphrases)", - }, - { - name: "-C", - description: - "Compression enable. Passes the -C flag to ssh(1) to enable compression", - }, - { - name: "-c", - description: - "Selects the cipher to use for encrypting the data transfer. This option is directly passed to ssh(1)", - args: { - name: "cipher", - description: "The selected cipher specification", - }, - }, - { - name: "-F", - description: - "Specifies an alternative per-user configuration file for ssh. This option is directly passed to ssh(1)", - args: { - name: "ssh_config", - description: "The selected ssh config", - }, - }, - { - name: "-i", - description: - "Selects the file from which the identity (private key) for public key authentication is read. This option is directly passed to ssh(1)", - args: { - name: "identity_file", - description: "Specified identity file", - }, - }, - { - name: "-J", - description: `Connect to the target host by first making an scp connection to the + }, + { + name: "-4", + description: "Forces scp to use IPv4 addresses only", + }, + { + name: "-6", + description: "Forces scp to use IPv6 addresses only", + }, + { + name: "-A", + description: + "Allows forwarding of ssh-agent(1) to the remote system. The default is not to forward an authentication agent", + }, + { + name: "-B", + description: + "Selects batch mode (prevents asking for passwords or passphrases)", + }, + { + name: "-C", + description: + "Compression enable. Passes the -C flag to ssh(1) to enable compression", + }, + { + name: "-c", + description: + "Selects the cipher to use for encrypting the data transfer. This option is directly passed to ssh(1)", + args: { + name: "cipher", + description: "The selected cipher specification", + }, + }, + { + name: "-F", + description: + "Specifies an alternative per-user configuration file for ssh. This option is directly passed to ssh(1)", + args: { + name: "ssh_config", + description: "The selected ssh config", + }, + }, + { + name: "-i", + description: + "Selects the file from which the identity (private key) for public key authentication is read. This option is directly passed to ssh(1)", + args: { + name: "identity_file", + description: "Specified identity file", + }, + }, + { + name: "-J", + description: `Connect to the target host by first making an scp connection to the jump host described by destination and then establishing a TCP forwarding to the ultimate destination from there. Multiple jump hops may be specified separated by comma characters. This is a shortcut to specify a ProxyJump configuration directive. This option is directly passed to ssh(1)`, - args: { - name: "destination", - description: "Scp destination", - }, - }, - { - name: "-l", - description: "Limits the used bandwidth, specified in Kbit/s", - args: { - name: "limit", - description: "Limit bandwidth in Kbit/s", - }, - }, - { - name: "-o", - description: `Can be used to pass options to ssh in the format used in + args: { + name: "destination", + description: "Scp destination", + }, + }, + { + name: "-l", + description: "Limits the used bandwidth, specified in Kbit/s", + args: { + name: "limit", + description: "Limit bandwidth in Kbit/s", + }, + }, + { + name: "-o", + description: `Can be used to pass options to ssh in the format used in ssh_config(5). This is useful for specifying options for which there is no separate scp command-line flag. For full details of the options listed below, and their possible values, see ssh_config(5)`, - args: { - name: "option", - suggestions: [ - { name: "AddressFamily" }, - { name: "BatchMode" }, - { name: "BindAddress" }, - { name: "ChallengeResponseAuthentication" }, - { name: "CheckHostIP" }, - { name: "Cipher" }, - { name: "Ciphers" }, - { name: "ClearAllForwardings" }, - { name: "Compression" }, - { name: "CompressionLevel" }, - { name: "ConnectionAttempts" }, - { name: "ConnectTimeout" }, - { name: "ControlMaster" }, - { name: "ControlPath" }, - { name: "ControlPersist" }, - { name: "DynamicForward" }, - { name: "EscapeChar" }, - { name: "ExitOnForwardFailure" }, - { name: "ForwardAgent" }, - { name: "ForwardX11" }, - { name: "ForwardX11Timeout" }, - { name: "ForwardX11Trusted" }, - { name: "GatewayPorts" }, - { name: "GlobalKnownHostsFile" }, - { name: "GSSAPIAuthentication" }, - { name: "GSSAPIDelegateCredentials" }, - { name: "HashKnownHosts" }, - { name: "Host" }, - { name: "HostbasedAuthentication" }, - { name: "HostKeyAlgorithms" }, - { name: "HostKeyAlias" }, - { name: "HostName" }, - { name: "IdentityFile" }, - { name: "IdentitiesOnly" }, - { name: "IPQoS" }, - { name: "KbdInteractiveAuthentication" }, - { name: "KbdInteractiveDevices" }, - { name: "KexAlgorithms" }, - { name: "LocalCommand" }, - { name: "LocalForward" }, - { name: "LogLevel" }, - { name: "MACs" }, - { name: "NoHostAuthenticationForLocalhost" }, - { name: "NumberOfPasswordPrompts" }, - { name: "PasswordAuthentication" }, - { name: "PermitLocalCommand" }, - { name: "PKCS11Provider" }, - { name: "Port" }, - { name: "PreferredAuthentications" }, - { name: "Protocol" }, - { name: "ProxyCommand" }, - { name: "PubkeyAuthentication" }, - { name: "RekeyLimit" }, - { name: "RequestTTY" }, - { name: "RhostsRSAAuthentication" }, - { name: "RSAAuthentication" }, - { name: "SendEnv" }, - { name: "ServerAliveInterval" }, - { name: "ServerAliveCountMax" }, - { name: "StrictHostKeyChecking" }, - { name: "TCPKeepAlive" }, - { name: "Tunnel" }, - { name: "TunnelDevice" }, - { name: "UsePrivilegedPort" }, - { name: "User" }, - { name: "UserKnownHostsFile" }, - { name: "VerifyHostKeyDNS" }, - { name: "VisualHostKey" }, - { name: "XAuthLocation" }, - ], - }, - }, - { - name: "-P", - description: `Specifies the port to connect to on the remote host. Note that + args: { + name: "option", + suggestions: [ + { name: "AddressFamily" }, + { name: "BatchMode" }, + { name: "BindAddress" }, + { name: "ChallengeResponseAuthentication" }, + { name: "CheckHostIP" }, + { name: "Cipher" }, + { name: "Ciphers" }, + { name: "ClearAllForwardings" }, + { name: "Compression" }, + { name: "CompressionLevel" }, + { name: "ConnectionAttempts" }, + { name: "ConnectTimeout" }, + { name: "ControlMaster" }, + { name: "ControlPath" }, + { name: "ControlPersist" }, + { name: "DynamicForward" }, + { name: "EscapeChar" }, + { name: "ExitOnForwardFailure" }, + { name: "ForwardAgent" }, + { name: "ForwardX11" }, + { name: "ForwardX11Timeout" }, + { name: "ForwardX11Trusted" }, + { name: "GatewayPorts" }, + { name: "GlobalKnownHostsFile" }, + { name: "GSSAPIAuthentication" }, + { name: "GSSAPIDelegateCredentials" }, + { name: "HashKnownHosts" }, + { name: "Host" }, + { name: "HostbasedAuthentication" }, + { name: "HostKeyAlgorithms" }, + { name: "HostKeyAlias" }, + { name: "HostName" }, + { name: "IdentityFile" }, + { name: "IdentitiesOnly" }, + { name: "IPQoS" }, + { name: "KbdInteractiveAuthentication" }, + { name: "KbdInteractiveDevices" }, + { name: "KexAlgorithms" }, + { name: "LocalCommand" }, + { name: "LocalForward" }, + { name: "LogLevel" }, + { name: "MACs" }, + { name: "NoHostAuthenticationForLocalhost" }, + { name: "NumberOfPasswordPrompts" }, + { name: "PasswordAuthentication" }, + { name: "PermitLocalCommand" }, + { name: "PKCS11Provider" }, + { name: "Port" }, + { name: "PreferredAuthentications" }, + { name: "Protocol" }, + { name: "ProxyCommand" }, + { name: "PubkeyAuthentication" }, + { name: "RekeyLimit" }, + { name: "RequestTTY" }, + { name: "RhostsRSAAuthentication" }, + { name: "RSAAuthentication" }, + { name: "SendEnv" }, + { name: "ServerAliveInterval" }, + { name: "ServerAliveCountMax" }, + { name: "StrictHostKeyChecking" }, + { name: "TCPKeepAlive" }, + { name: "Tunnel" }, + { name: "TunnelDevice" }, + { name: "UsePrivilegedPort" }, + { name: "User" }, + { name: "UserKnownHostsFile" }, + { name: "VerifyHostKeyDNS" }, + { name: "VisualHostKey" }, + { name: "XAuthLocation" }, + ], + }, + }, + { + name: "-P", + description: `Specifies the port to connect to on the remote host. Note that this option is written with a capital ‘P’, because -p is already reserved for preserving the times and modes of the file`, - args: { - name: "port", - }, - }, - { - name: "-p", - description: - "Preserves modification times, access times, and modes from the original file", - }, - { - name: "-q", - description: - "Quiet mode: disables the progress meter as well as warning and diagnostic messages from ssh(1)", - }, - { - name: "-r", - description: - "Recursively copy entire directories. Note that scp follows symbolic links encountered in the tree traversal", - }, - { - name: "-S", - description: - "Name of program to use for the encrypted connection. The program must understand ssh(1) options", - args: { - name: "program", - }, - }, - { - name: "-T", - description: `Disable strict filename checking. By default when copying files + args: { + name: "port", + }, + }, + { + name: "-p", + description: + "Preserves modification times, access times, and modes from the original file", + }, + { + name: "-q", + description: + "Quiet mode: disables the progress meter as well as warning and diagnostic messages from ssh(1)", + }, + { + name: "-r", + description: + "Recursively copy entire directories. Note that scp follows symbolic links encountered in the tree traversal", + }, + { + name: "-S", + description: + "Name of program to use for the encrypted connection. The program must understand ssh(1) options", + args: { + name: "program", + }, + }, + { + name: "-T", + description: `Disable strict filename checking. By default when copying files from a remote host to a local directory scp checks that the received filenames match those requested on the command-line to prevent the remote end from sending unexpected or unwanted files. @@ -229,13 +229,13 @@ interpret filename wildcards, these checks may cause wanted files to be rejected. This option disables these checks at the expense of fully trusting that the server will not send unexpected filenames`, - }, - { - name: "-v", - description: - "Verbose mode. Causes scp and ssh(1) to print debugging messages about their progress. This is helpful in debugging connection, authentication, and configuration problems", - }, - ], + }, + { + name: "-v", + description: + "Verbose mode. Causes scp and ssh(1) to print debugging messages about their progress. This is helpful in debugging connection, authentication, and configuration problems", + }, + ], }; export default completionSpec; diff --git a/extensions/terminal-suggest/src/completions/upstream/ssh.ts b/extensions/terminal-suggest/src/completions/upstream/ssh.ts index 9b0f5bd4e772..4998530d3ec6 100644 --- a/extensions/terminal-suggest/src/completions/upstream/ssh.ts +++ b/extensions/terminal-suggest/src/completions/upstream/ssh.ts @@ -43,7 +43,7 @@ const getConfigLines = async ( .map((line) => line.split(" ")[1]); // Get the lines of every include file - const includeLines: any = await Promise.all( + const includeLines: string[][] = await Promise.all( includes.map((file) => getConfigLines(file, executeShellCommand, home, basePath) ) @@ -89,10 +89,10 @@ export const configHosts: Fig.Generator = { return configLines .filter( - (line: any) => + (line) => line.trim().toLowerCase().startsWith("host ") && !line.includes("*") ) - .map((host: any) => ({ + .map((host) => ({ name: host.split(" ")[1], description: "SSH host", priority: 90, diff --git a/extensions/terminal-suggest/src/completions/upstream/tail.ts b/extensions/terminal-suggest/src/completions/upstream/tail.ts index 657c610b37dc..fd905588c60f 100644 --- a/extensions/terminal-suggest/src/completions/upstream/tail.ts +++ b/extensions/terminal-suggest/src/completions/upstream/tail.ts @@ -1,20 +1,20 @@ const completionSpec: Fig.Spec = { - name: "tail", - description: "Display the last part of a file", - args: { - isVariadic: true, - template: "filepaths", - }, - options: [ - { - name: "-f", - description: "Wait for additional data to be appended", - }, - { - name: "-r", - description: "Display in reverse order", - }, - ], + name: "tail", + description: "Display the last part of a file", + args: { + isVariadic: true, + template: "filepaths", + }, + options: [ + { + name: "-f", + description: "Wait for additional data to be appended", + }, + { + name: "-r", + description: "Display in reverse order", + }, + ], }; export default completionSpec; diff --git a/extensions/terminal-suggest/src/completions/upstream/top.ts b/extensions/terminal-suggest/src/completions/upstream/top.ts index df922164c069..5831fd7b89ed 100644 --- a/extensions/terminal-suggest/src/completions/upstream/top.ts +++ b/extensions/terminal-suggest/src/completions/upstream/top.ts @@ -1,49 +1,49 @@ const completionSpec: Fig.Spec = { - name: "top", - description: "Display Linux tasks", - options: [ - { - name: ["-h", "-v"], - description: "Show library version and usage prompt", - }, - { - name: "-b", - description: "Starts top in Batch mode", - args: { - name: "operation", - }, - }, - { - name: "-c", - description: "Starts top with last remembered c state reversed", - args: { - name: "toggle", - }, - }, - { - name: "-i", - description: - "Starts top with the last remembered 'i' state reversed. When this toggle is Off, tasks that are idled or zombied will not be displayed", - args: { - name: "toggle", - }, - }, - { - name: "-s", - description: "Starts top with secure mode forced", - args: { - name: "delay", - }, - }, - { - name: "-pid", - description: "Monitor pids", - args: { - name: "process ids", - isVariadic: true, - }, - }, - ], + name: "top", + description: "Display Linux tasks", + options: [ + { + name: ["-h", "-v"], + description: "Show library version and usage prompt", + }, + { + name: "-b", + description: "Starts top in Batch mode", + args: { + name: "operation", + }, + }, + { + name: "-c", + description: "Starts top with last remembered c state reversed", + args: { + name: "toggle", + }, + }, + { + name: "-i", + description: + "Starts top with the last remembered 'i' state reversed. When this toggle is Off, tasks that are idled or zombied will not be displayed", + args: { + name: "toggle", + }, + }, + { + name: "-s", + description: "Starts top with secure mode forced", + args: { + name: "delay", + }, + }, + { + name: "-pid", + description: "Monitor pids", + args: { + name: "process ids", + isVariadic: true, + }, + }, + ], }; export default completionSpec; diff --git a/extensions/terminal-suggest/src/completions/upstream/touch.ts b/extensions/terminal-suggest/src/completions/upstream/touch.ts index 452088783139..6987272ad846 100644 --- a/extensions/terminal-suggest/src/completions/upstream/touch.ts +++ b/extensions/terminal-suggest/src/completions/upstream/touch.ts @@ -1,59 +1,59 @@ const completionSpec: Fig.Spec = { - name: "touch", - description: "Change file access and modification times", - args: { - name: "file", - isVariadic: true, - template: "folders", - suggestCurrentToken: true, - }, - options: [ - { - name: "-A", - description: - "Adjust the access and modification time stamps for the file by the specified value", - args: { - name: "time", - description: "[-][[hh]mm]SS", - }, - }, - { name: "-a", description: "Change the access time of the file" }, - { - name: "-c", - description: "Do not create the file if it does not exist", - }, - { - name: "-f", - description: - "Attempt to force the update, even if the file permissions do not currently permit it", - }, - { - name: "-h", - description: - "If the file is a symbolic link, change the times of the link itself rather than the file that the link points to", - }, - { - name: "-m", - description: "Change the modification time of the file", - }, - { - name: "-r", - description: - "Use the access and modifications times from the specified file instead of the current time of day", - args: { - name: "file", - }, - }, - { - name: "-t", - description: - "Change the access and modification times to the specified time instead of the current time of day", - args: { - name: "timestamp", - description: "[[CC]YY]MMDDhhmm[.SS]", - }, - }, - ], + name: "touch", + description: "Change file access and modification times", + args: { + name: "file", + isVariadic: true, + template: "folders", + suggestCurrentToken: true, + }, + options: [ + { + name: "-A", + description: + "Adjust the access and modification time stamps for the file by the specified value", + args: { + name: "time", + description: "[-][[hh]mm]SS", + }, + }, + { name: "-a", description: "Change the access time of the file" }, + { + name: "-c", + description: "Do not create the file if it does not exist", + }, + { + name: "-f", + description: + "Attempt to force the update, even if the file permissions do not currently permit it", + }, + { + name: "-h", + description: + "If the file is a symbolic link, change the times of the link itself rather than the file that the link points to", + }, + { + name: "-m", + description: "Change the modification time of the file", + }, + { + name: "-r", + description: + "Use the access and modifications times from the specified file instead of the current time of day", + args: { + name: "file", + }, + }, + { + name: "-t", + description: + "Change the access and modification times to the specified time instead of the current time of day", + args: { + name: "timestamp", + description: "[[CC]YY]MMDDhhmm[.SS]", + }, + }, + ], }; export default completionSpec; diff --git a/extensions/terminal-suggest/src/completions/upstream/uname.ts b/extensions/terminal-suggest/src/completions/upstream/uname.ts index e73b9efe3979..775387703d41 100644 --- a/extensions/terminal-suggest/src/completions/upstream/uname.ts +++ b/extensions/terminal-suggest/src/completions/upstream/uname.ts @@ -1,36 +1,36 @@ const completionSpec: Fig.Spec = { - name: "uname", - description: "Print operating system name", - options: [ - { - name: "-a", - description: "Print all available system information", - }, - { - name: "-m", - description: "Print the machine hardware name", - }, - { - name: "-n", - description: "Print the system hostname", - }, - { - name: "-p", - description: "Print the machine processor architecture name", - }, - { - name: "-r", - description: "Print the operating system release", - }, - { - name: "-s", - description: "Print the operating system name", - }, - { - name: "-v", - description: "Print the operating system version", - }, - ], + name: "uname", + description: "Print operating system name", + options: [ + { + name: "-a", + description: "Print all available system information", + }, + { + name: "-m", + description: "Print the machine hardware name", + }, + { + name: "-n", + description: "Print the system hostname", + }, + { + name: "-p", + description: "Print the machine processor architecture name", + }, + { + name: "-r", + description: "Print the operating system release", + }, + { + name: "-s", + description: "Print the operating system name", + }, + { + name: "-v", + description: "Print the operating system version", + }, + ], }; export default completionSpec; diff --git a/extensions/terminal-suggest/src/completions/upstream/vim.ts b/extensions/terminal-suggest/src/completions/upstream/vim.ts index 7e41109f025c..267edb385fff 100644 --- a/extensions/terminal-suggest/src/completions/upstream/vim.ts +++ b/extensions/terminal-suggest/src/completions/upstream/vim.ts @@ -1,244 +1,244 @@ const completionSpec: Fig.Spec = { - name: "vim", - description: "Vi IMproved, a programmer's text editor", - args: { - template: "filepaths", - // suggestCurrentToken: true, - }, - options: [ - { - name: "-v", - description: "Vi mode (like 'vi')", - }, - { - name: "-e", - description: "Ex mode (like 'ex')", - }, - { - name: "-E", - description: "Improved Ex mode", - }, - { - name: "-s", - description: - "Enable silent mode (when in ex mode), or Read Normal mode commands from file", - args: { - name: "scriptin", - template: "filepaths", - isOptional: true, - }, - }, - { - name: "-d", - description: "Diff mode (like 'vimdiff')", - }, - { - name: "-y", - description: "Easy mode (like 'evim', modeless)", - }, - { - name: "-R", - description: "Readonly mode (like 'view')", - }, - { - name: "-Z", - description: "Restricted mode (like 'rvim')", - }, - { - name: "-m", - description: "Modifications (writing files) not allowed", - }, - { - name: "-M", - description: "Modifications in text not allowed", - }, - { - name: "-b", - description: "Binary mode", - }, - { - name: "-l", - description: "Lisp mode", - }, - { - name: "-C", - description: "Compatible with Vi: 'compatible'", - }, - { - name: "-N", - description: "Not fully Vi compatible: 'nocompatible'", - }, - { - name: "-V", - description: "Be verbose [level N] [log messages to fname]", - args: [ - { - name: "N", - }, - { - name: "fname", - template: "filepaths", - }, - ], - }, - { - name: "-D", - description: "Debugging mode", - }, - { - name: "-n", - description: "No swap file, use memory only", - }, - { - name: "-r", - description: - "Recover crashed session if filename is specified, otherwise list swap files and exit", - args: { - name: "filename", - isOptional: true, - template: "filepaths", - }, - }, - { - name: "-L", - description: "Same as -r", - args: { - name: "filename", - template: "filepaths", - }, - }, - { - name: "-T", - description: "Set terminal type to ", - args: { - name: "terminal", - }, - }, - { - name: "--not-a-term", - description: "Skip warning for input/output not being a terminal", - }, - { - name: "--ttyfail", - description: "Exit if input or output is not a terminal", - }, - { - name: "-u", - description: "Use instead of any .vimrc", - args: { - name: "vimrc", - template: "filepaths", - }, - }, - { - name: "--noplugin", - description: "Don't load plugin scripts", - }, - { - name: "-p", - description: "Open N tab pages (default: one for each file)", - args: { - name: "N", - isOptional: true, - }, - }, - { - name: "-o", - description: "Open N windows (default: one for each file)", - args: { - name: "N", - isOptional: true, - }, - }, - { - name: "-O", - description: "Like -o but split vertically", - args: { - name: "N", - isOptional: true, - }, - }, - { - name: "+", - description: - "Start at end of file, if line number is specified, start at that line", - args: { - name: "lnum", - isOptional: true, - }, - }, - { - name: "--cmd", - description: "Execute before loading any vimrc file", - args: { - name: "command", - isCommand: true, - }, - }, - { - name: "-c", - description: "Execute after loading the first file", - args: { - name: "command", - }, - }, - { - name: "-S", - description: "Source file after loading the first file", - args: { - name: "session", - template: "filepaths", - }, - }, - { - name: "-w", - description: "Append all typed commands to file ", - args: { - name: "scriptout", - template: "filepaths", - }, - }, - { - name: "-W", - description: "Write all typed commands to file ", - args: { - name: "scriptout", - template: "filepaths", - }, - }, - { - name: "-x", - description: "Edit encrypted files", - }, - { - name: "--startuptime", - description: "Write startup timing messages to ", - args: { - name: "file", - template: "filepaths", - }, - }, - { - name: "-i", - description: "Use instead of .viminfo", - args: { - name: "viminfo", - template: "filepaths", - }, - }, - { - name: "--clean", - description: "'nocompatible', Vim defaults, no plugins, no viminfo", - }, - { - name: ["-h", "--help"], - description: "Print Help message and exit", - }, - { - name: "--version", - description: "Print version information and exit", - }, - ], + name: "vim", + description: "Vi IMproved, a programmer's text editor", + args: { + template: "filepaths", + // suggestCurrentToken: true, + }, + options: [ + { + name: "-v", + description: "Vi mode (like 'vi')", + }, + { + name: "-e", + description: "Ex mode (like 'ex')", + }, + { + name: "-E", + description: "Improved Ex mode", + }, + { + name: "-s", + description: + "Enable silent mode (when in ex mode), or Read Normal mode commands from file", + args: { + name: "scriptin", + template: "filepaths", + isOptional: true, + }, + }, + { + name: "-d", + description: "Diff mode (like 'vimdiff')", + }, + { + name: "-y", + description: "Easy mode (like 'evim', modeless)", + }, + { + name: "-R", + description: "Readonly mode (like 'view')", + }, + { + name: "-Z", + description: "Restricted mode (like 'rvim')", + }, + { + name: "-m", + description: "Modifications (writing files) not allowed", + }, + { + name: "-M", + description: "Modifications in text not allowed", + }, + { + name: "-b", + description: "Binary mode", + }, + { + name: "-l", + description: "Lisp mode", + }, + { + name: "-C", + description: "Compatible with Vi: 'compatible'", + }, + { + name: "-N", + description: "Not fully Vi compatible: 'nocompatible'", + }, + { + name: "-V", + description: "Be verbose [level N] [log messages to fname]", + args: [ + { + name: "N", + }, + { + name: "fname", + template: "filepaths", + }, + ], + }, + { + name: "-D", + description: "Debugging mode", + }, + { + name: "-n", + description: "No swap file, use memory only", + }, + { + name: "-r", + description: + "Recover crashed session if filename is specified, otherwise list swap files and exit", + args: { + name: "filename", + isOptional: true, + template: "filepaths", + }, + }, + { + name: "-L", + description: "Same as -r", + args: { + name: "filename", + template: "filepaths", + }, + }, + { + name: "-T", + description: "Set terminal type to ", + args: { + name: "terminal", + }, + }, + { + name: "--not-a-term", + description: "Skip warning for input/output not being a terminal", + }, + { + name: "--ttyfail", + description: "Exit if input or output is not a terminal", + }, + { + name: "-u", + description: "Use instead of any .vimrc", + args: { + name: "vimrc", + template: "filepaths", + }, + }, + { + name: "--noplugin", + description: "Don't load plugin scripts", + }, + { + name: "-p", + description: "Open N tab pages (default: one for each file)", + args: { + name: "N", + isOptional: true, + }, + }, + { + name: "-o", + description: "Open N windows (default: one for each file)", + args: { + name: "N", + isOptional: true, + }, + }, + { + name: "-O", + description: "Like -o but split vertically", + args: { + name: "N", + isOptional: true, + }, + }, + { + name: "+", + description: + "Start at end of file, if line number is specified, start at that line", + args: { + name: "lnum", + isOptional: true, + }, + }, + { + name: "--cmd", + description: "Execute before loading any vimrc file", + args: { + name: "command", + isCommand: true, + }, + }, + { + name: "-c", + description: "Execute after loading the first file", + args: { + name: "command", + }, + }, + { + name: "-S", + description: "Source file after loading the first file", + args: { + name: "session", + template: "filepaths", + }, + }, + { + name: "-w", + description: "Append all typed commands to file ", + args: { + name: "scriptout", + template: "filepaths", + }, + }, + { + name: "-W", + description: "Write all typed commands to file ", + args: { + name: "scriptout", + template: "filepaths", + }, + }, + { + name: "-x", + description: "Edit encrypted files", + }, + { + name: "--startuptime", + description: "Write startup timing messages to ", + args: { + name: "file", + template: "filepaths", + }, + }, + { + name: "-i", + description: "Use instead of .viminfo", + args: { + name: "viminfo", + template: "filepaths", + }, + }, + { + name: "--clean", + description: "'nocompatible', Vim defaults, no plugins, no viminfo", + }, + { + name: ["-h", "--help"], + description: "Print Help message and exit", + }, + { + name: "--version", + description: "Print version information and exit", + }, + ], }; export default completionSpec; diff --git a/extensions/terminal-suggest/src/completions/upstream/wget.ts b/extensions/terminal-suggest/src/completions/upstream/wget.ts index fd8442d9e8aa..ff14b34f0f86 100644 --- a/extensions/terminal-suggest/src/completions/upstream/wget.ts +++ b/extensions/terminal-suggest/src/completions/upstream/wget.ts @@ -1,399 +1,399 @@ const completionSpec: Fig.Spec = { - name: "wget", - description: "A non-interactive network retriever", - args: { - isVariadic: true, - name: "url", - description: "The url(s) to retrieve", - }, - options: [ - { - name: ["-V", "--version"], - description: "Display the version of Wget and exit", - }, - { name: ["-h", "--help"], description: "Print this help" }, - { - name: ["-b", "--background"], - description: "Go to background after startup", - }, - { - name: ["-e", "--execute=COMMAND"], - description: "Execute a `.wgetrc'-style command", - }, - { name: ["-o", "--output-file=FILE"], description: "Log messages to FILE" }, - { - name: ["-a", "--append-output=FILE"], - description: "Append messages to FILE", - }, - { name: ["-q", "--quiet"], description: "Quiet (no output)" }, - { - name: ["-v", "--verbose"], - description: "Be verbose (this is the default)", - }, - { - name: ["-nv", "--no-verbose"], - description: "Turn off verboseness, without being quiet", - }, - { - name: "--report-speed=TYPE", - description: "Output bandwidth as TYPE. TYPE can be bits", - }, - { - name: ["-i", "--input-file=FILE"], - description: "Download URLs found in local or external FILE", - }, - { name: ["-F", "--force-html"], description: "Treat input file as HTML" }, - { - name: ["-B", "--base=URL"], - description: "Resolves HTML input-file links (-i -F) relative to URL", - }, - { name: "--config=FILE", description: "Specify config file to use" }, - { name: "--no-config", description: "Do not read any config file" }, - { - name: "--rejected-log=FILE", - description: "Log reasons for URL rejection to FILE", - }, - { - name: ["-t", "--tries=NUMBER"], - description: "Set number of retries to NUMBER (0 unlimits)", - }, - { - name: "--retry-connrefused", - description: "Retry even if connection is refused", - }, - { - name: "--retry-on-http-error", - description: "Comma-separated list of HTTP errors to retry", - }, - { - name: ["-O", "--output-document=FILE"], - description: "Write documents to FILE", - }, - { - name: ["-nc", "--no-clobber"], - description: - "Skip downloads that would download to existing files (overwriting them)", - }, - { - name: "--no-netrc", - description: "Don't try to obtain credentials from .netrc", - }, - { - name: ["-c", "--continue"], - description: "Resume getting a partially-downloaded file", - }, - { - name: "--start-pos=OFFSET", - description: "Start downloading from zero-based position OFFSET", - }, - { name: "--progress=TYPE", description: "Select progress gauge type" }, - { - name: "--show-progress", - description: "Display the progress bar in any verbosity mode", - }, - { - name: ["-N", "--timestamping"], - description: "Don't re-retrieve files unless newer than local", - }, - { name: ["-S", "--server-response"], description: "Print server response" }, - { name: "--spider", description: "Don't download anything" }, - { - name: ["-T", "--timeout=SECONDS"], - description: "Set all timeout values to SECONDS", - }, - { - name: "--dns-timeout=SECS", - description: "Set the DNS lookup timeout to SECS", - }, - { - name: "--connect-timeout=SECS", - description: "Set the connect timeout to SECS", - }, - { - name: "--read-timeout=SECS", - description: "Set the read timeout to SECS", - }, - { - name: ["-w", "--wait=SECONDS"], - description: "Wait SECONDS between retrievals", - }, - { - name: "--waitretry=SECONDS", - description: "Wait 1..SECONDS between retries of a retrieval", - }, - { - name: "--random-wait", - description: "Wait from 0.5*WAIT...1.5*WAIT secs between retrievals", - }, - { name: "--no-proxy", description: "Explicitly turn off proxy" }, - { - name: ["-Q", "--quota=NUMBER"], - description: "Set retrieval quota to NUMBER", - }, - { - name: "--bind-address=ADDRESS", - description: "Bind to ADDRESS (hostname or IP) on local host", - }, - { name: "--limit-rate=RATE", description: "Limit download rate to RATE" }, - { name: "--no-dns-cache", description: "Disable caching DNS lookups" }, - { - name: "--restrict-file-names=OS", - description: "Restrict chars in file names to ones OS allows", - }, - { - name: "--ignore-case", - description: "Ignore case when matching files/directories", - }, - { - name: ["-4", "--inet4-only"], - description: "Connect only to IPv4 addresses", - }, - { - name: ["-6", "--inet6-only"], - description: "Connect only to IPv6 addresses", - }, - { - name: "--user=USER", - description: "Set both ftp and http user to USER", - }, - { - name: "--password=PASS", - description: "Set both ftp and http password to PASS", - }, - { name: "--ask-password", description: "Prompt for passwords" }, - { name: "--no-iri", description: "Turn off IRI support" }, - { - name: "--local-encoding=ENC", - description: "Use ENC as the local encoding for IRIs", - }, - { - name: "--remote-encoding=ENC", - description: "Use ENC as the default remote encoding", - }, - { name: "--unlink", description: "Remove file before clobber" }, - { - name: "--xattr", - description: "Turn on storage of metadata in extended file attributes", - }, - { - name: ["-nd", "--no-directories"], - description: "Don't create directories", - }, - { - name: ["-x", "--force-directories"], - description: "Force creation of directories", - }, - { - name: ["-nH", "--no-host-directories"], - description: "Don't create host directories", - }, - { - name: "--protocol-directories", - description: "Use protocol name in directories", - }, - { - name: ["-P", "--directory-prefix=PREFIX"], - description: "Save files to PREFIX/", - }, - { - name: "--cut-dirs=NUMBER", - description: "Ignore NUMBER remote directory components", - }, - { name: "--http-user=USER", description: "Set http user to USER" }, - { - name: "--http-password=PASS", - description: "Set http password to PASS", - }, - { name: "--no-cache", description: "Disallow server-cached data" }, - { - name: ["-E", "--adjust-extension"], - description: "Save HTML/CSS documents with proper extensions", - }, - { - name: "--ignore-length", - description: "Ignore 'Content-Length' header field", - }, - { - name: "--header=STRING", - description: "Insert STRING among the headers", - }, - { - name: "--compression=TYPE", - description: - "Choose compression, one of auto, gzip and none. (default: none)", - }, - { - name: "--max-redirect", - description: "Maximum redirections allowed per page", - }, - { name: "--proxy-user=USER", description: "Set USER as proxy username" }, - { - name: "--proxy-password=PASS", - description: "Set PASS as proxy password", - }, - { - name: "--referer=URL", - description: "Include 'Referer: URL' header in HTTP request", - }, - { name: "--save-headers", description: "Save the HTTP headers to file" }, - { - name: ["-U", "--user-agent=AGENT"], - description: "Identify as AGENT instead of Wget/VERSION", - }, - { - name: "--no-http-keep-alive", - description: "Disable HTTP keep-alive (persistent connections)", - }, - { name: "--no-cookies", description: "Don't use cookies" }, - { - name: "--load-cookies=FILE", - description: "Load cookies from FILE before session", - }, - { - name: "--save-cookies=FILE", - description: "Save cookies to FILE after session", - }, - { - name: "--keep-session-cookies", - description: "Load and save session (non-permanent) cookies", - }, - { - name: "--post-data=STRING", - description: "Use the POST method; send STRING as the data", - }, - { - name: "--post-file=FILE", - description: "Use the POST method; send contents of FILE", - }, - { - name: "--method=HTTPMethod", - description: 'Use method "HTTPMethod" in the request', - }, - { - name: "--body-data=STRING", - description: "Send STRING as data. --method MUST be set", - }, - { - name: "--body-file=FILE", - description: "Send contents of FILE. --method MUST be set", - }, - { - name: "--content-on-error", - description: "Output the received content on server errors", - }, - { - name: "--secure-protocol=PR", - description: "Choose secure protocol, one of auto, SSLv2,", - }, - { name: "--https-only", description: "Only follow secure HTTPS links" }, - { - name: "--no-check-certificate", - description: "Don't validate the server's certificate", - }, - { name: "--certificate=FILE", description: "Client certificate file" }, - { - name: "--certificate-type=TYPE", - description: "Client certificate type, PEM or DER", - }, - { name: "--private-key=FILE", description: "Private key file" }, - { - name: "--private-key-type=TYPE", - description: "Private key type, PEM or DER", - }, - { - name: "--ca-certificate=FILE", - description: "File with the bundle of CAs", - }, - { - name: "--ca-directory=DIR", - description: "Directory where hash list of CAs is stored", - }, - { name: "--crl-file=FILE", description: "File with bundle of CRLs" }, - { - name: "--ciphers=STR", - description: - "Set the priority string (GnuTLS) or cipher list string (OpenSSL) directly", - }, - { name: ["-r", "--recursive"], description: "Specify recursive download" }, - { - name: ["-l", "--level=NUMBER"], - description: "Maximum recursion depth (inf or 0 for infinite)", - }, - { - name: "--delete-after", - description: "Delete files locally after downloading them", - }, - { - name: ["-k", "--convert-links"], - description: "Make links in downloaded HTML or CSS point to local files", - }, - { - name: ["-K", "--backup-converted"], - description: "Before converting file X, back up as X.orig", - }, - { - name: ["-m", "--mirror"], - description: "Shortcut for -N -r -l inf --no-remove-listing", - }, - { - name: ["-p", "--page-requisites"], - description: "Get all images, etc. needed to display HTML page", - }, - { - name: ["-A", "--accept=LIST"], - description: "Comma-separated list of accepted extensions", - }, - { - name: ["-R", "--reject=LIST"], - description: "Comma-separated list of rejected extensions", - }, - { - name: "--accept-regex=REGEX", - description: "Regex matching accepted URLs", - }, - { - name: "--reject-regex=REGEX", - description: "Regex matching rejected URLs", - }, - { name: "--regex-type=TYPE", description: "Regex type (posix)" }, - { - name: ["-D", "--domains=LIST"], - description: "Comma-separated list of accepted domains", - }, - { - name: "--exclude-domains=LIST", - description: "Comma-separated list of rejected domains", - }, - { - name: "--follow-ftp", - description: "Follow FTP links from HTML documents", - }, - { - name: "--follow-tags=LIST", - description: "Comma-separated list of followed HTML tags", - }, - { - name: "--ignore-tags=LIST", - description: "Comma-separated list of ignored HTML tags", - }, - { - name: ["-H", "--span-hosts"], - description: "Go to foreign hosts when recursive", - }, - { name: ["-L", "--relative"], description: "Follow relative links only" }, - { - name: ["-I", "--include-directories=LIST"], - description: "List of allowed directories", - }, - { - name: ["-X", "--exclude-directories=LIST"], - description: "List of excluded directories", - }, - { - name: ["-np", "--no-parent"], - description: "Don't ascend to the parent directory", - }, - ], + name: "wget", + description: "A non-interactive network retriever", + args: { + isVariadic: true, + name: "url", + description: "The url(s) to retrieve", + }, + options: [ + { + name: ["-V", "--version"], + description: "Display the version of Wget and exit", + }, + { name: ["-h", "--help"], description: "Print this help" }, + { + name: ["-b", "--background"], + description: "Go to background after startup", + }, + { + name: ["-e", "--execute=COMMAND"], + description: "Execute a `.wgetrc'-style command", + }, + { name: ["-o", "--output-file=FILE"], description: "Log messages to FILE" }, + { + name: ["-a", "--append-output=FILE"], + description: "Append messages to FILE", + }, + { name: ["-q", "--quiet"], description: "Quiet (no output)" }, + { + name: ["-v", "--verbose"], + description: "Be verbose (this is the default)", + }, + { + name: ["-nv", "--no-verbose"], + description: "Turn off verboseness, without being quiet", + }, + { + name: "--report-speed=TYPE", + description: "Output bandwidth as TYPE. TYPE can be bits", + }, + { + name: ["-i", "--input-file=FILE"], + description: "Download URLs found in local or external FILE", + }, + { name: ["-F", "--force-html"], description: "Treat input file as HTML" }, + { + name: ["-B", "--base=URL"], + description: "Resolves HTML input-file links (-i -F) relative to URL", + }, + { name: "--config=FILE", description: "Specify config file to use" }, + { name: "--no-config", description: "Do not read any config file" }, + { + name: "--rejected-log=FILE", + description: "Log reasons for URL rejection to FILE", + }, + { + name: ["-t", "--tries=NUMBER"], + description: "Set number of retries to NUMBER (0 unlimits)", + }, + { + name: "--retry-connrefused", + description: "Retry even if connection is refused", + }, + { + name: "--retry-on-http-error", + description: "Comma-separated list of HTTP errors to retry", + }, + { + name: ["-O", "--output-document=FILE"], + description: "Write documents to FILE", + }, + { + name: ["-nc", "--no-clobber"], + description: + "Skip downloads that would download to existing files (overwriting them)", + }, + { + name: "--no-netrc", + description: "Don't try to obtain credentials from .netrc", + }, + { + name: ["-c", "--continue"], + description: "Resume getting a partially-downloaded file", + }, + { + name: "--start-pos=OFFSET", + description: "Start downloading from zero-based position OFFSET", + }, + { name: "--progress=TYPE", description: "Select progress gauge type" }, + { + name: "--show-progress", + description: "Display the progress bar in any verbosity mode", + }, + { + name: ["-N", "--timestamping"], + description: "Don't re-retrieve files unless newer than local", + }, + { name: ["-S", "--server-response"], description: "Print server response" }, + { name: "--spider", description: "Don't download anything" }, + { + name: ["-T", "--timeout=SECONDS"], + description: "Set all timeout values to SECONDS", + }, + { + name: "--dns-timeout=SECS", + description: "Set the DNS lookup timeout to SECS", + }, + { + name: "--connect-timeout=SECS", + description: "Set the connect timeout to SECS", + }, + { + name: "--read-timeout=SECS", + description: "Set the read timeout to SECS", + }, + { + name: ["-w", "--wait=SECONDS"], + description: "Wait SECONDS between retrievals", + }, + { + name: "--waitretry=SECONDS", + description: "Wait 1..SECONDS between retries of a retrieval", + }, + { + name: "--random-wait", + description: "Wait from 0.5*WAIT...1.5*WAIT secs between retrievals", + }, + { name: "--no-proxy", description: "Explicitly turn off proxy" }, + { + name: ["-Q", "--quota=NUMBER"], + description: "Set retrieval quota to NUMBER", + }, + { + name: "--bind-address=ADDRESS", + description: "Bind to ADDRESS (hostname or IP) on local host", + }, + { name: "--limit-rate=RATE", description: "Limit download rate to RATE" }, + { name: "--no-dns-cache", description: "Disable caching DNS lookups" }, + { + name: "--restrict-file-names=OS", + description: "Restrict chars in file names to ones OS allows", + }, + { + name: "--ignore-case", + description: "Ignore case when matching files/directories", + }, + { + name: ["-4", "--inet4-only"], + description: "Connect only to IPv4 addresses", + }, + { + name: ["-6", "--inet6-only"], + description: "Connect only to IPv6 addresses", + }, + { + name: "--user=USER", + description: "Set both ftp and http user to USER", + }, + { + name: "--password=PASS", + description: "Set both ftp and http password to PASS", + }, + { name: "--ask-password", description: "Prompt for passwords" }, + { name: "--no-iri", description: "Turn off IRI support" }, + { + name: "--local-encoding=ENC", + description: "Use ENC as the local encoding for IRIs", + }, + { + name: "--remote-encoding=ENC", + description: "Use ENC as the default remote encoding", + }, + { name: "--unlink", description: "Remove file before clobber" }, + { + name: "--xattr", + description: "Turn on storage of metadata in extended file attributes", + }, + { + name: ["-nd", "--no-directories"], + description: "Don't create directories", + }, + { + name: ["-x", "--force-directories"], + description: "Force creation of directories", + }, + { + name: ["-nH", "--no-host-directories"], + description: "Don't create host directories", + }, + { + name: "--protocol-directories", + description: "Use protocol name in directories", + }, + { + name: ["-P", "--directory-prefix=PREFIX"], + description: "Save files to PREFIX/", + }, + { + name: "--cut-dirs=NUMBER", + description: "Ignore NUMBER remote directory components", + }, + { name: "--http-user=USER", description: "Set http user to USER" }, + { + name: "--http-password=PASS", + description: "Set http password to PASS", + }, + { name: "--no-cache", description: "Disallow server-cached data" }, + { + name: ["-E", "--adjust-extension"], + description: "Save HTML/CSS documents with proper extensions", + }, + { + name: "--ignore-length", + description: "Ignore 'Content-Length' header field", + }, + { + name: "--header=STRING", + description: "Insert STRING among the headers", + }, + { + name: "--compression=TYPE", + description: + "Choose compression, one of auto, gzip and none. (default: none)", + }, + { + name: "--max-redirect", + description: "Maximum redirections allowed per page", + }, + { name: "--proxy-user=USER", description: "Set USER as proxy username" }, + { + name: "--proxy-password=PASS", + description: "Set PASS as proxy password", + }, + { + name: "--referer=URL", + description: "Include 'Referer: URL' header in HTTP request", + }, + { name: "--save-headers", description: "Save the HTTP headers to file" }, + { + name: ["-U", "--user-agent=AGENT"], + description: "Identify as AGENT instead of Wget/VERSION", + }, + { + name: "--no-http-keep-alive", + description: "Disable HTTP keep-alive (persistent connections)", + }, + { name: "--no-cookies", description: "Don't use cookies" }, + { + name: "--load-cookies=FILE", + description: "Load cookies from FILE before session", + }, + { + name: "--save-cookies=FILE", + description: "Save cookies to FILE after session", + }, + { + name: "--keep-session-cookies", + description: "Load and save session (non-permanent) cookies", + }, + { + name: "--post-data=STRING", + description: "Use the POST method; send STRING as the data", + }, + { + name: "--post-file=FILE", + description: "Use the POST method; send contents of FILE", + }, + { + name: "--method=HTTPMethod", + description: 'Use method "HTTPMethod" in the request', + }, + { + name: "--body-data=STRING", + description: "Send STRING as data. --method MUST be set", + }, + { + name: "--body-file=FILE", + description: "Send contents of FILE. --method MUST be set", + }, + { + name: "--content-on-error", + description: "Output the received content on server errors", + }, + { + name: "--secure-protocol=PR", + description: "Choose secure protocol, one of auto, SSLv2,", + }, + { name: "--https-only", description: "Only follow secure HTTPS links" }, + { + name: "--no-check-certificate", + description: "Don't validate the server's certificate", + }, + { name: "--certificate=FILE", description: "Client certificate file" }, + { + name: "--certificate-type=TYPE", + description: "Client certificate type, PEM or DER", + }, + { name: "--private-key=FILE", description: "Private key file" }, + { + name: "--private-key-type=TYPE", + description: "Private key type, PEM or DER", + }, + { + name: "--ca-certificate=FILE", + description: "File with the bundle of CAs", + }, + { + name: "--ca-directory=DIR", + description: "Directory where hash list of CAs is stored", + }, + { name: "--crl-file=FILE", description: "File with bundle of CRLs" }, + { + name: "--ciphers=STR", + description: + "Set the priority string (GnuTLS) or cipher list string (OpenSSL) directly", + }, + { name: ["-r", "--recursive"], description: "Specify recursive download" }, + { + name: ["-l", "--level=NUMBER"], + description: "Maximum recursion depth (inf or 0 for infinite)", + }, + { + name: "--delete-after", + description: "Delete files locally after downloading them", + }, + { + name: ["-k", "--convert-links"], + description: "Make links in downloaded HTML or CSS point to local files", + }, + { + name: ["-K", "--backup-converted"], + description: "Before converting file X, back up as X.orig", + }, + { + name: ["-m", "--mirror"], + description: "Shortcut for -N -r -l inf --no-remove-listing", + }, + { + name: ["-p", "--page-requisites"], + description: "Get all images, etc. needed to display HTML page", + }, + { + name: ["-A", "--accept=LIST"], + description: "Comma-separated list of accepted extensions", + }, + { + name: ["-R", "--reject=LIST"], + description: "Comma-separated list of rejected extensions", + }, + { + name: "--accept-regex=REGEX", + description: "Regex matching accepted URLs", + }, + { + name: "--reject-regex=REGEX", + description: "Regex matching rejected URLs", + }, + { name: "--regex-type=TYPE", description: "Regex type (posix)" }, + { + name: ["-D", "--domains=LIST"], + description: "Comma-separated list of accepted domains", + }, + { + name: "--exclude-domains=LIST", + description: "Comma-separated list of rejected domains", + }, + { + name: "--follow-ftp", + description: "Follow FTP links from HTML documents", + }, + { + name: "--follow-tags=LIST", + description: "Comma-separated list of followed HTML tags", + }, + { + name: "--ignore-tags=LIST", + description: "Comma-separated list of ignored HTML tags", + }, + { + name: ["-H", "--span-hosts"], + description: "Go to foreign hosts when recursive", + }, + { name: ["-L", "--relative"], description: "Follow relative links only" }, + { + name: ["-I", "--include-directories=LIST"], + description: "List of allowed directories", + }, + { + name: ["-X", "--exclude-directories=LIST"], + description: "List of excluded directories", + }, + { + name: ["-np", "--no-parent"], + description: "Don't ascend to the parent directory", + }, + ], }; // GNU Wget 1.20.3, a non-interactive network retriever. diff --git a/extensions/terminal-suggest/src/completions/upstream/yarn.ts b/extensions/terminal-suggest/src/completions/upstream/yarn.ts index a3de493a1b3b..04c573a151be 100644 --- a/extensions/terminal-suggest/src/completions/upstream/yarn.ts +++ b/extensions/terminal-suggest/src/completions/upstream/yarn.ts @@ -82,7 +82,7 @@ const getGlobalPackagesGenerator: Fig.Generator = { name: dependencyName, icon: "📦", })); - } catch (e) { } + } catch (e) {} return []; }, @@ -101,7 +101,7 @@ const allDependenciesGenerator: Fig.Generator = { name: dependency.name.split("@")[0], icon: "📦", })); - } catch (e) { } + } catch (e) {} return []; }, }; @@ -127,7 +127,7 @@ const configList: Fig.Generator = { if (configObject) { return Object.keys(configObject).map((key) => ({ name: key })); } - } catch (e) { } + } catch (e) {} return []; }, @@ -367,7 +367,7 @@ export const createCLIsGenerator: Fig.Generator = { postProcess: function (out) { try { return JSON.parse(out).results.map( - (item: any) => + (item: { package: { name: string; description: string } }) => ({ name: item.package.name.substring(7), description: item.package.description, @@ -1550,9 +1550,9 @@ const completionSpec: Fig.Spec = { try { const workspacesDefinitions = isYarnV1 ? // transform Yarn V1 output to array of workspaces like Yarn V2 - await getWorkspacesDefinitionsV1() + await getWorkspacesDefinitionsV1() : // in yarn v>=2.0.0, workspaces definitions are a list of JSON lines - await getWorkspacesDefinitionsVOther(); + await getWorkspacesDefinitionsVOther(); const subcommands: Fig.Subcommand[] = workspacesDefinitions.map( ({ name, location }: { name: string; location: string }) => ({ @@ -1578,7 +1578,7 @@ const completionSpec: Fig.Spec = { name: script, })); } - } catch (e) { } + } catch (e) {} return []; }, }, diff --git a/extensions/terminal-suggest/src/constants.ts b/extensions/terminal-suggest/src/constants.ts index 8f04abc5fe63..25ada5a06aa2 100644 --- a/extensions/terminal-suggest/src/constants.ts +++ b/extensions/terminal-suggest/src/constants.ts @@ -42,10 +42,10 @@ export const upstreamSpecs = [ 'npm', 'yarn', 'python', + 'python3', 'pnpm', 'node', 'nvm', - 'npx', ]; diff --git a/extensions/terminal-suggest/src/fig/autocomplete-parser/parseArguments.ts b/extensions/terminal-suggest/src/fig/autocomplete-parser/parseArguments.ts index c26f3a77143d..6307848e2c74 100644 --- a/extensions/terminal-suggest/src/fig/autocomplete-parser/parseArguments.ts +++ b/extensions/terminal-suggest/src/fig/autocomplete-parser/parseArguments.ts @@ -35,6 +35,7 @@ import { } from './errors.js'; import { convertSubcommand, initializeDefault } from '../fig-autocomplete-shared'; import { exec, type ExecException } from 'child_process'; +import type { IFigExecuteExternals } from '../execute'; type ArgArrayState = { args: Array | null; @@ -89,6 +90,7 @@ export type ArgumentParserState = { // Used to exclude subcommand suggestions after user has entered a subcommand arg. haveEnteredSubcommandArgs: boolean; isEndOfOptions: boolean; + fileExtensions?: string[]; }; // Result with derived completionObj/currentArg from cached state. @@ -100,6 +102,7 @@ export type ArgumentParserResult = { commandIndex: number; suggestionFlags: SuggestionFlags; annotations: Annotation[]; + fileExtensions?: string[]; }; export const createArgState = (args?: Internal.Arg[]): ArgArrayState => { @@ -605,8 +608,15 @@ const historyExecuteShellCommand: Fig.ExecuteCommandFunction = async () => { ); }; -const getExecuteShellCommandFunction = (isParsingHistory = false) => - isParsingHistory ? historyExecuteShellCommand : () => { throw new Error('Not implemented'); }; +function getExecuteShellCommandFunction( + isParsingHistory = false, + executeExternals: IFigExecuteExternals, +) { + if (isParsingHistory) { + return historyExecuteShellCommand; + } + return executeExternals.executeCommand; +} // const getGenerateSpecCacheKey = ( // completionObj: Internal.Subcommand, @@ -749,6 +759,7 @@ export const getResultFromState = ( currentArg, searchTerm, suggestionFlags, + fileExtensions: state.fileExtensions, }; }; @@ -790,13 +801,14 @@ const parseArgumentsCached = async ( command: Command, context: Fig.ShellContext, spec: Fig.Spec, + executeExternals: IFigExecuteExternals, // authClient: AuthClient, isParsingHistory?: boolean, startIndex = 0, // localconsole: console.console = console, ): Promise => { // Route to cp.exec instead, we don't need to deal with ipc - const exec = getExecuteShellCommandFunction(isParsingHistory); + const exec = getExecuteShellCommandFunction(isParsingHistory, executeExternals); let currentCommand = command; let tokens = currentCommand.tokens.slice(startIndex); @@ -1036,7 +1048,15 @@ const parseArgumentsCached = async ( // } else { // parseArgumentsCache.set(cacheKey, state); // } - + if ('args' in spec) { + for (const arg of Array.isArray(spec.args) ? spec.args : [spec.args]) { + for (const generator of Array.isArray(arg?.generators) ? arg.generators : [arg?.generators]) { + if (generator?.filepathOptions?.extensions) { + state.fileExtensions = generator.filepathOptions.extensions; + } + } + } + } return state; }; @@ -1124,6 +1144,7 @@ export const parseArguments = async ( command: Command | null, context: Fig.ShellContext, spec: Fig.Spec, + executeExternals: IFigExecuteExternals, // authClient: AuthClient, isParsingHistory = false, // localconsole: console.console = console, @@ -1157,6 +1178,7 @@ export const parseArguments = async ( context, // authClient, spec, + executeExternals, isParsingHistory, 0, ); diff --git a/extensions/terminal-suggest/src/fig/autocomplete-tools/generators/index.ts b/extensions/terminal-suggest/src/fig/autocomplete-tools/generators/index.ts new file mode 100644 index 000000000000..38cacb06514e --- /dev/null +++ b/extensions/terminal-suggest/src/fig/autocomplete-tools/generators/index.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 { filepaths, FilepathsOptions, folders } from './src/filepaths'; +export * from './src/keyvalue'; +export { ai } from './src/ai'; diff --git a/extensions/terminal-suggest/src/fig/autocomplete-tools/generators/src/ai.ts b/extensions/terminal-suggest/src/fig/autocomplete-tools/generators/src/ai.ts new file mode 100644 index 000000000000..87e0b0891d5e --- /dev/null +++ b/extensions/terminal-suggest/src/fig/autocomplete-tools/generators/src/ai.ts @@ -0,0 +1,130 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +export type GeneratorFn = (args: { + tokens: string[]; + executeCommand: Fig.ExecuteCommandFunction; + generatorContext: Fig.GeneratorContext; +}) => Promise | T; + +const MAX_TOKENS = 4097; +const TOKEN_TO_CHAR_RATIO = 4; +const MARGIN_RATIO = 0.8; +const MAX_CHARS = MAX_TOKENS * TOKEN_TO_CHAR_RATIO * MARGIN_RATIO; + +/** + * A generator that uses the Fig AI API to generate suggestions. + * + * @param prompt The prompt to use for the AI. Can be a string or a generator function. + * @param message The message to send to the AI. Can be a string or a generator function. + * @param postProcess A function to post-process the AI's response. + * @param temperature The temperature to use for the AI. + * @returns A Fig generator. + */ +export function ai({ + name, + prompt, + message, + postProcess, + temperature, + splitOn, +}: { + name: string; + prompt?: string | GeneratorFn; + message: string | GeneratorFn | null; + postProcess?: (out: string) => Fig.Suggestion[]; + temperature?: number; + splitOn?: string; +}): Fig.Generator { + return { + scriptTimeout: 15000, + custom: async (tokens, executeCommand, generatorContext) => { + const settingOutput = await executeCommand({ + command: 'fig', + args: ['settings', '--format', 'json', 'autocomplete.ai.enabled'], + }); + + if (!JSON.parse(settingOutput.stdout)) { + return []; + } + + const promptString = + typeof prompt === 'function' + ? await prompt({ + tokens, + executeCommand, + generatorContext, + }) + : prompt; + + const messageString = + typeof message === 'function' + ? await message({ + tokens, + executeCommand, + generatorContext, + }) + : message; + + if (messageString === null || messageString.length === 0) { + console.warn('No message provided to AI generator'); + return []; + } + + const budget = MAX_CHARS - (promptString?.length ?? 0); + + const body = { + model: 'gpt-3.5-turbo', + source: 'autocomplete', + name, + messages: [ + ...(promptString + ? [ + { + role: 'system', + content: promptString, + }, + ] + : []), + { + role: 'user', + content: messageString.slice(0, budget), + }, + ], + temperature, + }; + + const bodyJson = JSON.stringify(body); + + const requestOutput = await executeCommand({ + command: 'fig', + args: ['_', 'request', '--route', '/ai/chat', '--method', 'POST', '--body', bodyJson], + }); + const json = JSON.parse(requestOutput.stdout); + + const a = + json?.choices + .map((c: any) => c?.message?.content) + .filter((c: unknown) => typeof c === 'string') + .flatMap((c: string) => + splitOn ? c.split(splitOn).filter((s) => s.trim().length > 0) : [c] + ) + .map((out: string) => { + if (postProcess) { + return postProcess(out); + } + const text = out.trim().replace(/\n/g, ' '); + // eslint-disable-next-line local/code-no-dangerous-type-assertions allow-any-unicode-next-line + return { + icon: '🪄', + name: text, + insertValue: `'${text}'`, + description: 'Generated by Fig AI', + } as Fig.Suggestion; + }) ?? []; + + return a; + }, + }; +} diff --git a/extensions/terminal-suggest/src/fig/autocomplete-tools/generators/src/filepaths.ts b/extensions/terminal-suggest/src/fig/autocomplete-tools/generators/src/filepaths.ts new file mode 100644 index 000000000000..59d19203f169 --- /dev/null +++ b/extensions/terminal-suggest/src/fig/autocomplete-tools/generators/src/filepaths.ts @@ -0,0 +1,224 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { ensureTrailingSlash, shellExpand } from './resolve'; + +export interface FilepathsOptions { + /** + * Show suggestions with any of these extensions. Do not include the leading dot. + */ + extensions?: string[]; + /** + * Show suggestions where the name exactly matches one of these strings + */ + equals?: string | string[]; + /** + * Show suggestions where the name matches this expression + */ + matches?: RegExp; + /** + * Will treat folders like files, filtering based on the name. + */ + filterFolders?: boolean; + /** + * Set properties of suggestions of type 'file'. + */ + editFileSuggestions?: Omit; + /** + * Set properties of suggestions of type 'folder'. + */ + editFolderSuggestions?: Omit; + /** + * Start to suggest filepaths and folders from this directory. + */ + rootDirectory?: string; + /** + * Set how the generator should display folders: + * - **Default:** `always` will always suggest folders. + * - `never`: will never suggest folders. + * - `only`: will show only folders and no files. + */ + showFolders?: 'always' | 'never' | 'only'; +} + +export function sortFilesAlphabetically(array: string[], skip: string[] = []): string[] { + const skipLower = skip.map((str) => str.toLowerCase()); + const results = array.filter((x) => !skipLower.includes(x.toLowerCase())); + + // Put all files beginning with . after all those that don't, sort alphabetically within each. + return [ + ...results.filter((x) => !x.startsWith('.')).sort((a, b) => a.localeCompare(b)), + ...results.filter((x) => x.startsWith('.')).sort((a, b) => a.localeCompare(b)), + '../', + ]; +} + +/** + * @param cwd - The current working directory when the user started typing the new path + * @param searchTerm - The path inserted by the user, it can be relative to cwd or absolute + * @returns The directory the user inserted, taking into account the cwd. + */ +export const getCurrentInsertedDirectory = ( + cwd: string | null, + searchTerm: string, + context: Fig.ShellContext +): string => { + if (cwd === null) { + return '/'; + } + const resolvedPath = shellExpand(searchTerm, context); + + const dirname = resolvedPath.slice(0, resolvedPath.lastIndexOf('/') + 1); + + if (dirname === '') { + return ensureTrailingSlash(cwd); + } + + return dirname.startsWith('/') ? dirname : `${ensureTrailingSlash(cwd)}${dirname}`; +}; + +/** + * Sugar over using the `filepaths` template with `filterTemplateSuggestions`. If any of the + * conditions match, the suggestion will be accepted. + * + * Basic filepath filters can be replaced with this generator. + * + * @example + * ``` + * // inside a `Fig.Arg`... + * generators: filepaths({ extensions: ['mjs', 'js', 'json'] }); + * ``` + */ +function filepathsFn(options: FilepathsOptions = {}): Fig.Generator { + const { + extensions = [], + equals = [], + matches, + filterFolders = false, + editFileSuggestions, + editFolderSuggestions, + rootDirectory, + showFolders = 'always', + } = options; + // TODO: automatically remove eventual leading dots + const extensionsSet = new Set(extensions); + const equalsSet = new Set(equals); + + // NOTE: If no filter is provided we should not run the filterSuggestions fn. + // !! When new filtering parameters are added we should increase this function + const shouldFilterSuggestions = () => extensions.length > 0 || equals.length > 0 || matches; + + const filterSuggestions = ( + suggestions: Fig.TemplateSuggestion[] = [] + ): Fig.TemplateSuggestion[] => { + if (!shouldFilterSuggestions()) { return suggestions; } + + return suggestions.filter(({ name = '', type }) => { + if (!filterFolders && type === 'folder') { return true; } + + if (equalsSet.has(name)) { return true; } + if (matches && !!name.match(matches)) { return true; } + // handle extensions + const [, ...suggestionExtensions] = name.split('.'); + if (suggestionExtensions.length >= 1) { + let i = suggestionExtensions.length - 1; + let stackedExtensions = suggestionExtensions[i]; + do { + if (extensionsSet.has(stackedExtensions)) { + return true; + } + i -= 1; + // `i` may become -1 which is not a valid index, but the extensionSet check at the beginning is not run in that case, + // so the wrong extension is not evaluated + stackedExtensions = [suggestionExtensions[i], stackedExtensions].join('.'); + } while (i >= 0); + } + return false; + }); + }; + + const postProcessSuggestions = ( + suggestions: Fig.TemplateSuggestion[] = [] + ): Fig.TemplateSuggestion[] => { + if (!editFileSuggestions && !editFolderSuggestions) { return suggestions; } + + return suggestions.map((suggestion) => ({ + ...suggestion, + ...((suggestion.type === 'file' ? editFileSuggestions : editFolderSuggestions) || {}) + })); + }; + + return { + trigger: (oldToken, newToken) => { + const oldLastSlashIndex = oldToken.lastIndexOf('/'); + const newLastSlashIndex = newToken.lastIndexOf('/'); + // If the final path segment has changed, trigger new suggestions + if (oldLastSlashIndex !== newLastSlashIndex) { + return true; + } + // Here, there could either be no slashes, or something before the + // final slash has changed. In the case where there are no slashes, + // we don't want to trigger on each keystroke, so explicitly return false. + if (oldLastSlashIndex === -1 && newLastSlashIndex === -1) { + return false; + } + // We know there's at least one slash in the string thanks to the case + // above, so trigger if anything before the final slash has changed. + return oldToken.slice(0, oldLastSlashIndex) !== newToken.slice(0, newLastSlashIndex); + }, + getQueryTerm: (token) => token.slice(token.lastIndexOf('/') + 1), + filepathOptions: options, + custom: async (_, executeCommand, generatorContext) => { + const { isDangerous, currentWorkingDirectory, searchTerm } = generatorContext; + const currentInsertedDirectory = + getCurrentInsertedDirectory( + rootDirectory ?? currentWorkingDirectory, + searchTerm, + generatorContext + ) ?? '/'; + + try { + const data = await executeCommand({ + command: 'ls', + args: ['-1ApL'], + cwd: currentInsertedDirectory, + }); + const sortedFiles = sortFilesAlphabetically(data.stdout.split('\n'), ['.DS_Store']); + + const generatorOutputArray: Fig.TemplateSuggestion[] = []; + // Then loop through them and add them to the generatorOutputArray + // depending on the template type + for (const name of sortedFiles) { + if (name) { + const templateType = name.endsWith('/') ? 'folders' : 'filepaths'; + if ( + (templateType === 'filepaths' && showFolders !== 'only') || + (templateType === 'folders' && showFolders !== 'never') + ) { + generatorOutputArray.push({ + type: templateType === 'filepaths' ? 'file' : 'folder', + name, + insertValue: name, + isDangerous, + context: { templateType }, + }); + } + } + } + + // Filter suggestions. This takes in the array of suggestions, filters it, + // and outputs an array of suggestions + return postProcessSuggestions(filterSuggestions(generatorOutputArray)); + } catch (err) { + return []; + } + }, + }; +} + +export const folders = Object.assign( + () => filepathsFn({ showFolders: 'only' }), + Object.freeze(filepathsFn({ showFolders: 'only' })) +); +export const filepaths = Object.assign(filepathsFn, Object.freeze(filepathsFn())); diff --git a/extensions/terminal-suggest/src/fig/autocomplete-tools/generators/src/keyvalue.ts b/extensions/terminal-suggest/src/fig/autocomplete-tools/generators/src/keyvalue.ts new file mode 100644 index 000000000000..d26a630e25e8 --- /dev/null +++ b/extensions/terminal-suggest/src/fig/autocomplete-tools/generators/src/keyvalue.ts @@ -0,0 +1,355 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +export type KeyValueSuggestions = + | string[] + | Fig.Suggestion[] + | NonNullable; + +/** @deprecated use `KeyValueSuggestions` */ +export type Suggestions = KeyValueSuggestions; + +export type CacheValue = boolean | 'keys' | 'values'; + +export interface ValueListInit { + /** String to use as the separator between keys and values */ + delimiter?: string; + + /** List of suggestions */ + values?: KeyValueSuggestions; + + /** Cache key and value suggestions */ + cache?: boolean; + + /** Insert the delimiter string after accepting a suggestion (default: false) */ + insertDelimiter?: boolean; + + /** Don't filter repeated values from suggestions (default: false) */ + allowRepeatedValues?: boolean; +} + +export interface KeyValueInit { + /** String to use as the separator between keys and values */ + separator?: string; + + /** List of key suggestions */ + keys?: KeyValueSuggestions; + + /** List of value suggestions */ + values?: KeyValueSuggestions; + + /** Cache key and value suggestions */ + cache?: CacheValue; + + /** Should the separator be inserted after a key? (default: true ) */ + insertSeparator?: boolean; +} + +export interface KeyValueListInit { + /** String to use as the separator between keys and values */ + separator?: string; + + /** String to use as the separator between key-value pairs */ + delimiter?: string; + + /** List of key suggestions */ + keys?: KeyValueSuggestions; + + /** List of value suggestions */ + values?: KeyValueSuggestions; + + /** Cache key and value suggestions */ + cache?: CacheValue; + + /** Should the separator be inserted after a key? (default: true ) */ + insertSeparator?: boolean; + + /** Insert the delimiter string after accepting a value suggestion (default: false) */ + insertDelimiter?: boolean; + + /** Don't filter repeated keys from suggestions (default: false) */ + allowRepeatedKeys?: boolean; + + /** Don't filter repeated values from suggestions (default: true) */ + allowRepeatedValues?: boolean; +} + +/** Cache of Fig suggestions using the string[]/Suggestion[]/function as a key */ +const suggestionCache = new Map(); + +function appendToInsertValue(append: string, suggestions: Fig.Suggestion[]): Fig.Suggestion[] { + if (append.length === 0) { + return suggestions; + } + return suggestions.map((item) => + item.insertValue ? item : { ...item, insertValue: item.name + append } + ); +} + +async function kvSuggestionsToFigSuggestions( + suggestions: KeyValueSuggestions, + append: string, + init: Parameters> +): Promise { + if (typeof suggestions === 'function') { + const out = await suggestions(...init); + return appendToInsertValue(append, out?.filter(o => !!o) as Fig.Suggestion[]); + } + if (typeof suggestions[0] === 'string') { + const out = (suggestions as string[]).map((name) => ({ name })); + return appendToInsertValue(append, out); + } + return appendToInsertValue(append, suggestions as Fig.Suggestion[]); +} + +async function getSuggestions( + suggestions: KeyValueSuggestions, + append: string, + useSuggestionCache: boolean, + init: Parameters> +): Promise { + if (useSuggestionCache || Array.isArray(suggestions)) { + let value = suggestionCache.get(suggestions); + if (value === undefined) { + value = await kvSuggestionsToFigSuggestions(suggestions, append, init); + suggestionCache.set(suggestions, value); + } + return value; + } + return kvSuggestionsToFigSuggestions(suggestions, append, init); +} + +function shouldUseCache(isKey: boolean, cache: CacheValue) { + if (typeof cache === 'string') { + return (isKey && cache === 'keys') || (!isKey && cache === 'values'); + } + return cache; +} + +/** Get the final index of any of the strings */ +function lastIndexOf(haystack: string, ...needles: readonly string[]) { + return Math.max(...needles.map((needle) => haystack.lastIndexOf(needle))); +} + +function removeRepeatSuggestions( + alreadyUsed: string[], + suggestions: readonly Fig.Suggestion[] +): Fig.Suggestion[] { + const seen = new Set(alreadyUsed); + return suggestions.filter((suggestion) => { + if (typeof suggestion.name === 'string') { + return !seen.has(suggestion.name); + } + return !suggestion.name?.some((name) => seen.has(name)); + }); +} + +/** + * Create a generator that gives suggestions for val,val,... arguments. You + * can use a `string[]` or `Fig.Suggestion[]` for the values. + * + * You can set `cache: true` to enable caching results. The suggestions are cached + * globally using the function as a key, so enabling caching for any one generator + * will set the cache values for the functions for the entire spec. This behavior + * can be used to compose expensive generators without incurring a cost every time + * they're used. + * + * The primary use of this is to enable the same caching behavior as `keyValue` + * and `keyValueList`. If your goal is to create a $PATH-like value, use a generator + * object literal: `{ template: 'filepaths', trigger: ':', getQueryTerm: ':' }` + */ +export function valueList({ + delimiter = ',', + values = [], + cache = false, + insertDelimiter = false, + allowRepeatedValues = false, +}: ValueListInit): Fig.Generator { + return { + trigger: (newToken, oldToken) => + newToken.lastIndexOf(delimiter) !== oldToken.lastIndexOf(delimiter), + + getQueryTerm: (token) => token.slice(token.lastIndexOf(delimiter) + delimiter.length), + + custom: async (...init) => { + const out = await getSuggestions(values, insertDelimiter ? delimiter : '', cache, init); + if (allowRepeatedValues) { + return out; + } + const [tokens] = init; + const valuesInList = tokens[tokens.length - 1]?.split(delimiter); + return removeRepeatSuggestions(valuesInList, out); + }, + }; +} + +/** + * Create a generator that gives suggestions for key=value arguments. You + * can use a `string[]` or `Fig.Suggestion[]` for the keys and values, or a + * function with the same signature as `Fig.Generator['custom']`. + * + * You can set `cache: true` to enable caching results. The suggestions are cached + * globally using the function as a key, so enabling caching for any one generator + * will set the cache values for the functions for the entire spec. This behavior + * can be used to copmpose expensive key/value generators without incurring the + * initial cost every time they're used. + * + * Note that you should only cache generators that produce the same output regardless + * of their input. You can cache either the keys or values individually using `'keys'` + * or `'values'` as the `cache` property value. + * + * @example + * + * ```typescript + * // set-values a=1 b=3 c=2 + * const spec: Fig.Spec = { + * name: 'set-values', + * args: { + * name: 'values', + * isVariadic: true, + * generators: keyValue({ + * keys: ['a', 'b', 'c'], + * values: ['1', '2', '3'], + * }), + * }, + * } + * ``` + * + * @example The separator between keys and values can be customized (default: `=`) + * + * ```typescript + * // key1:value + * keyValue({ + * separator: ':', + * keys: [ + * { name: 'key1', icon: 'fig://icon?type=string' }, + * { name: 'key2', icon: 'fig://icon?type=string' }, + * ], + * }), + * ``` + */ +export function keyValue({ + separator = '=', + keys = [], + values = [], + cache = false, + insertSeparator = true, +}: KeyValueInit): Fig.Generator { + return { + trigger: (newToken, oldToken) => newToken.indexOf(separator) !== oldToken.indexOf(separator), + getQueryTerm: (token) => token.slice(token.indexOf(separator) + 1), + custom: async (...init) => { + const [tokens] = init; + const finalToken = tokens[tokens.length - 1]; + const isKey = !finalToken.includes(separator); + const suggestions = isKey ? keys : values; + const useCache = shouldUseCache(isKey, cache); + const append = isKey ? (insertSeparator ? separator : '') : ''; + return getSuggestions(suggestions, append, useCache, init); + }, + }; +} + +/** + * Create a generator that gives suggestions for `k=v,k=v,...` arguments. You + * can use a `string[]` or `Fig.Suggestion[]` for the keys and values, or a + * function with the same signature as `Fig.Generator['custom']` + * + * You can set `cache: true` to enable caching results. The suggestions are cached + * globally using the function as a key, so enabling caching for any one generator + * will set the cache values for the functions for the entire spec. This behavior + * can be used to copmpose expensive key/value generators without incurring the + * initial cost every time they're used. + * + * Note that you should only cache generators that produce the same output regardless + * of their input. You can cache either the keys or values individually using `'keys'` + * or `'values'` as the `cache` property value. + * + * @example + * + * ```typescript + * // set-values a=1,b=3,c=2 + * const spec: Fig.Spec = { + * name: 'set-values', + * args: { + * name: 'values', + * generators: keyValueList({ + * keys: ['a', 'b', 'c'], + * values: ['1', '2', '3'], + * }), + * }, + * } + * ``` + * + * @example + * + * The separator between keys and values can be customized. It's `=` by + * default. You can also change the key/value pair delimiter, which is `,` + * by default. + * + * ```typescript + * // key1:value&key2:another + * keyValueList({ + * separator: ':', + * delimiter: '&' + * keys: [ + * { name: 'key1', icon: 'fig://icon?type=string' }, + * { name: 'key2', icon: 'fig://icon?type=string' }, + * ], + * }), + * ``` + */ +export function keyValueList({ + separator = '=', + delimiter = ',', + keys = [], + values = [], + cache = false, + insertSeparator = true, + insertDelimiter = false, + allowRepeatedKeys = false, + allowRepeatedValues = true, +}: KeyValueListInit): Fig.Generator { + return { + trigger: (newToken, oldToken) => { + const newTokenIdx = lastIndexOf(newToken, separator, delimiter); + const oldTokenIdx = lastIndexOf(oldToken, separator, delimiter); + return newTokenIdx !== oldTokenIdx; + }, + getQueryTerm: (token) => { + const index = lastIndexOf(token, separator, delimiter); + return token.slice(index + 1); + }, + custom: async (...init) => { + const [tokens] = init; + + const finalToken = tokens[tokens.length - 1]; + const index = lastIndexOf(finalToken, separator, delimiter); + const isKey = index === -1 || finalToken.slice(index, index + separator.length) !== separator; + + const suggestions = isKey ? keys : values; + const useCache = shouldUseCache(isKey, cache); + const append = isKey ? (insertSeparator ? separator : '') : insertDelimiter ? delimiter : ''; + const out = await getSuggestions(suggestions, append, useCache, init); + + if (isKey) { + if (allowRepeatedKeys) { + return out; + } + const existingKeys = finalToken + .split(delimiter) + .map((chunk) => chunk.slice(0, chunk.indexOf(separator))); + return removeRepeatSuggestions(existingKeys, out); + } + + if (allowRepeatedValues) { + return out; + } + const existingValues = finalToken + .split(delimiter) + .map((chunk) => chunk.slice(chunk.indexOf(separator) + separator.length)); + return removeRepeatSuggestions(existingValues, out); + }, + }; +} diff --git a/extensions/terminal-suggest/src/fig/autocomplete-tools/generators/src/resolve.ts b/extensions/terminal-suggest/src/fig/autocomplete-tools/generators/src/resolve.ts new file mode 100644 index 000000000000..da43cc352d66 --- /dev/null +++ b/extensions/terminal-suggest/src/fig/autocomplete-tools/generators/src/resolve.ts @@ -0,0 +1,36 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +export const ensureTrailingSlash = (str: string) => (str.endsWith('/') ? str : `${str}/`); + +const replaceTilde = (path: string, homeDir: string) => { + if (path.startsWith('~') && (path.length === 1 || path.charAt(1) === '/')) { + return path.replace('~', homeDir); + } + return path; +}; + +const replaceVariables = (path: string, environmentVariables: Record) => { + // Replace simple $VAR variables + const resolvedSimpleVariables = path.replace(/\$([A-Za-z0-9_]+)/g, (key) => { + const envKey = key.slice(1); + return environmentVariables[envKey] ?? key; + }); + + // Replace complex ${VAR} variables + const resolvedComplexVariables = resolvedSimpleVariables.replace( + /\$\{([A-Za-z0-9_]+)(?::-([^}]+))?\}/g, + (match, envKey, defaultValue) => environmentVariables[envKey] ?? defaultValue ?? match + ); + + return resolvedComplexVariables; +}; + +export const shellExpand = (path: string, context: Fig.ShellContext): string => { + const { environmentVariables } = context; + return replaceVariables( + replaceTilde(path, environmentVariables?.HOME ?? '~'), + environmentVariables + ); +}; diff --git a/extensions/terminal-suggest/src/fig/autocomplete/generators/customSuggestionsGenerator.ts b/extensions/terminal-suggest/src/fig/autocomplete/generators/customSuggestionsGenerator.ts index e08fea6745a8..4aa412ba248c 100644 --- a/extensions/terminal-suggest/src/fig/autocomplete/generators/customSuggestionsGenerator.ts +++ b/extensions/terminal-suggest/src/fig/autocomplete/generators/customSuggestionsGenerator.ts @@ -3,7 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { executeCommand } from '../../api-binding-wrappers/executeCommandWrappers'; + +import { FilepathsOptions } from '../../autocomplete-tools/generators'; +import { IFigExecuteExternals } from '../../execute'; import { runCachedGenerator, GeneratorContext, @@ -13,7 +15,8 @@ import { export async function getCustomSuggestions( generator: Fig.Generator, context: GeneratorContext, -): Promise { + executableExternals: IFigExecuteExternals +): Promise { if (!generator.custom) { return []; } @@ -23,6 +26,10 @@ export async function getCustomSuggestions( return []; } + if (generator.filepathOptions) { + return generator.filepathOptions; + } + const { tokenArray, currentWorkingDirectory, @@ -37,7 +44,7 @@ export async function getCustomSuggestions( generator, context, () => - generator.custom!(tokenArray, executeCommand, { + generator.custom!(tokenArray, executableExternals.executeCommand, { currentWorkingDirectory, currentProcess, sshPrefix: '', diff --git a/extensions/terminal-suggest/src/fig/autocomplete/generators/scriptSuggestionsGenerator.ts b/extensions/terminal-suggest/src/fig/autocomplete/generators/scriptSuggestionsGenerator.ts index 123a33071809..bd8463265c49 100644 --- a/extensions/terminal-suggest/src/fig/autocomplete/generators/scriptSuggestionsGenerator.ts +++ b/extensions/terminal-suggest/src/fig/autocomplete/generators/scriptSuggestionsGenerator.ts @@ -3,7 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { executeCommandTimeout } from '../../api-binding-wrappers/executeCommand'; + +import { IFigExecuteExternals } from '../../execute'; import { GeneratorContext, haveContextForGenerator, @@ -14,6 +15,7 @@ export async function getScriptSuggestions( generator: Fig.Generator, context: GeneratorContext, defaultTimeout: number = 5000, + executeExternals: IFigExecuteExternals ): Promise { const { script, postProcess, splitOn } = generator; if (!script) { @@ -26,7 +28,7 @@ export async function getScriptSuggestions( } try { - const { isDangerous, tokenArray, currentWorkingDirectory } = context; + const { isDangerous, tokenArray, currentWorkingDirectory, environmentVariables } = context; // A script can either be a string or a function that returns a string. // If the script is a function, run it, and get the output string. const commandToRun = @@ -44,6 +46,7 @@ export async function getScriptSuggestions( command: commandToRun[0], args: commandToRun.slice(1), cwd: currentWorkingDirectory, + env: environmentVariables }; } else { executeCommandInput = { @@ -62,10 +65,12 @@ export async function getScriptSuggestions( const { stdout } = await runCachedGenerator( generator, context, - () => executeCommandTimeout(executeCommandInput, timeout), + () => { + return executeExternals.executeCommandTimeout(executeCommandInput, timeout); + }, generator.cache?.cacheKey ?? JSON.stringify(executeCommandInput), ); - + // let result: Array | undefined = []; // If we have a splitOn function diff --git a/extensions/terminal-suggest/src/fig/autocomplete/state/generators.ts b/extensions/terminal-suggest/src/fig/autocomplete/state/generators.ts index 729f8621b22a..4a0b72b55c38 100644 --- a/extensions/terminal-suggest/src/fig/autocomplete/state/generators.ts +++ b/extensions/terminal-suggest/src/fig/autocomplete/state/generators.ts @@ -10,6 +10,7 @@ import { GeneratorState, GeneratorContext } from '../generators/helpers'; import { sleep } from '../../shared/utils'; import type { ArgumentParserResult } from '../../autocomplete-parser/parseArguments'; import { getCustomSuggestions } from '../generators/customSuggestionsGenerator'; +import { IFigExecuteExternals } from '../../execute'; export const shellContextSelector = ({ figState, @@ -36,7 +37,10 @@ const getGeneratorContext = (state: AutocompleteState): GeneratorContext => { export const createGeneratorState = ( // setNamed: NamedSetState, state: AutocompleteState, -): { triggerGenerators: ((result: ArgumentParserResult) => GeneratorState[]) } => { + executeExternals?: IFigExecuteExternals +): { + triggerGenerators: (result: ArgumentParserResult, executeExternals: IFigExecuteExternals) => GeneratorState[]; +} => { // function updateGenerator( // generatorState: GeneratorState, // getUpdate: () => Partial, @@ -76,11 +80,9 @@ export const createGeneratorState = ( // } // return generatorState; // } - - const triggerGenerator = (currentState: GeneratorState) => { - console.info('Triggering generator', { currentState }); + const triggerGenerator = (currentState: GeneratorState, executeExternals: IFigExecuteExternals) => { const { generator, context } = currentState; - let request: Promise; + let request: Promise; if (generator.template) { // TODO: Implement template generators @@ -91,11 +93,12 @@ export const createGeneratorState = ( request = getScriptSuggestions( generator, context, - // getSetting(SETTINGS.SCRIPT_TIMEOUT, 5000), + undefined, // getSetting(SETTINGS.SCRIPT_TIMEOUT, 5000), + executeExternals ); } else { - request = getCustomSuggestions(generator, context); + request = getCustomSuggestions(generator, context, executeExternals); // filepaths/folders templates are now a sugar for two custom generators, we need to filter // the suggestion created by those two custom generators // if (generator.filterTemplateSuggestions) { @@ -118,6 +121,7 @@ export const createGeneratorState = ( const triggerGenerators = ( parserResult: ArgumentParserResult, + executeExternals: IFigExecuteExternals, ): GeneratorState[] => { const { parserResult: { currentArg: previousArg, searchTerm: previousSearchTerm }, @@ -182,7 +186,7 @@ export const createGeneratorState = ( const result = previousGeneratorState?.result || []; const generatorState = { generator, context, result, loading: true }; - const getTriggeredState = () => triggerGenerator(generatorState); + const getTriggeredState = () => triggerGenerator(generatorState, executeExternals); if (currentArg?.debounce) { sleep( typeof currentArg.debounce === 'number' && currentArg.debounce > 0 diff --git a/extensions/terminal-suggest/src/fig/api-binding-wrappers/executeCommand.ts b/extensions/terminal-suggest/src/fig/execute.ts similarity index 75% rename from extensions/terminal-suggest/src/fig/api-binding-wrappers/executeCommand.ts rename to extensions/terminal-suggest/src/fig/execute.ts index f45b02ebfad0..2c642019b671 100644 --- a/extensions/terminal-suggest/src/fig/api-binding-wrappers/executeCommand.ts +++ b/extensions/terminal-suggest/src/fig/execute.ts @@ -3,9 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { osIsWindows } from '../../helpers/os'; -import { spawnHelper2 } from '../../shell/common'; -import { withTimeout } from '../shared/utils'; +import { osIsWindows } from '../helpers/os'; +import { spawnHelper2 } from '../shell/common'; +import { withTimeout } from './shared/utils'; export const cleanOutput = (output: string) => output @@ -20,8 +20,7 @@ export const executeCommandTimeout = async ( ): Promise => { const command = [input.command, ...input.args].join(' '); try { - console.info(`About to run shell command '${command}'`); - const start = performance.now(); + console.debug(`About to run shell command '${command}'`); const result = await withTimeout( Math.max(timeout, input.timeout ?? 0), spawnHelper2(input.command, input.args, { @@ -30,11 +29,6 @@ export const executeCommandTimeout = async ( timeout: input.timeout, }) ); - const end = performance.now(); - console.info(`Result of shell command '${command}'`, { - result, - time: end - start, - }); const cleanStdout = cleanOutput(result.stdout); const cleanStderr = cleanOutput(result.stderr); @@ -54,3 +48,15 @@ export const executeCommandTimeout = async ( throw err; } }; + + +export const executeCommand: Fig.ExecuteCommandFunction = (args) => + executeCommandTimeout(args); + +export interface IFigExecuteExternals { + executeCommand: Fig.ExecuteCommandFunction; + executeCommandTimeout: ( + input: Fig.ExecuteCommandInput, + timeout: number, + ) => Promise; +} diff --git a/extensions/terminal-suggest/src/fig/figInterface.ts b/extensions/terminal-suggest/src/fig/figInterface.ts index 3c663d8be352..d9fb84832c07 100644 --- a/extensions/terminal-suggest/src/fig/figInterface.ts +++ b/extensions/terminal-suggest/src/fig/figInterface.ts @@ -16,10 +16,13 @@ import type { ICompletionResource } from '../types'; import { osIsWindows } from '../helpers/os'; import { removeAnyFileExtension } from '../helpers/file'; import type { EnvironmentVariable } from './api-bindings/types'; +import { asArray, availableSpecs } from '../terminalSuggestMain'; +import { IFigExecuteExternals } from './execute'; export interface IFigSpecSuggestionsResult { filesRequested: boolean; foldersRequested: boolean; + fileExtensions?: string[]; hasCurrentArg: boolean; items: vscode.TerminalCompletionItem[]; } @@ -34,12 +37,14 @@ export async function getFigSuggestions( env: Record, name: string, precedingText: string, - token?: vscode.CancellationToken + executeExternals: IFigExecuteExternals, + token?: vscode.CancellationToken, ): Promise { const result: IFigSpecSuggestionsResult = { filesRequested: false, foldersRequested: false, hasCurrentArg: false, + fileExtensions: undefined, items: [], }; for (const spec of specs) { @@ -48,11 +53,10 @@ export async function getFigSuggestions( if (!specLabels) { continue; } - for (const specLabel of specLabels) { const availableCommand = (osIsWindows() ? availableCommands.find(command => (typeof command.label === 'string' ? command.label : command.label.label).match(new RegExp(`${specLabel}(\\.[^ ]+)?$`))) - : availableCommands.find(command => (typeof command.label === 'string' ? command.label : command.label.label).startsWith(specLabel))); + : availableCommands.find(command => (typeof command.label === 'string' ? command.label : command.label.label) === (specLabel))); if (!availableCommand || (token && token.isCancellationRequested)) { continue; } @@ -64,31 +68,38 @@ export async function getFigSuggestions( result.items.push(createCompletionItem( terminalContext.cursorPosition, prefix, - { label: { label: specLabel, description } }, + { + label: { label: specLabel, description }, + kind: vscode.TerminalCompletionItemKind.Method + }, description, - availableCommand.detail) - ); + availableCommand.detail + )); } continue; } const commandAndAliases = (osIsWindows() ? availableCommands.filter(command => specLabel === removeAnyFileExtension(command.definitionCommand ?? (typeof command.label === 'string' ? command.label : command.label.label))) - : availableCommands.filter(command => specLabel === (command.definitionCommand ?? command.label))); + : availableCommands.filter(command => specLabel === (command.definitionCommand ?? (typeof command.label === 'string' ? command.label : command.label.label)))); if ( !(osIsWindows() ? commandAndAliases.some(e => precedingText.startsWith(`${removeAnyFileExtension((typeof e.label === 'string' ? e.label : e.label.label))} `)) - : commandAndAliases.some(e => precedingText.startsWith(`${e.label} `))) + : commandAndAliases.some(e => precedingText.startsWith(`${typeof e.label === 'string' ? e.label : e.label.label} `))) ) { - // the spec label is not the first word in the command line, so do not provide options or args continue; } - const completionItemResult = await getFigSpecSuggestions(spec, terminalContext, prefix, shellIntegrationCwd, env, name, token); + const actualSpec = availableCommand.definitionCommand ? availableSpecs.find(s => s.name === availableCommand.definitionCommand) : spec; + if (!actualSpec) { + continue; + } + const completionItemResult = await getFigSpecSuggestions(actualSpec, terminalContext, prefix, shellIntegrationCwd, env, name, executeExternals, token); result.hasCurrentArg ||= !!completionItemResult?.hasCurrentArg; if (completionItemResult) { result.filesRequested ||= completionItemResult.filesRequested; result.foldersRequested ||= completionItemResult.foldersRequested; + result.fileExtensions = completionItemResult.fileExtensions; if (completionItemResult.items) { result.items.push(...completionItemResult.items); } @@ -105,10 +116,12 @@ async function getFigSpecSuggestions( shellIntegrationCwd: vscode.Uri | undefined, env: Record, name: string, - token?: vscode.CancellationToken + executeExternals: IFigExecuteExternals, + token?: vscode.CancellationToken, ): Promise { let filesRequested = false; let foldersRequested = false; + let fileExtensions: string[] | undefined; const command = getCommand(terminalContext.commandLine, {}, terminalContext.cursorPosition); if (!command || !shellIntegrationCwd) { @@ -121,11 +134,11 @@ async function getFigSpecSuggestions( currentProcess: name, // TODO: pass in aliases }; - const parsedArguments: ArgumentParserResult = await parseArguments(command, shellContext, spec); + const parsedArguments: ArgumentParserResult = await parseArguments(command, shellContext, spec, executeExternals); const items: vscode.TerminalCompletionItem[] = []; // TODO: Pass in and respect cancellation token - const completionItemResult = await collectCompletionItemResult(command, parsedArguments, prefix, terminalContext, shellIntegrationCwd, env, items); + const completionItemResult = await collectCompletionItemResult(command, parsedArguments, prefix, terminalContext, shellIntegrationCwd, env, items, executeExternals); if (token?.isCancellationRequested) { return undefined; } @@ -133,13 +146,15 @@ async function getFigSpecSuggestions( if (completionItemResult) { filesRequested = completionItemResult.filesRequested; foldersRequested = completionItemResult.foldersRequested; + fileExtensions = completionItemResult.filesRequested ? parsedArguments.fileExtensions : undefined; } return { filesRequested, foldersRequested, + fileExtensions, hasCurrentArg: !!parsedArguments.currentArg, - items: items, + items, }; } @@ -152,10 +167,12 @@ export async function collectCompletionItemResult( terminalContext: { commandLine: string; cursorPosition: number }, shellIntegrationCwd: vscode.Uri | undefined, env: Record, - items: vscode.TerminalCompletionItem[] -): Promise<{ filesRequested: boolean; foldersRequested: boolean } | undefined> { + items: vscode.TerminalCompletionItem[], + executeExternals: IFigExecuteExternals +): Promise<{ filesRequested: boolean; foldersRequested: boolean; fileExtensions: string[] | undefined } | undefined> { let filesRequested = false; let foldersRequested = false; + let fileExtensions: string[] | undefined; const addSuggestions = async (specArgs: SpecArg[] | Record | undefined, kind: vscode.TerminalCompletionItemKind, parsedArguments?: ArgumentParserResult) => { if (kind === vscode.TerminalCompletionItemKind.Argument && parsedArguments?.currentArg?.generators) { @@ -191,10 +208,19 @@ export async function collectCompletionItemResult( fuzzySearchEnabled: false, userFuzzySearchEnabled: false, }; - const s = createGeneratorState(state); - const generatorResults = s.triggerGenerators(parsedArguments); + const s = createGeneratorState(state, executeExternals); + const generatorResults = s.triggerGenerators(parsedArguments, executeExternals); for (const generatorResult of generatorResults) { for (const item of (await generatorResult?.request) ?? []) { + if (item.type === 'file') { + filesRequested = true; + foldersRequested = true; + fileExtensions = item._internal?.fileExtensions as string[] | undefined; + } + if (item.type === 'folder') { + foldersRequested = true; + } + if (!item.name) { continue; } @@ -233,6 +259,35 @@ export async function collectCompletionItemResult( return { filesRequested, foldersRequested }; } const flagsToExclude = kind === vscode.TerminalCompletionItemKind.Flag ? parsedArguments?.passedOptions.map(option => option.name).flat() : undefined; + + function addItem(label: string, item: SpecArg) { + if (flagsToExclude?.includes(label)) { + return; + } + + let itemKind = kind; + const lastArgType: string | undefined = parsedArguments?.annotations.at(-1)?.type; + if (lastArgType === 'subcommand_arg') { + if (typeof item === 'object' && 'args' in item && (asArray(item.args ?? [])).length > 0) { + itemKind = vscode.TerminalCompletionItemKind.Option; + } + } + else if (lastArgType === 'option_arg') { + itemKind = vscode.TerminalCompletionItemKind.OptionValue; + } + + items.push( + createCompletionItem( + terminalContext.cursorPosition, + prefix, + { label }, + undefined, + typeof item === 'string' ? item : item.description, + itemKind, + ) + ); + } + if (Array.isArray(specArgs)) { for (const item of specArgs) { const suggestionLabels = getFigSuggestionLabel(item); @@ -240,36 +295,12 @@ export async function collectCompletionItemResult( continue; } for (const label of suggestionLabels) { - if (flagsToExclude?.includes(label)) { - continue; - } - items.push( - createCompletionItem( - terminalContext.cursorPosition, - prefix, - { label }, - undefined, - typeof item === 'string' ? item : item.description, - kind - ) - ); + addItem(label, item); } } } else { for (const [label, item] of Object.entries(specArgs)) { - if (flagsToExclude?.includes(label)) { - continue; - } - items.push( - createCompletionItem( - terminalContext.cursorPosition, - prefix, - { label }, - undefined, - typeof item === 'string' ? item : item.description, - kind - ) - ); + addItem(label, item); } } }; @@ -284,7 +315,7 @@ export async function collectCompletionItemResult( await addSuggestions(parsedArguments.completionObj.options, vscode.TerminalCompletionItemKind.Flag, parsedArguments); } - return { filesRequested, foldersRequested }; + return { filesRequested, foldersRequested, fileExtensions }; } function convertEnvRecordToArray(env: Record): EnvironmentVariable[] { diff --git a/extensions/terminal-suggest/src/helpers/filepaths.ts b/extensions/terminal-suggest/src/helpers/filepaths.ts new file mode 100644 index 000000000000..ff35aae22556 --- /dev/null +++ b/extensions/terminal-suggest/src/helpers/filepaths.ts @@ -0,0 +1,17 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export function filepaths(options: { extensions?: string[]; editFileSuggestions?: { priority: number } }): Fig.Generator { + return { + custom: async (tokens, executeCommand, generatorContext) => { + const fileExtensionsMap: Record = { fileExtensions: options.extensions || [] }; + return [{ type: 'file', _internal: fileExtensionsMap }, { type: 'folder' }]; + }, + trigger: (oldToken, newToken) => { + return true; + }, + getQueryTerm: (token) => token + }; +} diff --git a/extensions/terminal-suggest/src/helpers/promise.ts b/extensions/terminal-suggest/src/helpers/promise.ts new file mode 100644 index 000000000000..14e86bc04811 --- /dev/null +++ b/extensions/terminal-suggest/src/helpers/promise.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 createTimeoutPromise(timeout: number, defaultValue: T): Promise { + return new Promise(resolve => setTimeout(() => resolve(defaultValue), timeout)); +} diff --git a/extensions/terminal-suggest/src/shell/bash.ts b/extensions/terminal-suggest/src/shell/bash.ts index c2189fffb705..157449089715 100644 --- a/extensions/terminal-suggest/src/shell/bash.ts +++ b/extensions/terminal-suggest/src/shell/bash.ts @@ -16,7 +16,8 @@ export async function getBashGlobals(options: ExecOptionsWithStringEncoding, exi } async function getAliases(options: ExecOptionsWithStringEncoding): Promise { - return getAliasesHelper('bash', ['-ic', 'alias'], /^alias (?[a-zA-Z0-9\.:-]+)='(?.+)'$/, options); + const args = process.platform === 'darwin' ? ['-icl', 'alias'] : ['-ic', 'alias']; + return getAliasesHelper('bash', args, /^alias (?[a-zA-Z0-9\.:-]+)='(?.+)'$/, options); } export async function getBuiltins( @@ -48,7 +49,7 @@ export async function getBuiltins( completions.push({ label: { label: cmd, description }, detail, - documentation, + documentation: new vscode.MarkdownString(documentation), kind: vscode.TerminalCompletionItemKind.Method }); diff --git a/extensions/terminal-suggest/src/shell/fish.ts b/extensions/terminal-suggest/src/shell/fish.ts index cc2fbaff6e0c..98c1ac09556a 100644 --- a/extensions/terminal-suggest/src/shell/fish.ts +++ b/extensions/terminal-suggest/src/shell/fish.ts @@ -3,21 +3,87 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import * as vscode from 'vscode'; import type { ICompletionResource } from '../types'; -import { execHelper, getAliasesHelper } from './common'; +import { getAliasesHelper } from './common'; import { type ExecOptionsWithStringEncoding } from 'node:child_process'; +import { fishBuiltinsCommandDescriptionsCache } from './fishBuiltinsCache'; + +const commandDescriptionsCache: Map | undefined = parseCache(fishBuiltinsCommandDescriptionsCache); export async function getFishGlobals(options: ExecOptionsWithStringEncoding, existingCommands?: Set): Promise<(string | ICompletionResource)[]> { return [ ...await getAliases(options), - ...await getBuiltins(options, existingCommands), + ...await getBuiltins(options), ]; } -async function getBuiltins(options: ExecOptionsWithStringEncoding, existingCommands?: Set): Promise { - const compgenOutput = await execHelper('functions -n', options); - const filter = (cmd: string) => cmd && !existingCommands?.has(cmd); - return compgenOutput.split(', ').filter(filter); +async function getBuiltins(options: ExecOptionsWithStringEncoding): Promise<(string | ICompletionResource)[]> { + const completions: ICompletionResource[] = []; + + // Use the cache directly for all commands + for (const cmd of [...commandDescriptionsCache!.keys()]) { + try { + const result = getCommandDescription(cmd); + if (result) { + completions.push({ + label: { label: cmd, description: result.description }, + detail: result.args, + documentation: new vscode.MarkdownString(result.documentation), + kind: vscode.TerminalCompletionItemKind.Method + }); + } else { + console.warn(`Fish command "${cmd}" not found in cache.`); + completions.push({ + label: cmd, + kind: vscode.TerminalCompletionItemKind.Method + }); + } + } catch (e) { + // Ignore errors + completions.push({ + label: cmd, + kind: vscode.TerminalCompletionItemKind.Method + }); + } + } + + return completions; +} + +export function getCommandDescription(command: string): { documentation?: string; description?: string; args?: string | undefined } | undefined { + if (!commandDescriptionsCache) { + return undefined; + } + const result = commandDescriptionsCache.get(command); + if (!result) { + return undefined; + } + + if (result.shortDescription) { + return { + description: result.shortDescription, + args: result.args, + documentation: result.description + }; + } else { + return { + description: result.description, + args: result.args, + documentation: result.description + }; + } +} + +function parseCache(cache: Object): Map | undefined { + if (!cache) { + return undefined; + } + const result = new Map(); + for (const [key, value] of Object.entries(cache)) { + result.set(key, value); + } + return result; } async function getAliases(options: ExecOptionsWithStringEncoding): Promise { diff --git a/extensions/terminal-suggest/src/shell/fishBuiltinsCache.ts b/extensions/terminal-suggest/src/shell/fishBuiltinsCache.ts new file mode 100644 index 000000000000..2a5aefc4aa67 --- /dev/null +++ b/extensions/terminal-suggest/src/shell/fishBuiltinsCache.ts @@ -0,0 +1,301 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export const fishBuiltinsCommandDescriptionsCache = { + ".": { + "shortDescription": "source - evaluate contents of file", + "description": "source evaluates the commands of the specified FILE in the current\nshell as a new block of code. This is different from starting a new\nprocess to perform the commands (i.e. fish < FILE) since the commands\nwill be evaluated by the current shell, which means that changes in\nshell variables will affect the current shell. If additional\narguments are specified after the file name, they will be inserted\ninto the argv variable. The argv variable will not include the name\nof the sourced file.\n\nfish will search the working directory to resolve relative paths but\nwill not search PATH .\n\nIf no file is specified and stdin is not the terminal, or if the file\nname - is used, stdin will be read.\n\nThe exit status of source is the exit status of the last job to\nexecute. If something goes wrong while opening or reading the file,\nsource exits with a non-zero status.\n\n. (a single period) is an alias for the source command. The use of .\nis deprecated in favour of source, and . will be removed in a future\nversion of fish.\n\nsource creates a new local scope; set --local within a sourced block\nwill not affect variables in the enclosing scope.\n\nThe -h or --help option displays help about using this command.\n\nEXAMPLE\n\n source ~/.config/fish/config.fish\n # Causes fish to re-read its initialization file.\n\nCAVEATS\nIn fish versions prior to 2.3.0, the argv variable would have a\nsingle element (the name of the sourced file) if no arguments are\npresent. Otherwise, it would contain arguments without the name of\nthe sourced file. That behavior was very confusing and unlike other\nshells such as bash and zsh.", + "args": "source FILE [ARGUMENTS ...]\nSOMECOMMAND | source" + }, + ":": { + "shortDescription": "No operation command", + "description": "The `:` command is a no-op (no operation) command that returns a successful (zero) exit status. It can be used as a placeholder in scripts where a command is syntactically required but no action is desired." + }, + "[": { + "shortDescription": "Test if a statement is true", + "description": "Evaluate an expression and return a status of true (0) or false (non-zero). Unlike the `test` command, the `[` command requires a closing `]`.", + "args": "EXPRESSION ]" + }, + "_": { + "shortDescription": "", + "description": "" + }, + "abbr": { + "shortDescription": "manage fish abbreviations", + "description": "abbr manages abbreviations - user-defined words that are replaced\nwith longer phrases when entered.\n\nNOTE:\n Only typed-in commands use abbreviations. Abbreviations are not\n expanded in scripts.\n\nFor example, a frequently-run command like git checkout can be\nabbreviated to gco. After entering gco and pressing Space or Enter,\nthe full text git checkout will appear in the command line. To avoid\nexpanding something that looks like an abbreviation, the default\nControl+Space binding inserts a space without expanding.\n\nAn abbreviation may match a literal word, or it may match a pattern\ngiven by a regular expression. When an abbreviation matches a word,\nthat word is replaced by new text, called its expansion. This\nexpansion may be a fixed new phrase, or it can be dynamically created\nvia a fish function. This expansion occurs after pressing space or\nenter.\n\nCombining these features, it is possible to create custom syntaxes,\nwhere a regular expression recognizes matching tokens, and the\nexpansion function interprets them. See the Examples section.\n\nChanged in version 3.6.0: Previous versions of this allowed saving\nabbreviations in universal variables. That's no longer possible.\nExisting variables will still be imported and abbr --erase will also\nerase the variables. We recommend adding abbreviations to\nconfig.fish by just adding the abbr --add command. When you run\nabbr, you will see output like this\n\n > abbr\n abbr -a -- foo bar # imported from a universal variable, see `help abbr`\n\nIn that case you should take the part before the # comment and save\nit in config.fish, then you can run abbr --erase to remove the\nuniversal variable:\n\n > abbr >> ~/.config/fish/config.fish\n > abbr --erase (abbr --list)\n\nADD SUBCOMMAND\nabbr [-a | --add] NAME [--position command | anywhere] [-r | --regex PATTERN]\n [--set-cursor[=MARKER]] ([-f | --function FUNCTION] | EXPANSION)\n\nabbr --add creates a new abbreviation. With no other options, the\nstring NAME is replaced by EXPANSION.\n\nWith --position command, the abbreviation will only expand when it is\npositioned as a command, not as an argument to another command. With\n--position anywhere the abbreviation may expand anywhere in the\ncommand line. The default is command.\n\nWith --regex, the abbreviation matches using the regular expression\ngiven by PATTERN, instead of the literal NAME. The pattern is\ninterpreted using PCRE2 syntax and must match the entire token. If\nmultiple abbreviations match the same token, the last abbreviation\nadded is used.\n\nWith --set-cursor=MARKER, the cursor is moved to the first occurrence\nof MARKER in the expansion. The MARKER value is erased. The MARKER\nmay be omitted (i.e. simply --set-cursor), in which case it defaults\nto %.\n\nWith -f FUNCTION or --function FUNCTION, FUNCTION is treated as the\nname of a fish function instead of a literal replacement. When the\nabbreviation matches, the function will be called with the matching\ntoken as an argument. If the function's exit status is 0 (success),\nthe token will be replaced by the function's output; otherwise the\ntoken will be left unchanged. No EXPANSION may be given separately.\n\n Examples\n\n abbr --add gco git checkout\n\nAdd a new abbreviation where gco will be replaced with git checkout.\n\n abbr -a --position anywhere -- -C --color\n\nAdd a new abbreviation where -C will be replaced with --color. The --\nallows -C to be treated as the name of the abbreviation, instead of\nan option.\n\n abbr -a L --position anywhere --set-cursor \"% | less\"\n\nAdd a new abbreviation where L will be replaced with | less, placing\nthe cursor before the pipe.\n\n function last_history_item\n echo $history[1]\n end\n abbr -a !! --position anywhere --function last_history_item\n\nThis first creates a function last_history_item which outputs the\nlast entered command. It then adds an abbreviation which replaces !!\nwith the result of calling this function. Taken together, this is\nsimilar to the !! history expansion feature of bash.\n\n function vim_edit\n echo vim $argv\n end\n abbr -a vim_edit_texts --position command --regex \".+\\.txt\" --function vim_edit\n\nThis first creates a function vim_edit which prepends vim before its\nargument. It then adds an abbreviation which matches commands ending\nin .txt, and replaces the command with the result of calling this\nfunction. This allows text files to be \"executed\" as a command to\nopen them in vim, similar to the \"suffix alias\" feature in zsh.\n\n abbr 4DIRS --set-cursor=! \"$(string join \\n -- 'for dir in */' 'cd $dir' '!' 'cd ..' 'end')\"\n\nThis creates an abbreviation \"4DIRS\" which expands to a multi-line\nloop \"template.\" The template enters each directory and then leaves\nit. The cursor is positioned ready to enter the command to run in\neach directory, at the location of the !, which is itself erased.\n\nOTHER SUBCOMMANDS\n\n abbr --rename OLD_NAME NEW_NAME\n\nRenames an abbreviation, from OLD_NAME to NEW_NAME\n\n abbr [-s | --show]\n\nShow all abbreviations in a manner suitable for import and export\n\n abbr [-l | --list]\n\nPrints the names of all abbreviation\n\n abbr [-e | --erase] NAME\n\nErases the abbreviation with the given name\n\n abbr -q or --query [NAME...]\n\nReturn 0 (true) if one of the NAME is an abbreviation.\n\n abbr -h or --help\n\nDisplays help for the abbr command.", + "args": "abbr --add NAME [--position command | anywhere] [-r | --regex PATTERN]\n[--set-cursor[=MARKER]] ([-f | --function FUNCTION] | EXPANSION)\nabbr --erase NAME ...\nabbr --rename OLD_WORD NEW_WORD\nabbr --show\nabbr --list\nabbr --query NAME ..." + }, + "and": { + "shortDescription": "conditionally execute a command", + "description": "and is used to execute a command if the previous command was\nsuccessful (returned a status of 0).\n\nand statements may be used as part of the condition in an while or if\nblock.\n\nand does not change the current exit status itself, but the command\nit runs most likely will. The exit status of the last foreground\ncommand to exit can always be accessed using the $status variable.\n\nThe -h or --help option displays help about using this command.\n\nEXAMPLE\nThe following code runs the make command to build a program. If the\nbuild succeeds, make's exit status is 0, and the program is\ninstalled. If either step fails, the exit status is 1, and make clean\nis run, which removes the files created by the build process.\n\n make; and make install; or make clean\n\nSEE ALSO\n\n• or command\n\n• not command", + "args": "PREVIOUS; and COMMAND" + }, + "argparse": { + "shortDescription": "parse options passed to a fish script or function", + "description": "This command makes it easy for fish scripts and functions to handle\narguments. You pass arguments that define the known options, followed\nby a literal --, then the arguments to be parsed (which might also\ninclude a literal --). argparse then sets variables to indicate the\npassed options with their values, and sets $argv to the remaining\narguments. See the usage section below.\n\nEach option specification (OPTION_SPEC) is written in the domain\nspecific language described below. All OPTION_SPECs must appear after\nany argparse flags and before the -- that separates them from the\narguments to be parsed.\n\nEach option that is seen in the ARG list will result in variables\nnamed _flag_X, where X is the short flag letter and the long flag\nname (if they are defined). For example a --help option could cause\nargparse to define one variable called _flag_h and another called\n_flag_help.\n\nThe variables will be set with local scope (i.e., as if the script\nhad done set -l _flag_X). If the flag is a boolean (that is, it just\nis passed or not, it doesn't have a value) the values are the short\nand long flags seen. If the option is not a boolean the values will\nbe zero or more values corresponding to the values collected when the\nARG list is processed. If the flag was not seen the flag variable\nwill not be set.\n\nOPTIONS\nThe following argparse options are available. They must appear before\nall OPTION_SPECs:\n\n-n or --name\n The command name for use in error messages. By default the\n current function name will be used, or argparse if run outside\n of a function.\n\n-x or --exclusive OPTIONS\n A comma separated list of options that are mutually exclusive.\n You can use this more than once to define multiple sets of\n mutually exclusive options. You give either the short or long\n version of each option, and you still need to otherwise define\n the options.\n\n-N or --min-args NUMBER\n The minimum number of acceptable non-option arguments. The\n default is zero.\n\n-X or --max-args NUMBER\n The maximum number of acceptable non-option arguments. The\n default is infinity.\n\n-i or --ignore-unknown\n Ignores unknown options, keeping them and their arguments in\n $argv instead.\n\n-s or --stop-nonopt\n Causes scanning the arguments to stop as soon as the first\n non-option argument is seen. Among other things, this is\n useful to implement subcommands that have their own options.\n\n-h or --help\n Displays help about using this command.\n\nUSAGE\nTo use this command, pass the option specifications (OPTION_SPEC), a\nmandatory --, and then the arguments to be parsed.\n\nA simple example:\n\n argparse --name=my_function 'h/help' 'n/name=' -- $argv\n or return\n\nIf $argv is empty then there is nothing to parse and argparse returns\nzero to indicate success. If $argv is not empty then it is checked\nfor flags -h, --help, -n and --name. If they are found they are\nremoved from the arguments and local variables called _flag_OPTION\nare set so the script can determine which options were seen. If $argv\ndoesn't have any errors, like a missing mandatory value for an\noption, then argparse exits with a status of zero. Otherwise it\nwrites appropriate error messages to stderr and exits with a status\nof one.\n\nThe or return means that the function returns argparse's status if it\nfailed, so if it goes on argparse succeeded.\n\nThe -- argument is required. You do not have to include any option\nspecifications or arguments after the -- but you must include the --.\nFor example, this is acceptable:\n\n set -l argv foo\n argparse 'h/help' 'n/name' -- $argv\n argparse --min-args=1 -- $argv\n\nBut this is not:\n\n set -l argv\n argparse 'h/help' 'n/name' $argv\n\nThe first -- seen is what allows the argparse command to reliably\nseparate the option specifications and options to argparse itself\n(like --ignore-unknown) from the command arguments, so it is\nrequired.\n\nOPTION SPECIFICATIONS\nEach option specification consists of:\n\n• An optional alphanumeric short flag character, followed by a / if\n the short flag can be used by someone invoking your command or, for\n backwards compatibility, a - if it should not be exposed as a valid\n short flag (in which case it will also not be exposed as a flag\n variable).\n\n• An optional long flag name, which if not present the short flag can\n be used, and if that is also not present, an error is reported\n\n• Nothing if the flag is a boolean that takes no argument or is an\n integer flag, or\n\n • = if it requires a value and only the last instance of the\n flag is saved, or\n\n • =? if it takes an optional value and only the last instance of\n the flag is saved, or\n\n • =+ if it requires a value and each instance of the flag is\n saved.\n\n• Optionally a ! followed by fish script to validate the value.\n Typically this will be a function to run. If the exit status is\n zero the value for the flag is valid. If non-zero the value is\n invalid. Any error messages should be written to stdout (not\n stderr). See the section on Flag Value Validation for more\n information.\n\nSee the fish_opt command for a friendlier but more verbose way to\ncreate option specifications.\n\nIf a flag is not seen when parsing the arguments then the\ncorresponding _flag_X var(s) will not be set.\n\nINTEGER FLAG\nSometimes commands take numbers directly as options, like foo -55. To\nallow this one option spec can have the # modifier so that any\ninteger will be understood as this flag, and the last number will be\ngiven as its value (as if = was used).\n\nThe # must follow the short flag letter (if any), and other modifiers\nlike = are not allowed, except for - (for backwards compatibility):\n\n m#maximum\n\nThis does not read numbers given as +NNN, only those that look like\nflags - -NNN.\n\nNOTE: OPTIONAL ARGUMENTS\nAn option defined with =? can take optional arguments. Optional\narguments have to be directly attached to the option they belong to.\n\nThat means the argument will only be used for the option if you use\nit like:\n\n cmd --flag=value\n # or\n cmd -fvalue\n\nbut not if used like:\n\n cmd --flag value\n # \"value\" here will be used as a positional argument\n # and \"--flag\" won't have an argument.\n\nIf this weren't the case, using an option without an optional\nargument would be difficult if you also wanted to use positional\narguments.\n\nFor example:\n\n grep --color auto\n # Here \"auto\" will be used as the search string,\n # \"color\" will not have an argument and will fall back to the default,\n # which also *happens to be* auto.\n grep --color always\n # Here grep will still only use color \"auto\"matically\n # and search for the string \"always\".\n\nThis isn't specific to argparse but common to all things using\ngetopt(3) (if they have optional arguments at all). That grep example\nis how GNU grep actually behaves.\n\nFLAG VALUE VALIDATION\nSometimes you need to validate the option values. For example, that\nit is a valid integer within a specific range, or an ip address, or\nsomething entirely different. You can always do this after argparse\nreturns but you can also request that argparse perform the validation\nby executing arbitrary fish script. To do so simply append an !\n(exclamation-mark) then the fish script to be run. When that code is\nexecuted three vars will be defined:\n\n• _argparse_cmd will be set to the value of the value of the argparse\n --name value.\n\n• _flag_name will be set to the short or long flag that being\n processed.\n\n• _flag_value will be set to the value associated with the flag being\n processed.\n\nThese variables are passed to the function as local exported\nvariables.\n\nThe script should write any error messages to stdout, not stderr. It\nshould return a status of zero if the flag value is valid otherwise a\nnon-zero status to indicate it is invalid.\n\nFish ships with a _validate_int function that accepts a --min and\n--max flag. Let's say your command accepts a -m or --max flag and the\nminimum allowable value is zero and the maximum is 5. You would\ndefine the option like this: m/max=!_validate_int --min 0 --max 5.\nThe default if you just call _validate_int without those flags is to\nsimply check that the value is a valid integer with no limits on the\nmin or max value allowed.\n\nHere are some examples of flag validations:\n\n # validate that a path is a directory\n argparse 'p/path=!test -d \"$_flag_value\"' -- --path $__fish_config_dir\n # validate that a function does not exist\n argparse 'f/func=!not functions -q \"$_flag_value\"' -- -f alias\n # validate that a string matches a regex\n argparse 'c/color=!string match -rq \\'^#?[0-9a-fA-F]{6}$\\' \"$_flag_value\"' -- -c 'c0ffee'\n # validate with a validator function\n argparse 'n/num=!_validate_int --min 0 --max 99' -- --num 42\n\nEXAMPLE OPTION_SPECS\nSome OPTION_SPEC examples:\n\n• h/help means that both -h and --help are valid. The flag is a\n boolean and can be used more than once. If either flag is used then\n _flag_h and _flag_help will be set to however either flag was seen,\n as many times as it was seen. So it could be set to -h, -h and\n --help, and count $_flag_h would yield \"3\".\n\n• help means that only --help is valid. The flag is a boolean and can\n be used more than once. If it is used then _flag_help will be set\n as above. Also h-help (with an arbitrary short letter) for\n backwards compatibility.\n\n• longonly= is a flag --longonly that requires an option, there is no\n short flag or even short flag variable.\n\n• n/name= means that both -n and --name are valid. It requires a\n value and can be used at most once. If the flag is seen then\n _flag_n and _flag_name will be set with the single mandatory value\n associated with the flag.\n\n• n/name=? means that both -n and --name are valid. It accepts an\n optional value and can be used at most once. If the flag is seen\n then _flag_n and _flag_name will be set with the value associated\n with the flag if one was provided else it will be set with no\n values.\n\n• name=+ means that only --name is valid. It requires a value and can\n be used more than once. If the flag is seen then _flag_name will be\n set with the values associated with each occurrence.\n\n• x means that only -x is valid. It is a boolean that can be used\n more than once. If it is seen then _flag_x will be set as above.\n\n• x=, x=?, and x=+ are similar to the n/name examples above but there\n is no long flag alternative to the short flag -x.\n\n• #max (or #-max) means that flags matching the regex \"^--?\\d+$\" are\n valid. When seen they are assigned to the variable _flag_max. This\n allows any valid positive or negative integer to be specified by\n prefixing it with a single \"-\". Many commands support this idiom.\n For example head -3 /a/file to emit only the first three lines of\n /a/file.\n\n• n#max means that flags matching the regex \"^--?\\d+$\" are valid.\n When seen they are assigned to the variables _flag_n and _flag_max.\n This allows any valid positive or negative integer to be specified\n by prefixing it with a single \"-\". Many commands support this\n idiom. For example head -3 /a/file to emit only the first three\n lines of /a/file. You can also specify the value using either flag:\n -n NNN or --max NNN in this example.\n\n• #longonly causes the last integer option to be stored in\n _flag_longonly.\n\nAfter parsing the arguments the argv variable is set with local scope\nto any values not already consumed during flag processing. If there\nare no unbound values the variable is set but count $argv will be\nzero.\n\nIf an error occurs during argparse processing it will exit with a\nnon-zero status and print error messages to stderr.\n\nEXAMPLES\nA simple use:\n\n argparse h/help -- $argv\n or return\n\n if set -q _flag_help\n # TODO: Print help here\n return 0\n end\n\nThis just wants one option - -h / --help. Any other option is an\nerror. If it is given it prints help and exits.\n\nHow fish_add_path - add to the path parses its args:\n\n argparse -x g,U -x P,U -x a,p g/global U/universal P/path p/prepend a/append h/help m/move v/verbose n/dry-run -- $argv\n\nThere are a variety of boolean flags, all with long and short\nversions. A few of these cannot be used together, and that is what\nthe -x flag is used for. -x g,U means that --global and --universal\nor their short equivalents conflict, and if they are used together\nyou get an error. In this case you only need to give the short or\nlong flag, not the full option specification.\n\nAfter this it figures out which variable it should operate on\naccording to the --path flag:\n\n set -l var fish_user_paths\n set -q _flag_path\n and set var PATH\n\nLIMITATIONS\nOne limitation with --ignore-unknown is that, if an unknown option is\ngiven in a group with known options, the entire group will be kept in\n$argv. argparse will not do any permutations here.\n\nFor instance:\n\n argparse --ignore-unknown h -- -ho\n echo $_flag_h # is -h, because -h was given\n echo $argv # is still -ho\n\nThis limitation may be lifted in future.\n\nAdditionally, it can only parse known options up to the first unknown\noption in the group - the unknown option could take options, so it\nisn't clear what any character after an unknown option means.", + "args": "argparse [OPTIONS] OPTION_SPEC ... -- [ARG ...]" + }, + "begin": { + "shortDescription": "start a new block of code", + "description": "begin is used to create a new block of code.\n\nA block allows the introduction of a new variable scope, redirection\nof the input or output of a set of commands as a group, or to specify\nprecedence when using the conditional commands like and.\n\nThe block is unconditionally executed. begin; ...; end is equivalent\nto if true; ...; end.\n\nbegin does not change the current exit status itself. After the block\nhas completed, $status will be set to the status returned by the most\nrecent command.\n\nThe -h or --help option displays help about using this command.\n\nEXAMPLE\nThe following code sets a number of variables inside of a block\nscope. Since the variables are set inside the block and have local\nscope, they will be automatically deleted when the block ends.\n\n begin\n set -l PIRATE Yarrr\n\n ...\n end\n\n echo $PIRATE\n # This will not output anything, since the PIRATE variable\n # went out of scope at the end of the block\n\nIn the following code, all output is redirected to the file out.html.\n\n begin\n echo $xml_header\n echo $html_header\n if test -e $file\n ...\n end\n ...\n end > out.html", + "args": "begin; [COMMANDS ...]; end" + }, + "bg": { + "shortDescription": "send jobs to background", + "description": "bg sends jobs to the background, resuming them if they are stopped.\n\nA background job is executed simultaneously with fish, and does not\nhave access to the keyboard. If no job is specified, the last job to\nbe used is put in the background. If PID is specified, the jobs\ncontaining the specified process IDs are put in the background.\n\nFor compatibility with other shells, job expansion syntax is\nsupported for bg. A PID of the format %1 will be interpreted as the\nPID of job 1. Job numbers can be seen in the output of jobs.\n\nWhen at least one of the arguments isn't a valid job specifier, bg\nwill print an error without backgrounding anything.\n\nWhen all arguments are valid job specifiers, bg will background all\nmatching jobs that exist.\n\nThe -h or --help option displays help about using this command.\n\nEXAMPLE\nbg 123 456 789 will background the jobs that contain processes 123,\n456 and 789.\n\nIf only 123 and 789 exist, it will still background them and print an\nerror about 456.\n\nbg 123 banana or bg banana 123 will complain that \"banana\" is not a\nvalid job specifier.\n\nbg %1 will background job 1.", + "args": "bg [PID ...]" + }, + "bind": { + "shortDescription": "handle fish key bindings", + "description": "bind manages bindings.\n\nIt can add bindings if given a SEQUENCE of characters to bind to.\nThese should be written as fish escape sequences. The most important\nof these are \\c for the control key, and \\e for escape, and because\nof historical reasons also the Alt key (sometimes also called\n\"Meta\").\n\nFor example, Alt+W can be written as \\ew, and Control+X (^X) can be\nwritten as \\cx. Note that Alt-based key bindings are case sensitive\nand Control-based key bindings are not. This is a constraint of\ntext-based terminals, not fish.\n\nThe generic key binding that matches if no other binding does can be\nset by specifying a SEQUENCE of the empty string (that is, '' ). For\nmost key bindings, it makes sense to bind this to the self-insert\nfunction (i.e. bind '' self-insert). This will insert any keystrokes\nnot specifically bound to into the editor. Non-printable characters\nare ignored by the editor, so this will not result in control\nsequences being inserted.\n\nIf the -k switch is used, the name of a key (such as 'down', 'up' or\n'backspace') is used instead of a sequence. The names used are the\nsame as the corresponding curses variables, but without the 'key'\nprefix. (See terminfo(5) for more information, or use bind\n--key-names for a list of all available named keys). Normally this\nwill print an error if the current $TERM entry doesn't have a given\nkey, unless the -s switch is given.\n\nTo find out what sequence a key combination sends, you can use\nfish_key_reader.\n\nCOMMAND can be any fish command, but it can also be one of a set of\nspecial input functions. These include functions for moving the\ncursor, operating on the kill-ring, performing tab completion, etc.\nUse bind --function-names for a complete list of these input\nfunctions.\n\nWhen COMMAND is a shellscript command, it is a good practice to put\nthe actual code into a function and simply bind to the function name.\nThis way it becomes significantly easier to test the function while\nediting, and the result is usually more readable as well.\n\nNOTE:\n Special input functions cannot be combined with ordinary shell\n script commands. The commands must be entirely a sequence of\n special input functions (from bind -f) or all shell script\n commands (i.e., valid fish script). To run special input functions\n from regular fish script, use commandline -f (see also\n commandline). If a script produces output, it should finish by\n calling commandline -f repaint to tell fish that a repaint is in\n order.\n\nIf no SEQUENCE is provided, all bindings (or just the bindings in the\ngiven MODE) are printed. If SEQUENCE is provided but no COMMAND, just\nthe binding matching that sequence is printed.\n\nTo save custom key bindings, put the bind statements into\nconfig.fish. Alternatively, fish also automatically executes a\nfunction called fish_user_key_bindings if it exists.\n\nKey bindings may use \"modes\", which mimics Vi's modal input behavior.\nThe default mode is \"default\". Every key binding applies to a single\nmode; you can specify which one with -M MODE. If the key binding\nshould change the mode, you can specify the new mode with -m\nNEW_MODE. The mode can be viewed and changed via the $fish_bind_mode\nvariable. If you want to change the mode from inside a fish function,\nuse set fish_bind_mode MODE.\n\nOPTIONS\nThe following options are available:\n\n-k or --key\n Specify a key name, such as 'left' or 'backspace' instead of a\n character sequence\n\n-K or --key-names\n Display a list of available key names. Specifying -a or --all\n includes keys that don't have a known mapping\n\n-f or --function-names\n Display a list of available input functions\n\n-L or --list-modes\n Display a list of defined bind modes\n\n-M MODE or --mode MODE\n Specify a bind mode that the bind is used in. Defaults to\n \"default\"\n\n-m NEW_MODE or --sets-mode NEW_MODE\n Change the current mode to NEW_MODE after this binding is\n executed\n\n-e or --erase\n Erase the binding with the given sequence and mode instead of\n defining a new one. Multiple sequences can be specified with\n this flag. Specifying -a or --all with -M or --mode erases\n all binds in the given mode regardless of sequence.\n Specifying -a or --all without -M or --mode erases all binds\n in all modes regardless of sequence.\n\n-a or --all\n See --erase and --key-names\n\n--preset and --user\n Specify if bind should operate on user or preset bindings.\n User bindings take precedence over preset bindings when fish\n looks up mappings. By default, all bind invocations work on\n the \"user\" level except for listing, which will show both\n levels. All invocations except for inserting new bindings can\n operate on both levels at the same time (if both --preset and\n --user are given). --preset should only be used in full\n binding sets (like when working on fish_vi_key_bindings).\n\n-s or --silent\n Silences some of the error messages, including for unknown key\n names and unbound sequences.\n\n-h or --help\n Displays help about using this command.\n\nSPECIAL INPUT FUNCTIONS\nThe following special input functions are available:\n\nand only execute the next function if the previous succeeded\n (note: only some functions report success)\n\naccept-autosuggestion\n accept the current autosuggestion\n\nbackward-char\n move one character to the left. If the completion pager is\n active, select the previous completion instead.\n\nbackward-bigword\n move one whitespace-delimited word to the left\n\nbackward-delete-char\n deletes one character of input to the left of the cursor\n\nbackward-kill-bigword\n move the whitespace-delimited word to the left of the cursor\n to the killring\n\nbackward-kill-line\n move everything from the beginning of the line to the cursor\n to the killring\n\nbackward-kill-path-component\n move one path component to the left of the cursor to the\n killring. A path component is everything likely to belong to a\n path component, i.e. not any of the following: /={,}'\":@\n |;<>&, plus newlines and tabs.\n\nbackward-kill-word\n move the word to the left of the cursor to the killring. The\n \"word\" here is everything up to punctuation or whitespace.\n\nbackward-word\n move one word to the left\n\nbeginning-of-buffer\n moves to the beginning of the buffer, i.e. the start of the\n first line\n\nbeginning-of-history\n move to the beginning of the history\n\nbeginning-of-line\n move to the beginning of the line\n\nbegin-selection\n start selecting text\n\ncancel cancel the current commandline and replace it with a new empty\n one\n\ncancel-commandline\n cancel the current commandline and replace it with a new empty\n one, leaving the old one in place with a marker to show that\n it was cancelled\n\ncapitalize-word\n make the current word begin with a capital letter\n\nclear-screen\n clears the screen and redraws the prompt. if the terminal\n doesn't support clearing the screen it is the same as repaint.\n\ncomplete\n guess the remainder of the current token\n\ncomplete-and-search\n invoke the searchable pager on completion options (for\n convenience, this also moves backwards in the completion\n pager)\n\ndelete-char\n delete one character to the right of the cursor\n\ndelete-or-exit\n delete one character to the right of the cursor, or exit the\n shell if the commandline is empty\n\ndown-line\n move down one line\n\ndowncase-word\n make the current word lowercase\n\nend-of-buffer\n moves to the end of the buffer, i.e. the end of the first line\n\nend-of-history\n move to the end of the history\n\nend-of-line\n move to the end of the line\n\nend-selection\n end selecting text\n\nexpand-abbr\n expands any abbreviation currently under the cursor\n\nexecute\n run the current commandline\n\nexit exit the shell\n\nforward-bigword\n move one whitespace-delimited word to the right\n\nforward-char\n move one character to the right; or if at the end of the\n commandline, accept the current autosuggestion. If the\n completion pager is active, select the next completion\n instead.\n\nforward-single-char\n move one character to the right; or if at the end of the\n commandline, accept a single char from the current\n autosuggestion.\n\nforward-word\n move one word to the right; or if at the end of the\n commandline, accept one word from the current autosuggestion.\n\nhistory-pager\n invoke the searchable pager on history (incremental search);\n or if the history pager is already active, search further\n backwards in time.\n\nhistory-pager-delete\n permanently delete the history item selected in the history\n pager\n\nhistory-search-backward\n search the history for the previous match\n\nhistory-search-forward\n search the history for the next match\n\nhistory-prefix-search-backward\n search the history for the previous prefix match\n\nhistory-prefix-search-forward\n search the history for the next prefix match\n\nhistory-token-search-backward\n search the history for the previous matching argument\n\nhistory-token-search-forward\n search the history for the next matching argument\n\nforward-jump and backward-jump\n read another character and jump to its next occurence\n after/before the cursor\n\nforward-jump-till and backward-jump-till\n jump to right before the next occurence\n\nrepeat-jump and repeat-jump-reverse\n redo the last jump in the same/opposite direction\n\nkill-bigword\n move the next whitespace-delimited word to the killring\n\nkill-line\n move everything from the cursor to the end of the line to the\n killring\n\nkill-selection\n move the selected text to the killring\n\nkill-whole-line\n move the line (including the following newline) to the\n killring. If the line is the last line, its preceeding newline\n is also removed\n\nkill-inner-line\n move the line (without the following newline) to the killring\n\nkill-word\n move the next word to the killring\n\nnextd-or-forward-word\n if the commandline is empty, then move forward in the\n directory history, otherwise move one word to the right; or if\n at the end of the commandline, accept one word from the\n current autosuggestion.\n\nor only execute the next function if the previous did not succeed\n (note: only some functions report failure)\n\npager-toggle-search\n toggles the search field if the completions pager is visible;\n or if used after history-pager, search forwards in time.\n\nprevd-or-backward-word\n if the commandline is empty, then move backward in the\n directory history, otherwise move one word to the left\n\nrepaint\n reexecutes the prompt functions and redraws the prompt (also\n force-repaint for backwards-compatibility)\n\nrepaint-mode\n reexecutes the fish_mode_prompt and redraws the prompt. This\n is useful for vi-mode. If no fish_mode_prompt exists or it\n prints nothing, it acts like a normal repaint.\n\nself-insert\n inserts the matching sequence into the command line\n\nself-insert-notfirst\n inserts the matching sequence into the command line, unless\n the cursor is at the beginning\n\nsuppress-autosuggestion\n remove the current autosuggestion. Returns true if there was a\n suggestion to remove.\n\nswap-selection-start-stop\n go to the other end of the highlighted text without changing\n the selection\n\ntranspose-chars\n transpose two characters to the left of the cursor\n\ntranspose-words\n transpose two words to the left of the cursor\n\ntogglecase-char\n toggle the capitalisation (case) of the character under the\n cursor\n\ntogglecase-selection\n toggle the capitalisation (case) of the selection\n\ninsert-line-under\n add a new line under the current line\n\ninsert-line-over\n add a new line over the current line\n\nup-line\n move up one line\n\nundo and redo\n revert or redo the most recent edits on the command line\n\nupcase-word\n make the current word uppercase\n\nyank insert the latest entry of the killring into the buffer\n\nyank-pop\n rotate to the previous entry of the killring\n\nADDITIONAL FUNCTIONS\nThe following functions are included as normal functions, but are\nparticularly useful for input editing:\n\nup-or-search and down-or-search\n move the cursor or search the history depending on the cursor\n position and current mode\n\nedit_command_buffer\n open the visual editor (controlled by the VISUAL or EDITOR\n environment variables) with the current command-line contents\n\nfish_clipboard_copy\n copy the current selection to the system clipboard\n\nfish_clipboard_paste\n paste the current selection from the system clipboard before\n the cursor\n\nfish_commandline_append\n append the argument to the command-line. If the command-line\n already ends with the argument, this removes the suffix\n instead. Starts with the last command from history if the\n command-line is empty.\n\nfish_commandline_prepend\n prepend the argument to the command-line. If the command-line\n already starts with the argument, this removes the prefix\n instead. Starts with the last command from history if the\n command-line is empty.\n\nEXAMPLES\nExit the shell when Control+D is pressed:\n\n bind \\cd 'exit'\n\nPerform a history search when Page Up is pressed:\n\n bind -k ppage history-search-backward\n\nTurn on Vi key bindings and rebind Control+C to clear the input line:\n\n set -g fish_key_bindings fish_vi_key_bindings\n bind -M insert \\cc kill-whole-line repaint\n\nLaunch git diff and repaint the commandline afterwards when Control+G\nis pressed:\n\n bind \\cg 'git diff; commandline -f repaint'\n\nTERMINAL LIMITATIONS\nUnix terminals, like the ones fish operates in, are at heart 70s\ntechnology. They have some limitations that applications running\ninside them can't workaround.\n\nFor instance, the control key modifies a character by setting the top\nthree bits to 0. This means:\n\n• Many characters + control are indistinguishable from other keys.\n Control+I is tab, Control+J is newline (\\n).\n\n• Control and shift don't work simultaneously\n\nOther keys don't have a direct encoding, and are sent as escape\nsequences. For example → (Right) often sends \\e\\[C. These can differ\nfrom terminal to terminal, and the mapping is typically available in\nterminfo(5). Sometimes however a terminal identifies as e.g.\nxterm-256color for compatibility, but then implements xterm's\nsequences incorrectly.\n\nSPECIAL CASE: THE ESCAPE CHARACTER\nThe escape key can be used standalone, for example, to switch from\ninsertion mode to normal mode when using Vi keybindings. Escape can\nalso be used as a \"meta\" key, to indicate the start of an escape\nsequence, like for function or arrow keys. Custom bindings can also\nbe defined that begin with an escape character.\n\nHolding alt and something else also typically sends escape, for\nexample holding alt+a will send an escape character and then an \"a\".\n\nfish waits for a period after receiving the escape character, to\ndetermine whether it is standalone or part of an escape sequence.\nWhile waiting, additional key presses make the escape key behave as a\nmeta key. If no other key presses come in, it is handled as a\nstandalone escape. The waiting period is set to 30 milliseconds (0.03\nseconds). It can be configured by setting the fish_escape_delay_ms\nvariable to a value between 10 and 5000 ms. This can be a universal\nvariable that you set once from an interactive session. So the\nescape character has its own timeout configured with\nfish_escape_delay_ms.\n\nSee also Key sequences.", + "args": "bind [(-M | --mode) MODE] [(-m | --sets-mode) NEW_MODE] [--preset | --user] [-s | --silent] [-k | --key] SEQUENCE COMMAND ...\nbind [(-M | --mode) MODE] [-k | --key] [--preset] [--user] SEQUENCE\nbind (-K | --key-names) [-a | --all] [--preset] [--user]\nbind (-f | --function-names)\nbind (-L | --list-modes)\nbind (-e | --erase) [(-M | --mode) MODE] [--preset] [--user] [-a | --all] | [-k | --key] SEQUENCE ..." + }, + "block": { + "shortDescription": "temporarily block delivery of events", + "description": "block prevents events triggered by fish or the emit command from\nbeing delivered and acted upon while the block is in place.\n\nIn functions, block can be useful while performing work that should\nnot be interrupted by the shell.\n\nThe block can be removed. Any events which triggered while the block\nwas in place will then be delivered.\n\nEvent blocks should not be confused with code blocks, which are\ncreated with begin, if, while or for\n\nWithout options, the block command acts with function scope.\n\nThe following options are available:\n\n-l or --local\n Release the block automatically at the end of the current\n innermost code block scope.\n\n-g or --global\n Never automatically release the lock.\n\n-e or --erase\n Release global block.\n\n-h or --help\n Displays help about using this command.\n\nEXAMPLE\n\n # Create a function that listens for events\n function --on-event foo foo; echo 'foo fired'; end\n\n # Block the delivery of events\n block -g\n\n emit foo\n # No output will be produced\n\n block -e\n # 'foo fired' will now be printed\n\nNOTES\nEvents are only received from the current fish process as there is no\nway to send events from one fish process to another (yet).", + "args": "block [(--local | --global)]\nblock --erase" + }, + "break": { + "shortDescription": "Exit the current loop", + "description": "Terminate the execution of the nearest enclosing `while` or `for` loop and proceed with the next command after the loop." + }, + "breakpoint": { + "shortDescription": "Launch debug mode", + "description": "Pause execution and launch an interactive debug prompt. This is useful for inspecting the state of a script at a specific point." + }, + "builtin": { + "shortDescription": "run a builtin command", + "description": "builtin forces the shell to use a builtin command named BUILTIN,\nrather than a function or external program.\n\nThe following options are available:\n\n-n or --names\n Lists the names of all defined builtins.\n\n-q or --query BUILTIN\n Tests if any of the specified builtins exist. If any exist, it\n returns 0, 1 otherwise.\n\n-h or --help\n Displays help about using this command.\n\nEXAMPLE\n\n builtin jobs\n # executes the jobs builtin, even if a function named jobs exists", + "args": "builtin [OPTIONS] BUILTINNAME\nbuiltin --query BUILTINNAME ...\nbuiltin --names" + }, + "case": { + "shortDescription": "Match a value against patterns", + "description": "Within a `switch` block, the `case` command specifies patterns to match against the given value, executing the associated block if a match is found.", + "args": "PATTERN..." + }, + "cd": { + "shortDescription": "change directory", + "description": "cd changes the current working directory.\n\nIf DIRECTORY is given, it will become the new directory. If no\nparameter is given, the HOME environment variable will be used.\n\nIf DIRECTORY is a relative path, all the paths in the CDPATH will be\ntried as prefixes for it, in addition to PWD. It is recommended to\nkeep . as the first element of CDPATH, or PWD will be tried last.\n\nFish will also try to change directory if given a command that looks\nlike a directory (starting with ., / or ~, or ending with /), without\nexplicitly requiring cd.\n\nFish also ships a wrapper function around the builtin cd that\nunderstands cd - as changing to the previous directory. See also\nprevd. This wrapper function maintains a history of the 25 most\nrecently visited directories in the $dirprev and $dirnext global\nvariables. If you make those universal variables your cd history is\nshared among all fish instances.\n\nAs a special case, cd . is equivalent to cd $PWD, which is useful in\ncases where a mountpoint has been recycled or a directory has been\nremoved and recreated.\n\nThe --help or -h option displays help about using this command, and\ndoes not change the directory.\n\nEXAMPLES\n\n cd\n # changes the working directory to your home directory.\n\n cd /usr/src/fish-shell\n # changes the working directory to /usr/src/fish-shell\n\nSEE ALSO\nNavigate directories using the directory history or the directory\nstack", + "args": "cd [DIRECTORY]" + }, + "command": { + "shortDescription": "run a program", + "description": "command forces the shell to execute the program COMMANDNAME and\nignore any functions or builtins with the same name.\n\nThe following options are available:\n\n-a or --all\n Prints all COMMAND found in PATH, in the order found.\n\n-q or --query\n Silence output and print nothing, setting only exit status.\n Implies --search. For compatibility, this is also --quiet\n (deprecated).\n\n-v (or -s or --search)\n Prints the external command that would be executed, or prints\n nothing if no file with the specified name could be found in\n PATH.\n\n-h or --help\n Displays help about using this command.\n\nWith the -v option, command treats every argument as a separate\ncommand to look up and sets the exit status to 0 if any of the\nspecified commands were found, or 127 if no commands could be found.\n--quiet used with -v prevents commands being printed, like type -q.\n\nEXAMPLES\ncommand ls executes the ls program, even if an ls function also exists.\ncommand -s ls prints the path to the ls program.\ncommand -q git; and command git log runs git log only if git exists.", + "args": "command [OPTIONS] [COMMANDNAME [ARG ...]]" + }, + "commandline": { + "shortDescription": "set or get the current command line buffer", + "description": "commandline can be used to set or get the current contents of the\ncommand line buffer.\n\nWith no parameters, commandline returns the current value of the\ncommand line.\n\nWith CMD specified, the command line buffer is erased and replaced\nwith the contents of CMD.\n\nThe following options are available:\n\n-C or --cursor\n Set or get the current cursor position, not the contents of\n the buffer. If no argument is given, the current cursor\n position is printed, otherwise the argument is interpreted as\n the new cursor position. If one of the options -j, -p or -t\n is given, the position is relative to the respective substring\n instead of the entire command line buffer.\n\n-B or --selection-start\n Get current position of the selection start in the buffer.\n\n-E or --selection-end\n Get current position of the selection end in the buffer.\n\n-f or --function\n Causes any additional arguments to be interpreted as input\n functions, and puts them into the queue, so that they will be\n read before any additional actual key presses are. This\n option cannot be combined with any other option. See bind for\n a list of input functions.\n\n-h or --help\n Displays help about using this command.\n\nThe following options change the way commandline updates the command\nline buffer:\n\n-a or --append\n Do not remove the current commandline, append the specified\n string at the end of it.\n\n-i or --insert\n Do not remove the current commandline, insert the specified\n string at the current cursor position\n\n-r or --replace\n Remove the current commandline and replace it with the\n specified string (default)\n\nThe following options change what part of the commandline is printed\nor updated:\n\n-b or --current-buffer\n Select the entire commandline, not including any displayed\n autosuggestion (default).\n\n-j or --current-job\n Select the current job - a job here is one pipeline. Stops at\n logical operators or terminators (;, &, and newlines).\n\n-p or --current-process\n Select the current process - a process here is one command.\n Stops at logical operators, terminators, and pipes.\n\n-s or --current-selection\n Selects the current selection\n\n-t or --current-token\n Selects the current token\n\nThe following options change the way commandline prints the current\ncommandline buffer:\n\n-c or --cut-at-cursor\n Only print selection up until the current cursor position. If\n combined with --tokenize, this will print up until the last\n completed token - excluding the token the cursor is in. This\n is typically what you would want for instance in completions.\n To get both, use both commandline --cut-at-cursor --tokenize;\n commandline --cut-at-cursor --current-token, or commandline\n -co; commandline -ct for short.\n\n-o or --tokenize\n Tokenize the selection and print one string-type token per\n line.\n\nIf commandline is called during a call to complete a given string\nusing complete -C STRING, commandline will consider the specified\nstring to be the current contents of the command line.\n\nThe following options output metadata about the commandline state:\n\n-L or --line\n Print the line that the cursor is on, with the topmost line\n starting at 1.\n\n-S or --search-mode\n Evaluates to true if the commandline is performing a history\n search.\n\n-P or --paging-mode\n Evaluates to true if the commandline is showing pager\n contents, such as tab completions.\n\n--paging-full-mode\n Evaluates to true if the commandline is showing pager\n contents, such as tab completions and all lines are shown (no\n \" more rows\" message).\n\n--is-valid\n Returns true when the commandline is syntactically valid and\n complete. If it is, it would be executed when the execute\n bind function is called. If the commandline is incomplete,\n return 2, if erroneus, return 1.\n\nEXAMPLE\ncommandline -j $history[3] replaces the job under the cursor with the\nthird item from the command line history.\n\nIf the commandline contains\n\n > echo $flounder >&2 | less; and echo $catfish\n\n(with the cursor on the \"o\" of \"flounder\")\n\nThe echo $flounder >& is the first process, less the second and and\necho $catfish the third.\n\necho $flounder >&2 | less is the first job, and echo $catfish the\nsecond.\n\n$flounder is the current token.\n\nThe most common use for something like completions is\n\n set -l tokens (commandline -opc)\n\nwhich gives the current process (what is being completed), tokenized\ninto separate entries, up to but excluding the currently being\ncompleted token\n\nIf you are then also interested in the in-progress token, add\n\n:: set -l current (commandline -ct)\n\nNote that this makes it easy to render fish's infix matching moot -\nif possible it's best if the completions just print all possibilities\nand leave the matching to the current token up to fish's logic.\n\nMore examples:\n\n > commandline -t\n $flounder\n > commandline -ct\n $fl\n > commandline -b # or just commandline\n echo $flounder >&2 | less; and echo $catfish\n > commandline -p\n echo $flounder >&2\n > commandline -j\n echo $flounder >&2 | less", + "args": "commandline [OPTIONS] [CMD]" + }, + "complete": { + "shortDescription": "edit command-specific tab-completions", + "description": "complete defines, removes or lists completions for a command.\n\nFor an introduction to writing your own completions, see Writing your\nown completions in the fish manual.\n\nThe following options are available:\n\n-c or --command COMMAND\n Specifies that COMMAND is the name of the command. If there is\n no -c or -p, one non-option argument will be used as the\n command.\n\n-p or --path COMMAND\n Specifies that COMMAND is the absolute path of the command\n (optionally containing wildcards).\n\n-e or --erase\n Deletes the specified completion.\n\n-s or --short-option SHORT_OPTION\n Adds a short option to the completions list.\n\n-l or --long-option LONG_OPTION\n Adds a GNU-style long option to the completions list.\n\n-o or --old-option OPTION\n Adds an old-style short or long option (see below for\n details).\n\n-a or --arguments ARGUMENTS\n Adds the specified option arguments to the completions list.\n\n-k or --keep-order\n Keeps the order of ARGUMENTS instead of sorting\n alphabetically. Multiple complete calls with -k result in\n arguments of the later ones displayed first.\n\n-f or --no-files\n This completion may not be followed by a filename.\n\n-F or --force-files\n This completion may be followed by a filename, even if another\n applicable complete specified --no-files.\n\n-r or --require-parameter\n This completion must have an option argument, i.e. may not be\n followed by another option.\n\n-x or --exclusive\n Short for -r and -f.\n\n-w or --wraps WRAPPED_COMMAND\n Causes the specified command to inherit completions from\n WRAPPED_COMMAND (see below for details).\n\n-n or --condition CONDITION\n This completion should only be used if the CONDITION (a shell\n command) returns 0. This makes it possible to specify\n completions that should only be used in some cases. If\n multiple conditions are specified, fish will try them in the\n order they are specified until one fails or all succeeded.\n\n-C or --do-complete STRING\n Makes complete try to find all possible completions for the\n specified string. If there is no STRING, the current\n commandline is used instead.\n\n--escape\n When used with -C, escape special characters in completions.\n\n-h or --help\n Displays help about using this command.\n\nCommand-specific tab-completions in fish are based on the notion of\noptions and arguments. An option is a parameter which begins with a\nhyphen, such as -h, -help or --help. Arguments are parameters that do\nnot begin with a hyphen. Fish recognizes three styles of options, the\nsame styles as the GNU getopt library. These styles are:\n\n• Short options, like -a. Short options are a single character long,\n are preceded by a single hyphen and can be grouped together (like\n -la, which is equivalent to -l -a). Option arguments may be\n specified by appending the option with the value (-w32), or, if\n --require-parameter is given, in the following parameter (-w 32).\n\n• Old-style options, long like -Wall or -name or even short like -a.\n Old-style options can be more than one character long, are preceded\n by a single hyphen and may not be grouped together. Option\n arguments are specified by default following a space (-foo null) or\n after = (-foo=null).\n\n• GNU-style long options, like --colors. GNU-style long options can\n be more than one character long, are preceded by two hyphens, and\n can't be grouped together. Option arguments may be specified after\n a = (--quoting-style=shell), or, if --require-parameter is given,\n in the following parameter (--quoting-style shell).\n\nMultiple commands and paths can be given in one call to define the\nsame completions for multiple commands.\n\nMultiple command switches and wrapped commands can also be given to\ndefine multiple completions in one call.\n\nInvoking complete multiple times for the same command adds the new\ndefinitions on top of any existing completions defined for the\ncommand.\n\nWhen -a or --arguments is specified in conjunction with long, short,\nor old-style options, the specified arguments are only completed as\narguments for any of the specified options. If -a or --arguments is\nspecified without any long, short, or old-style options, the\nspecified arguments are used when completing non-option arguments to\nthe command (except when completing an option argument that was\nspecified with -r or --require-parameter).\n\nCommand substitutions found in ARGUMENTS should return a\nnewline-separated list of arguments, and each argument may optionally\nhave a tab character followed by the argument description.\nDescription given this way override a description given with -d or\n--description.\n\nDescriptions given with --description are also used to group options\ngiven with -s, -o or -l. Options with the same (non-empty)\ndescription will be listed as one candidate, and one of them will be\npicked. If the description is empty or no description was given this\nis skipped.\n\nThe -w or --wraps options causes the specified command to inherit\ncompletions from another command, \"wrapping\" the other command. The\nwrapping command can also have additional completions. A command can\nwrap multiple commands, and wrapping is transitive: if A wraps B, and\nB wraps C, then A automatically inherits all of C's completions.\nWrapping can be removed using the -e or --erase options. Wrapping\nonly works for completions specified with -c or --command and are\nignored when specifying completions with -p or --path.\n\nWhen erasing completions, it is possible to either erase all\ncompletions for a specific command by specifying complete -c COMMAND\n-e, or by specifying a specific completion option to delete.\n\nWhen complete is called without anything that would define or erase\ncompletions (options, arguments, wrapping, ...), it shows matching\ncompletions instead. So complete without any arguments shows all\nloaded completions, complete -c foo shows all loaded completions for\nfoo. Since completions are autoloaded, you will have to trigger them\nfirst.\n\nEXAMPLES\nThe short-style option -o for the gcc command needs a file argument:\n\n complete -c gcc -s o -r\n\nThe short-style option -d for the grep command requires one of read,\nskip or recurse:\n\n complete -c grep -s d -x -a \"read skip recurse\"\n\nThe su command takes any username as an argument. Usernames are given\nas the first colon-separated field in the file /etc/passwd. This can\nbe specified as:\n\n complete -x -c su -d \"Username\" -a \"(cat /etc/passwd | cut -d : -f 1)\"\n\nThe rpm command has several different modes. If the -e or --erase\nflag has been specified, rpm should delete one or more packages, in\nwhich case several switches related to deleting packages are valid,\nlike the nodeps switch.\n\nThis can be written as:\n\n complete -c rpm -n \"__fish_contains_opt -s e erase\" -l nodeps -d \"Don't check dependencies\"\n\nwhere __fish_contains_opt is a function that checks the command line\nbuffer for the presence of a specified set of options.\n\nTo implement an alias, use the -w or --wraps option:\n\n complete -c hub -w git\n\nNow hub inherits all of the completions from git. Note this can also\nbe specified in a function declaration (function thing -w\notherthing).\n\n complete -c git\n\nShows all completions for git.\n\nAny command foo that doesn't support grouping multiple short options\nin one string (not supporting -xf as short for -x -f) or a short\noption and its value in one string (not supporting -d9 instead of -d\n9) should be specified as a single-character old-style option instead\nof as a short-style option; for example, complete -c foo -o s;\ncomplete -c foo -o v would never suggest foo -ov but rather foo -o\n-v.", + "args": "complete ((-c | --command) | (-p | --path)) COMMAND [OPTIONS]\ncomplete (-C | --do-complete) [--escape] STRING" + }, + "contains": { + "shortDescription": "test if a word is present in a list", + "description": "contains tests whether the set VALUES contains the string KEY. If\nso, contains exits with code 0; if not, it exits with code 1.\n\nThe following options are available:\n\n-i or --index\n Print the index (number of the element in the set) of the\n first matching element.\n\n-h or --help\n Displays help about using this command.\n\nNote that contains interprets all arguments starting with a - as an\noption to contains, until an -- argument is reached.\n\nSee the examples below.\n\nEXAMPLE\nIf animals is a list of animals, the following will test if animals\ncontains \"cat\":\n\n if contains cat $animals\n echo Your animal list is evil!\n end\n\nThis code will add some directories to PATH if they aren't yet\nincluded:\n\n for i in ~/bin /usr/local/bin\n if not contains $i $PATH\n set PATH $PATH $i\n end\n end\n\nWhile this will check if function hasargs is being ran with the -q\noption:\n\n function hasargs\n if contains -- -q $argv\n echo '$argv contains a -q option'\n end\n end\n\nThe -- here stops contains from treating -q to an option to itself.\nInstead it treats it as a normal string to check.", + "args": "contains [OPTIONS] KEY [VALUES ...]" + }, + "continue": { + "shortDescription": "Skip to the next iteration of a loop", + "description": "Within a `while` or `for` loop, `continue` skips the remaining commands in the current iteration and proceeds to the next iteration of the loop." + }, + "count": { + "shortDescription": "", + "description": "" + }, + "disown": { + "shortDescription": "remove a process from the list of jobs", + "description": "disown removes the specified job from the list of jobs. The job\nitself continues to exist, but fish does not keep track of it any\nlonger.\n\nJobs in the list of jobs are sent a hang-up signal when fish\nterminates, which usually causes the job to terminate; disown allows\nthese processes to continue regardless.\n\nIf no process is specified, the most recently-used job is removed\n(like bg and fg). If one or more PIDs are specified, jobs with the\nspecified process IDs are removed from the job list. Invalid jobs are\nignored and a warning is printed.\n\nIf a job is stopped, it is sent a signal to continue running, and a\nwarning is printed. It is not possible to use the bg builtin to\ncontinue a job once it has been disowned.\n\ndisown returns 0 if all specified jobs were disowned successfully,\nand 1 if any problems were encountered.\n\nThe --help or -h option displays help about using this command.\n\nEXAMPLE\nfirefox &; disown will start the Firefox web browser in the\nbackground and remove it from the job list, meaning it will not be\nclosed when the fish process is closed.\n\ndisown (jobs -p) removes all jobs from the job list without\nterminating them.", + "args": "disown [PID ...]" + }, + "echo": { + "shortDescription": "", + "description": "" + }, + "else": { + "shortDescription": "Execute commands if the previous condition was false", + "description": "In an `if` block, the `else` section contains commands that execute if none of the preceding `if` or `else if` conditions were true." + }, + "emit": { + "shortDescription": "emit a generic event", + "description": "emit emits, or fires, an event. Events are delivered to, or caught\nby, special functions called event handlers. The arguments are passed\nto the event handlers as function arguments.\n\nThe --help or -h option displays help about using this command.\n\nEXAMPLE\nThe following code first defines an event handler for the generic\nevent named 'test_event', and then emits an event of that type.\n\n function event_test --on-event test_event\n echo event test: $argv\n end\n\n emit test_event something\n\nNOTES\nNote that events are only sent to the current fish process as there\nis no way to send events from one fish process to another.", + "args": "emit EVENT_NAME [ARGUMENTS ...]" + }, + "end": { + "shortDescription": "Terminate a block of code", + "description": "Conclude a block of code initiated by constructs like `if`, `switch`, `while`, `for`, or `function`." + }, + "eval": { + "shortDescription": "Execute arguments as a command", + "description": "Concatenate all arguments into a single command and execute it. This allows for dynamic construction and execution of commands.", + "args": "COMMAND..." + }, + "exec": { + "shortDescription": "execute command in current process", + "description": "exec replaces the currently running shell with a new command. On\nsuccessful completion, exec never returns. exec cannot be used inside\na pipeline.\n\nThe --help or -h option displays help about using this command.\n\nEXAMPLE\nexec emacs starts up the emacs text editor, and exits fish. When\nemacs exits, the session will terminate.", + "args": "exec COMMAND" + }, + "exit": { + "shortDescription": "exit the shell", + "description": "exit is a special builtin that causes the shell to exit. Either 255\nor the CODE supplied is used, whichever is lesser. Otherwise, the\nexit status will be that of the last command executed.\n\nIf exit is called while sourcing a file (using the source builtin)\nthe rest of the file will be skipped, but the shell itself will not\nexit.\n\nThe --help or -h option displays help about using this command.", + "args": "exit [CODE]" + }, + "false": { + "shortDescription": "Return an unsuccessful result", + "description": "A command that returns a non-zero exit status, indicating failure. It is often used in scripts to represent a false condition." + }, + "fg": { + "shortDescription": "bring job to foreground", + "description": "The fg builtin brings the specified job to the foreground, resuming\nit if it is stopped. While a foreground job is executed, fish is\nsuspended. If no job is specified, the last job to be used is put in\nthe foreground. If PID is specified, the job containing a process\nwith the specified process ID is put in the foreground.\n\nFor compatibility with other shells, job expansion syntax is\nsupported for fg. A PID of the format %1 will foreground job 1. Job\nnumbers can be seen in the output of jobs.\n\nThe --help or -h option displays help about using this command.\n\nEXAMPLE\nfg will put the last job in the foreground.\n\nfg %3 will put job 3 into the foreground.", + "args": "fg [PID]" + }, + "for": { + "shortDescription": "perform a set of commands multiple times", + "description": "for is a loop construct. It will perform the commands specified by\nCOMMANDS multiple times. On each iteration, the local variable\nspecified by VARNAME is assigned a new value from VALUES. If VALUES\nis empty, COMMANDS will not be executed at all. The VARNAME is\nvisible when the loop terminates and will contain the last value\nassigned to it. If VARNAME does not already exist it will be set in\nthe local scope. For our purposes if the for block is inside a\nfunction there must be a local variable with the same name. If the\nfor block is not nested inside a function then global and universal\nvariables of the same name will be used if they exist.\n\nMuch like set, for does not modify $status, but the evaluation of its\nsubordinate commands can.\n\nThe -h or --help option displays help about using this command.\n\nEXAMPLE\n\n for i in foo bar baz; echo $i; end\n\n # would output:\n foo\n bar\n baz\n\nNOTES\nThe VARNAME was local to the for block in releases prior to 3.0.0.\nThis means that if you did something like this:\n\n for var in a b c\n if break_from_loop\n break\n end\n end\n echo $var\n\nThe last value assigned to var when the loop terminated would not be\navailable outside the loop. What echo $var would write depended on\nwhat it was set to before the loop was run. Likely nothing.", + "args": "for VARNAME in [VALUES ...]; COMMANDS ...; end" + }, + "function": { + "shortDescription": "create a function", + "description": "function creates a new function NAME with the body BODY.\n\nA function is a list of commands that will be executed when the name\nof the function is given as a command.\n\nThe following options are available:\n\n-a NAMES or --argument-names NAMES\n Assigns the value of successive command-line arguments to the\n names given in NAMES. These are the same arguments given in\n argv, and are still available there. See also Argument\n Handling.\n\n-d DESCRIPTION or --description DESCRIPTION\n A description of what the function does, suitable as a\n completion description.\n\n-w WRAPPED_COMMAND or --wraps WRAPPED_COMMAND\n Inherit completions from the given WRAPPED_COMMAND. See the\n documentation for complete for more information.\n\n-e EVENT_NAME or --on-event EVENT_NAME\n Run this function when the specified named event is emitted.\n Fish internally generates named events, for example, when\n showing the prompt. Custom events can be emitted using the\n emit command.\n\n-v VARIABLE_NAME or --on-variable VARIABLE_NAME\n Run this function when the variable VARIABLE_NAME changes\n value. Note that fish makes no guarantees on any particular\n timing or even that the function will be run for every single\n set. Rather it will be run when the variable has been set at\n least once, possibly skipping some values or being run when\n the variable has been set to the same value (except for\n universal variables set in other shells - only changes in the\n value will be picked up for those).\n\n-j PID or --on-job-exit PID\n Run this function when the job containing a child process with\n the given process identifier PID exits. Instead of a PID, the\n string 'caller' can be specified. This is only allowed when in\n a command substitution, and will result in the handler being\n triggered by the exit of the job which created this command\n substitution.\n\n-p PID or --on-process-exit PID\n Run this function when the fish child process with process ID\n PID exits. Instead of a PID, for backward compatibility,\n \"%self\" can be specified as an alias for $fish_pid, and the\n function will be run when the current fish instance exits.\n\n-s SIGSPEC or --on-signal SIGSPEC\n Run this function when the signal SIGSPEC is delivered.\n SIGSPEC can be a signal number, or the signal name, such as\n SIGHUP (or just HUP). Note that the signal must have been\n delivered to fish; for example, Ctrl-C sends SIGINT to the\n foreground process group, which will not be fish if you are\n running another command at the time. Observing a signal will\n prevent fish from exiting in response to that signal.\n\n-S or --no-scope-shadowing\n Allows the function to access the variables of calling\n functions. Normally, any variables inside the function that\n have the same name as variables from the calling function are\n \"shadowed\", and their contents are independent of the calling\n function.\n\n It's important to note that this does not capture referenced\n variables or the scope at the time of function declaration! At\n this time, fish does not have any concept of closures, and\n variable lifetimes are never extended. In other words, by\n using --no-scope-shadowing the scope of the function each time\n it is run is shared with the scope it was called from rather\n than the scope it was defined in.\n\n-V or --inherit-variable NAME\n Snapshots the value of the variable NAME and defines a local\n variable with that same name and value when the function is\n defined. This is similar to a closure in other languages like\n Python but a bit different. Note the word \"snapshot\" in the\n first sentence. If you change the value of the variable after\n defining the function, even if you do so in the same scope\n (typically another function) the new value will not be used by\n the function you just created using this option. See the\n function notify example below for how this might be used.\n\nThe event handler switches (on-event, on-variable, on-job-exit,\non-process-exit and on-signal) cause a function to run automatically\nat specific events. New named events for --on-event can be fired\nusing the emit builtin. Fish already generates a few events, see\nEvent handlers for more.\n\nFunctions may not be named the same as a reserved keyword. These are\nelements of fish syntax or builtin commands which are essential for\nthe operations of the shell. Current reserved words are [, , and,\nargparse, begin, break, builtin, case, command, continue, else, end,\neval, exec, for, function, if, not, or, read, return, set, status,\nstring, switch, test, time, and while.\n\nEXAMPLE\n\n function ll\n ls -l $argv\n end\n\nwill run the ls command, using the -l option, while passing on any\nadditional files and switches to ls.\n\n function mkdir -d \"Create a directory and set CWD\"\n command mkdir $argv\n if test $status = 0\n switch $argv[(count $argv)]\n case '-*'\n\n case '*'\n cd $argv[(count $argv)]\n return\n end\n end\n end\n\nThis will run the mkdir command, and if it is successful, change the\ncurrent working directory to the one just created.\n\n function notify\n set -l job (jobs -l -g)\n or begin; echo \"There are no jobs\" >&2; return 1; end\n\n function _notify_job$job --on-job-exit $job --inherit-variable job\n echo -n \\a # beep\n functions -e _notify_job$job\n end\n end\n\nThis will beep when the most recent job completes.\n\nNOTES\nEvents are only received from the current fish process as there is no\nway to send events from one fish process to another.\n\nSEE MORE\nFor more explanation of how functions fit into fish, see Functions.", + "args": "function NAME [OPTIONS]; BODY; end" + }, + "functions": { + "shortDescription": "print or erase functions", + "description": "functions prints or erases functions.\n\nThe following options are available:\n\n-a or --all\n Lists all functions, even those whose name starts with an\n underscore.\n\n-c or --copy OLDNAME NEWNAME\n Creates a new function named NEWNAME, using the definition of\n the OLDNAME function.\n\n-d or --description DESCRIPTION\n Changes the description of this function.\n\n-e or --erase\n Causes the specified functions to be erased. This also means\n that it is prevented from autoloading in the current session.\n Use funcsave to remove the saved copy.\n\n-D or --details\n Reports the path name where the specified function is defined\n or could be autoloaded, stdin if the function was defined\n interactively or on the command line or by reading standard\n input, - if the function was created via source, and n/a if\n the function isn't available. (Functions created via alias\n will return -, because alias uses source internally.) If the\n --verbose option is also specified then five lines are\n written:\n\n • the pathname as already described,\n\n • autoloaded, not-autoloaded or n/a,\n\n • the line number within the file or zero if not applicable,\n\n • scope-shadowing if the function shadows the vars in the\n calling function (the normal case if it wasn't defined with\n --no-scope-shadowing), else no-scope-shadowing, or n/a if\n the function isn't defined,\n\n • the function description minimally escaped so it is a single\n line, or n/a if the function isn't defined or has no\n description.\n\n You should not assume that only five lines will be written\n since we may add additional information to the output in the\n future.\n\n--no-details\n Turns off function path reporting, so just the definition will\n be printed.\n\n-n or --names\n Lists the names of all defined functions.\n\n-q or --query\n Tests if the specified functions exist.\n\n-v or --verbose\n Make some output more verbose.\n\n-H or --handlers\n Show all event handlers.\n\n-t or --handlers-type TYPE\n Show all event handlers matching the given TYPE.\n\n-h or --help\n Displays help about using this command.\n\nThe default behavior of functions, when called with no arguments, is\nto print the names of all defined functions. Unless the -a option is\ngiven, no functions starting with underscores are included in the\noutput.\n\nIf any non-option parameters are given, the definition of the\nspecified functions are printed.\n\nCopying a function using -c copies only the body of the function, and\ndoes not attach any event notifications from the original function.\n\nOnly one function's description can be changed in a single invocation\nof functions -d.\n\nThe exit status of functions is the number of functions specified in\nthe argument list that do not exist, which can be used in concert\nwith the -q option.\n\nEXAMPLES\n\n functions -n\n # Displays a list of currently-defined functions\n\n functions -c foo bar\n # Copies the 'foo' function to a new function called 'bar'\n\n functions -e bar\n # Erases the function ``bar``\n\nSEE MORE\nFor more explanation of how functions fit into fish, see Functions.", + "args": "functions [-a | --all] [-n | --names]\nfunctions [-D | --details] [-v] FUNCTION\nfunctions -c OLDNAME NEWNAME\nfunctions -d DESCRIPTION FUNCTION\nfunctions [-e | -q] FUNCTION ..." + }, + "history": { + "shortDescription": "show and manipulate command history", + "description": "history is used to search, delete, and otherwise manipulate the\nhistory of interactive commands.\n\nThe following operations (sub-commands) are available:\n\nsearch Returns history items matching the search string. If no search\n string is provided it returns all history items. This is the\n default operation if no other operation is specified. You only\n have to explicitly say history search if you wish to search\n for one of the subcommands. The --contains search option will\n be used if you don't specify a different search option.\n Entries are ordered newest to oldest unless you use the\n --reverse flag. If stdout is attached to a tty the output will\n be piped through your pager by the history function. The\n history builtin simply writes the results to stdout.\n\ndelete Deletes history items. The --contains search option will be\n used if you don't specify a different search option. If you\n don't specify --exact a prompt will be displayed before any\n items are deleted asking you which entries are to be deleted.\n You can enter the word \"all\" to delete all matching entries.\n You can enter a single ID (the number in square brackets) to\n delete just that single entry. You can enter more than one ID,\n or an ID range separated by a space to delete multiple\n entries. Just press [enter] to not delete anything. Note that\n the interactive delete behavior is a feature of the history\n function. The history builtin only supports --exact\n --case-sensitive deletion.\n\nmerge Immediately incorporates history changes from other sessions.\n Ordinarily fish ignores history changes from sessions started\n after the current one. This command applies those changes\n immediately.\n\nsave Immediately writes all changes to the history file. The shell\n automatically saves the history file; this option is provided\n for internal use and should not normally need to be used by\n the user.\n\nclear Clears the history file. A prompt is displayed before the\n history is erased asking you to confirm you really want to\n clear all history unless builtin history is used.\n\nclear-session\n Clears the history file from all activity of the current\n session. Note: If history merge or builtin history merge is\n run in a session, only the history after this will be erased.\n\nThe following options are available:\n\nThese flags can appear before or immediately after one of the\nsub-commands listed above.\n\n-C or --case-sensitive\n Does a case-sensitive search. The default is case-insensitive.\n Note that prior to fish 2.4.0 the default was case-sensitive.\n\n-c or --contains\n Searches items in the history that contain the specified text\n string. This is the default for the --search flag. This is not\n currently supported by the delete subcommand.\n\n-e or --exact\n Searches or deletes items in the history that exactly match\n the specified text string. This is the default for the delete\n subcommand. Note that the match is case-insensitive by\n default. If you really want an exact match, including letter\n case, you must use the -C or --case-sensitive flag.\n\n-p or --prefix\n Searches items in the history that begin with the specified\n text string. This is not currently supported by the delete\n subcommand.\n\n-t or --show-time\n Prepends each history entry with the date and time the entry\n was recorded. By default it uses the strftime format # %c%n.\n You can specify another format; e.g., --show-time=\"%Y-%m-%d\n %H:%M:%S \" or --show-time=\"%a%I%p\". The short option, -t,\n doesn't accept a strftime format string; it only uses the\n default format. Any strftime format is allowed, including %s\n to get the raw UNIX seconds since the epoch.\n\n-z or --null\n Causes history entries written by the search operations to be\n terminated by a NUL character rather than a newline. This\n allows the output to be processed by read -z to correctly\n handle multiline history entries.\n\n-*NUMBER* -n NUMBER or --max NUMBER\n Limits the matched history items to the first NUMBER matching\n entries. This is only valid for history search.\n\n-R or --reverse\n Causes the history search results to be ordered oldest to\n newest. Which is the order used by most shells. The default is\n newest to oldest.\n\n-h or --help\n Displays help for this command.\n\nEXAMPLE\n\n history clear\n # Deletes all history items\n\n history search --contains \"foo\"\n # Outputs a list of all previous commands containing the string \"foo\".\n\n history delete --prefix \"foo\"\n # Interactively deletes commands which start with \"foo\" from the history.\n # You can select more than one entry by entering their IDs separated by a space.\n\nCUSTOMIZING THE NAME OF THE HISTORY FILE\nBy default interactive commands are logged to\n$XDG_DATA_HOME/fish/fish_history (typically\n~/.local/share/fish/fish_history).\n\nYou can set the fish_history variable to another name for the current\nshell session. The default value (when the variable is unset) is fish\nwhich corresponds to $XDG_DATA_HOME/fish/fish_history. If you set it\nto e.g. fun, the history would be written to\n$XDG_DATA_HOME/fish/fun_history. An empty string means history will\nnot be stored at all. This is similar to the private session features\nin web browsers.\n\nYou can change fish_history at any time (by using set -x fish_history\n\"session_name\") and it will take effect right away. If you set it to\n\"default\", it will use the default session name (which is \"fish\").\n\nOther shells such as bash and zsh use a variable named HISTFILE for a\nsimilar purpose. Fish uses a different name to avoid conflicts and\nsignal that the behavior is different (session name instead of a file\npath). Also, if you set the var to anything other than fish or\ndefault it will inhibit importing the bash history. That's because\nthe most common use case for this feature is to avoid leaking private\nor sensitive history when giving a presentation.\n\nNOTES\nIf you specify both --prefix and --contains the last flag seen is\nused.\n\nNote that for backwards compatibility each subcommand can also be\nspecified as a long option. For example, rather than history search\nyou can type history --search. Those long options are deprecated and\nwill be removed in a future release.", + "args": "history [search] [--show-time] [--case-sensitive]\n[--exact | --prefix | --contains] [--max N] [--null] [--reverse]\n [SEARCH_STRING ...]\nhistory delete [--case-sensitive]\n [--exact | --prefix | --contains] SEARCH_STRING ...\nhistory merge\nhistory save\nhistory clear\nhistory clear-session" + }, + "if": { + "shortDescription": "conditionally execute a command", + "description": "if will execute the command CONDITION. If the condition's exit status\nis 0, the commands COMMANDS_TRUE will execute. If the exit status is\nnot 0 and else is given, COMMANDS_FALSE will be executed.\n\nYou can use and or or in the condition. See the second example below.\n\nThe exit status of the last foreground command to exit can always be\naccessed using the $status variable.\n\nThe -h or --help option displays help about using this command.\n\nEXAMPLE\nThe following code will print foo.txt exists if the file foo.txt\nexists and is a regular file, otherwise it will print bar.txt exists\nif the file bar.txt exists and is a regular file, otherwise it will\nprint foo.txt and bar.txt do not exist.\n\n if test -f foo.txt\n echo foo.txt exists\n else if test -f bar.txt\n echo bar.txt exists\n else\n echo foo.txt and bar.txt do not exist\n end\n\nThe following code will print \"foo.txt exists and is readable\" if\nfoo.txt is a regular file and readable\n\n if test -f foo.txt\n and test -r foo.txt\n echo \"foo.txt exists and is readable\"\n end", + "args": "if CONDITION; COMMANDS_TRUE ...;\n[else if CONDITION2; COMMANDS_TRUE2 ...;]\n[else; COMMANDS_FALSE ...;]\nend" + }, + "jobs": { + "shortDescription": "print currently running jobs", + "description": "jobs prints a list of the currently running jobs and their status.\n\njobs accepts the following options:\n\n-c or --command\n Prints the command name for each process in jobs.\n\n-g or --group\n Only prints the group ID of each job.\n\n-l or --last\n Prints only the last job to be started.\n\n-p or --pid\n Prints the process ID for each process in all jobs.\n\n-q or --query\n Prints no output for evaluation of jobs by exit status only.\n For compatibility with old fish versions this is also --quiet\n (but this is deprecated).\n\n-h or --help\n Displays help about using this command.\n\nOn systems that support this feature, jobs will print the CPU usage\nof each job since the last command was executed. The CPU usage is\nexpressed as a percentage of full CPU activity. Note that on\nmultiprocessor systems, the total activity may be more than 100%.\n\nArguments of the form PID or %JOBID restrict the output to jobs with\nthe selected process identifiers or job numbers respectively.\n\nIf the output of jobs is redirected or if it is part of a command\nsubstitution, the column header that is usually printed is omitted,\nmaking it easier to parse.\n\nThe exit status of jobs is 0 if there are running background jobs and\n1 otherwise.\n\nEXAMPLE\njobs outputs a summary of the current jobs, such as two long-running\ntasks in this example:\n\n Job Group State Command\n 2 26012 running nc -l 55232 < /dev/random &\n 1 26011 running python tests/test_11.py &", + "args": "jobs [OPTIONS] [PID | %JOBID]" + }, + "math": { + "shortDescription": "perform mathematics calculations", + "description": "math performs mathematical calculations. It supports simple\noperations such as addition, subtraction, and so on, as well as\nfunctions like abs(), sqrt() and ln().\n\nBy default, the output shows up to 6 decimal places. To change the\nnumber of decimal places, use the --scale option, including --scale=0\nfor integer output. Trailing zeroes will always be trimmed.\n\nKeep in mind that parameter expansion happens before expressions are\nevaluated. This can be very useful in order to perform calculations\ninvolving shell variables or the output of command substitutions, but\nit also means that parenthesis (()) and the asterisk (*) glob\ncharacter have to be escaped or quoted. x can also be used to denote\nmultiplication, but it needs to be followed by whitespace to\ndistinguish it from hexadecimal numbers.\n\nParentheses for functions are optional - math sin pi prints 0.\nHowever, a comma will bind to the inner function, so math pow sin 3,\n5 is an error because it tries to give sin the arguments 3 and 5.\nWhen in doubt, use parentheses.\n\nmath ignores whitespace between arguments and takes its input as\nmultiple arguments (internally joined with a space), so math 2 +2 and\nmath \"2 + 2\" work the same. math 2 2 is an error.\n\nThe following options are available:\n\n-s N or --scale N\n Sets the scale of the result. N must be an integer or the\n word \"max\" for the maximum scale. A scale of zero causes\n results to be truncated, not rounded. Any non-integer\n component is thrown away. So 3/2 returns 1 rather than 2\n which 1.5 would normally round to. This is for compatibility\n with bc which was the basis for this command prior to fish\n 3.0.0. Scale values greater than zero causes the result to be\n rounded using the usual rules to the specified number of\n decimal places.\n\n-b BASE or --base BASE\n Sets the numeric base used for output (math always understands\n hexadecimal numbers as input). It currently understands \"hex\"\n or \"16\" for hexadecimal and \"octal\" or \"8\" for octal and\n implies a scale of 0 (other scales cause an error), so it will\n truncate the result down to an integer. This might change in\n the future. Hex numbers will be printed with a 0x prefix.\n Octal numbers will have a prefix of 0 but aren't understood by\n math as input.\n\n-h or --help\n Displays help about using this command.\n\nRETURN VALUES\nIf the expression is successfully evaluated and doesn't\nover/underflow or return NaN the return status is zero (success) else\none.\n\nSYNTAX\nmath knows some operators, constants, functions and can (obviously)\nread numbers.\n\nFor numbers, . is always the radix character regardless of locale -\n2.5, not 2,5. Scientific notation (10e5) and hexadecimal (0xFF) are\nalso available.\n\nmath allows you to use underscores as visual separators for digit\ngrouping. For example, you can write 1_000_000, 0x_89_AB_CD_EF, and\n1.234_567_e89.\n\nOPERATORS\nmath knows the following operators:\n\n+ for addition\n\n- for subtraction\n\n* or x for multiplication. * is the glob character and needs to be\n quoted or escaped, x needs to be followed by whitespace or it\n looks like 0x hexadecimal notation.\n\n/ for division\n\n^ for exponentiation\n\n% for modulo\n\n( or ) for grouping. These need to be quoted or escaped because ()\n denotes a command substitution.\n\nThey are all used in an infix manner - 5 + 2, not + 5 2.\n\nCONSTANTS\nmath knows the following constants:\n\ne Euler's number\n\npi π, you know this one. Half of Tau\n\ntau Equivalent to 2π, or the number of radians in a circle\n\nUse them without a leading $ - pi - 3 should be about 0.\n\nFUNCTIONS\nmath supports the following functions:\n\nabs the absolute value, with positive sign\n\nacos arc cosine\n\nasin arc sine\n\natan arc tangent\n\natan2 arc tangent of two variables\n\nbitand, bitor and bitxor\n perform bitwise operations. These will throw away any\n non-integer parts and interpret the rest as an int.\n\n Note: bitnot and bitnand don't exist. This is because numbers\n in math don't really have a width in terms of bits, and these\n operations necessarily care about leading zeroes.\n\n If you need to negate a specific number you can do it with an\n xor with a mask, e.g.:\n\n > math --base=hex bitxor 0x0F, 0xFF\n 0xF0\n\n > math --base=hex bitxor 0x2, 0x3\n # Here we mask with 0x3 == 0b111, so our number is 3 bits wide\n # Only the 1 bit isn't set.\n 0x1\n\nceil round number up to the nearest integer\n\ncos the cosine\n\ncosh hyperbolic cosine\n\nexp the base-e exponential function\n\nfac factorial - also known as x! (x * (x - 1) * (x - 2) * ... * 1)\n\nfloor round number down to the nearest integer\n\nln the base-e logarithm\n\nlog or log10\n the base-10 logarithm\n\nlog2 the base-2 logarithm\n\nmax returns the largest of the given numbers - this takes an\n arbitrary number of arguments (but at least one)\n\nmin returns the smallest of the given numbers - this takes an\n arbitrary number of arguments (but at least one)\n\nncr \"from n choose r\" combination function - how many subsets of\n size r can be taken from n (order doesn't matter)\n\nnpr the number of subsets of size r that can be taken from a set\n of n elements (including different order)\n\npow(x,y)\n returns x to the y (and can be written as x ^ y)\n\nround rounds to the nearest integer, away from 0\n\nsin the sine function\n\nsinh the hyperbolic sine\n\nsqrt the square root - (can also be written as x ^ 0.5)\n\ntan the tangent\n\ntanh the hyperbolic tangent\n\nAll of the trigonometric functions use radians (the pi-based scale,\nnot 360°).\n\nEXAMPLES\nmath 1+1 outputs 2.\n\nmath $status - 128 outputs the numerical exit status of the last\ncommand minus 128.\n\nmath 10 / 6 outputs 1.666667.\n\nmath -s0 10.0 / 6.0 outputs 1.\n\nmath -s3 10 / 6 outputs 1.666.\n\nmath \"sin(pi)\" outputs 0.\n\nmath 5 \\* 2 or math \"5 * 2\" or math 5 \"*\" 2 all output 10.\n\nmath 0xFF outputs 255, math 0 x 3 outputs 0 (because it computes 0\nmultiplied by 3).\n\nmath bitand 0xFE, 0x2e outputs 46.\n\nmath \"bitor(9,2)\" outputs 11.\n\nmath --base=hex 192 prints 0xc0.\n\nmath 'ncr(49,6)' prints 13983816 - that's the number of possible\npicks in 6-from-49 lotto.\n\nmath max 5,2,3,1 prints 5.\n\nCOMPATIBILITY NOTES\nFish 1.x and 2.x releases relied on the bc command for handling math\nexpressions. Starting with fish 3.0.0 fish uses the tinyexpr library\nand evaluates the expression without the involvement of any external\ncommands.\n\nYou don't need to use -- before the expression, even if it begins\nwith a minus sign which might otherwise be interpreted as an invalid\noption. If you do insert -- before the expression, it will cause\noption scanning to stop just like for every other command and it\nwon't be part of the expression.", + "args": "math [(-s | --scale) N] [(-b | --base) BASE] EXPRESSION ..." + }, + "not": { + "shortDescription": "negate the exit status of a job", + "description": "not negates the exit status of another command. If the exit status is\nzero, not returns 1. Otherwise, not returns 0.\n\nThe -h or --help option displays help about using this command.\n\nEXAMPLE\nThe following code reports an error and exits if no file named spoon\ncan be found.\n\n if not test -f spoon\n echo There is no spoon\n exit 1\n end", + "args": "not COMMAND [OPTIONS ...]" + }, + "or": { + "shortDescription": "conditionally execute a command", + "description": "or is used to execute a command if the previous command was not\nsuccessful (returned a status of something other than 0).\n\nor statements may be used as part of the condition in an if or while\nblock.\n\nor does not change the current exit status itself, but the command it\nruns most likely will. The exit status of the last foreground command\nto exit can always be accessed using the $status variable.\n\nThe -h or --help option displays help about using this command.\n\nEXAMPLE\nThe following code runs the make command to build a program. If the\nbuild succeeds, the program is installed. If either step fails, make\nclean is run, which removes the files created by the build process.\n\n make; and make install; or make clean\n\nSEE ALSO\n\n• and command", + "args": "COMMAND1; or COMMAND2" + }, + "path": { + "shortDescription": "manipulate and check paths", + "description": "path performs operations on paths.\n\nPATH arguments are taken from the command line unless standard input\nis connected to a pipe or a file, in which case they are read from\nstandard input, one PATH per line. It is an error to supply PATH\narguments on both the command line and on standard input.\n\nArguments starting with - are normally interpreted as switches; --\ncauses the following arguments not to be treated as switches even if\nthey begin with -. Switches and required arguments are recognized\nonly on the command line.\n\nWhen a path starts with -, path filter and path normalize will\nprepend ./ on output to avoid it being interpreted as an option\notherwise, so it's safe to pass path's output to other commands that\ncan handle relative paths.\n\nAll subcommands accept a -q or --quiet switch, which suppresses the\nusual output but exits with the documented status. In this case these\ncommands will quit early, without reading all of the available input.\n\nAll subcommands also accept a -Z or --null-out switch, which makes\nthem print output separated with NUL instead of newlines. This is for\nfurther processing, e.g. passing to another path, or xargs -0. This\nis not recommended when the output goes to the terminal or a command\nsubstitution.\n\nAll subcommands also accept a -z or --null-in switch, which makes\nthem accept arguments from stdin separated with NULL-bytes. Since\nUnix paths can't contain NULL, that makes it possible to handle all\npossible paths and read input from e.g. find -print0. If arguments\nare given on the commandline this has no effect. This should mostly\nbe unnecessary since path automatically starts splitting on NULL if\none appears in the first PATH_MAX bytes, PATH_MAX being the operating\nsystem's maximum length for a path plus a NULL byte.\n\nSome subcommands operate on the paths as strings and so work on\nnonexistent paths, while others need to access the paths themselves\nand so filter out nonexistent paths.\n\nThe following subcommands are available.\n\nBASENAME SUBCOMMAND\n\n path basename [-z | --null-in] [-Z | --null-out] [-q | --quiet] [PATH ...]\n\npath basename returns the last path component of the given path, by\nremoving the directory prefix and removing trailing slashes. In other\nwords, it is the part that is not the dirname. For files you might\ncall it the \"filename\".\n\nIt returns 0 if there was a basename, i.e. if the path wasn't empty\nor just slashes.\n\n Examples\n\n > path basename ./foo.mp4\n foo.mp4\n\n > path basename ../banana\n banana\n\n > path basename /usr/bin/\n bin\n\n > path basename /usr/bin/*\n # This prints all files in /usr/bin/\n # A selection:\n cp\n fish\n grep\n rm\n\nDIRNAME SUBCOMMAND\n\n path dirname [-z | --null-in] [-Z | --null-out] [-q | --quiet] [PATH ...]\n\npath dirname returns the dirname for the given path. This is the part\nbefore the last \"/\", discounting trailing slashes. In other words, it\nis the part that is not the basename (discounting superfluous\nslashes).\n\nIt returns 0 if there was a dirname, i.e. if the path wasn't empty or\njust slashes.\n\n Examples\n\n > path dirname ./foo.mp4\n .\n\n > path dirname ../banana\n ..\n\n > path dirname /usr/bin/\n /usr\n\nEXTENSION SUBCOMMAND\n\n path extension [-z | --null-in] [-Z | --null-out] [-q | --quiet] [PATH ...]\n\npath extension returns the extension of the given path. This is the\npart after (and including) the last \".\", unless that \".\" followed a\n\"/\" or the basename is \".\" or \"..\", in which case there is no\nextension and an empty line is printed.\n\nIf the filename ends in a \".\", only a \".\" is printed.\n\nIt returns 0 if there was an extension.\n\n Examples\n\n > path extension ./foo.mp4\n .mp4\n\n > path extension ../banana\n # an empty line, status 1\n\n > path extension ~/.config\n # an empty line, status 1\n\n > path extension ~/.config.d\n .d\n\n > path extension ~/.config.\n .\n\n > set -l path (path change-extension '' ./foo.mp4)\n > set -l extension (path extension ./foo.mp4)\n > echo $path$extension\n # reconstructs the original path again.\n ./foo.mp4\n\nFILTER SUBCOMMAND\n\n path filter [-z | --null-in] [-Z | --null-out] [-q | --quiet] \\\n [-d] [-f] [-l] [-r] [-w] [-x] \\\n [-v | --invert] [(-t | --type) TYPE] [(-p | --perm) PERMISSION] [PATH ...]\n\npath filter returns all of the given paths that match the given\nchecks. In all cases, the paths need to exist, nonexistent paths are\nalways filtered.\n\nThe available filters are:\n\n• -t or --type with the options: \"dir\", \"file\", \"link\", \"block\",\n \"char\", \"fifo\" and \"socket\", in which case the path needs to be a\n directory, file, link, block device, character device, named pipe\n or socket, respectively.\n\n• -d, -f and -l are short for --type=dir, --type=file and\n --type=link, respectively. There are no shortcuts for the other\n types.\n\n• -p or --perm with the options: \"read\", \"write\", and \"exec\", as well\n as \"suid\", \"sgid\", \"user\" (referring to the path owner) and \"group\"\n (referring to the path's group), in which case the path needs to\n have all of the given permissions for the current user.\n\n• -r, -w and -x are short for --perm=read, --perm=write and\n --perm=exec, respectively. There are no shortcuts for the other\n permissions.\n\nNote that the path needs to be any of the given types, but have all\nof the given permissions. This is because having a path that is both\nwritable and executable makes sense, but having a path that is both a\ndirectory and a file doesn't. Links will count as the type of the\nlinked-to file, so links to files count as files, links to\ndirectories count as directories.\n\nThe filter options can either be given as multiple options, or\ncomma-separated - path filter -t dir,file or path filter --type dir\n--type file are equivalent.\n\nWith --invert, the meaning of the filtering is inverted - any path\nthat wouldn't pass (including by not existing) passes, and any path\nthat would pass fails.\n\nWhen a path starts with -, path filter will prepend ./ to avoid it\nbeing interpreted as an option otherwise.\n\nIt returns 0 if at least one path passed the filter.\n\npath is is shorthand for path filter -q, i.e. just checking without\nproducing output, see The is subcommand.\n\n Examples\n\n > path filter /usr/bin /usr/argagagji\n # The (hopefully) nonexistent argagagji is filtered implicitly:\n /usr/bin\n\n > path filter --type file /usr/bin /usr/bin/fish\n # Only fish is a file\n /usr/bin/fish\n\n > path filter --type file,dir --perm exec,write /usr/bin/fish /home/me\n # fish is a file, which passes, and executable, which passes,\n # but probably not writable, which fails.\n #\n # $HOME is a directory and both writable and executable, typically.\n # So it passes.\n /home/me\n\n > path filter -fdxw /usr/bin/fish /home/me\n # This is the same as above: \"-f\" is \"--type=file\", \"-d\" is \"--type=dir\",\n # \"-x\" is short for \"--perm=exec\" and \"-w\" short for \"--perm=write\"!\n /home/me\n\n > path filter -fx $PATH/*\n # Prints all possible commands - the first entry of each name is what fish would execute!\n\nIS SUBCOMMAND\n\n path is [-z | --null-in] [-Z | --null-out] [-q | --quiet] \\\n [-d] [-f] [-l] [-r] [-w] [-x] \\\n [-v | --invert] [(-t | --type) TYPE] [(-p | --perm) PERMISSION] [PATH ...]\n\npath is is short for path filter -q. It returns true if any of the\ngiven files passes the filter, but does not produce any output.\n\n--quiet can still be passed for compatibility but is redundant. The\noptions are the same as for path filter.\n\n Examples\n\n > path is /usr/bin /usr/argagagji\n # /usr/bin exists, so this returns a status of 0 (true). It prints nothing.\n > path is /usr/argagagji\n # /usr/argagagji does not, so this returns a status of 1 (false). It also prints nothing.\n > path is -fx /bin/sh\n # /bin/sh is usually an executable file, so this returns true.\n\nMTIME SUBCOMMAND\n\n path mtime [-z | --null-in] [-Z | --null-out] [-q | --quiet] [-R | --relative] [PATH ...]\n\npath mtime returns the last modification time (\"mtime\" in unix\njargon) of the given paths, in seconds since the unix epoch (the\nbeginning of the 1st of January 1970).\n\nWith --relative (or -R), it prints the number of seconds since the\nmodification time. It only reads the current time once at start, so\nin case multiple paths are given the times are all relative to the\nstart of path mtime -R running.\n\nIf you want to know if a file is newer or older than another file,\nconsider using test -nt instead. See the test documentation.\n\nIt returns 0 if reading mtime for any path succeeded.\n\n Examples\n\n > date +%s\n # This prints the current time as seconds since the epoch\n 1657217847\n\n > path mtime /etc/\n 1657213796\n\n > path mtime -R /etc/\n 4078\n # So /etc/ on this system was last modified a little over an hour ago\n\n # This is the same as\n > math (date +%s) - (path mtime /etc/)\n\nNORMALIZE SUBCOMMAND\n\n path normalize [-z | --null-in] [-Z | --null-out] [-q | --quiet] [PATH ...]\n\npath normalize returns the normalized versions of all paths. That\nmeans it squashes duplicate \"/\" (except for two leading \"//\"),\ncollapses \"../\" with earlier components and removes \".\" components.\n\nUnlike realpath or path resolve, it does not make the paths absolute.\nIt also does not resolve any symlinks. As such it can operate on\nnon-existent paths.\n\nBecause it operates on paths as strings and doesn't resolve symlinks,\nit works sort of like pwd -L and cd. E.g. path normalize link/.. will\nreturn ., just like cd link; cd .. would return to the current\ndirectory. For a physical view of the filesystem, see path resolve.\n\nLeading \"./\" components are usually removed. But when a path starts\nwith -, path normalize will add it instead to avoid confusion with\noptions.\n\nIt returns 0 if any normalization was done, i.e. any given path\nwasn't in canonical form.\n\n Examples\n\n > path normalize /usr/bin//../../etc/fish\n # The \"//\" is squashed and the \"..\" components neutralize the components before\n /etc/fish\n\n > path normalize /bin//bash\n # The \"//\" is squashed, but /bin isn't resolved even if your system links it to /usr/bin.\n /bin/bash\n\n > path normalize ./my/subdirs/../sub2\n my/sub2\n\n > path normalize -- -/foo\n ./-/foo\n\nRESOLVE SUBCOMMAND\n\n path resolve [-z | --null-in] [-Z | --null-out] [-q | --quiet] [PATH ...]\n\npath resolve returns the normalized, physical and absolute versions\nof all paths. That means it resolves symlinks and does what path\nnormalize does: it squashes duplicate \"/\", collapses \"../\" with\nearlier components and removes \".\" components. Then it turns that\npath into the absolute path starting from the filesystem root \"/\".\n\nIt is similar to realpath, as it creates the \"real\", canonical\nversion of the path. However, for paths that can't be resolved, e.g.\nif they don't exist or form a symlink loop, it will resolve as far as\nit can and normalize the rest.\n\nBecause it resolves symlinks, it works sort of like pwd -P. E.g. path\nresolve link/.. will return the parent directory of what the link\npoints to, just like cd link; cd (pwd -P)/.. would go to it. For a\nlogical view of the filesystem, see path normalize.\n\nIt returns 0 if any normalization or resolution was done, i.e. any\ngiven path wasn't in canonical form.\n\n Examples\n\n > path resolve /bin//sh\n # The \"//\" is squashed, and /bin is resolved if your system links it to /usr/bin.\n # sh here is bash (this is common on linux systems)\n /usr/bin/bash\n\n > path resolve /bin/foo///bar/../baz\n # Assuming /bin exists and is a symlink to /usr/bin, but /bin/foo doesn't.\n # This resolves the /bin/ and normalizes the nonexistent rest:\n /usr/bin/foo/baz\n\nCHANGE-EXTENSION SUBCOMMAND\n\n path change-extension [-z | --null-in] [-Z | --null-out] \\\n [-q | --quiet] EXTENSION [PATH ...]\n\npath change-extension returns the given paths, with their extension\nchanged to the given new extension. The extension is the part after\n(and including) the last \".\", unless that \".\" followed a \"/\" or the\nbasename is \".\" or \"..\", in which case there is no previous extension\nand the new one is simply added.\n\nIf the extension is empty, any previous extension is stripped, along\nwith the \".\". This is, of course, the inverse of path extension.\n\nOne leading dot on the extension is ignored, so \".mp3\" and \"mp3\" are\ntreated the same.\n\nIt returns 0 if it was given any paths.\n\n Examples\n\n > path change-extension mp4 ./foo.wmv\n ./foo.mp4\n\n > path change-extension .mp4 ./foo.wmv\n ./foo.mp4\n\n > path change-extension '' ../banana\n ../banana\n # but status 1, because there was no extension.\n\n > path change-extension '' ~/.config\n /home/alfa/.config\n # status 1\n\n > path change-extension '' ~/.config.d\n /home/alfa/.config\n # status 0\n\n > path change-extension '' ~/.config.\n /home/alfa/.config\n # status 0\n\nSORT SUBCOMMAND\n\n path sort [-z | --null-in] [-Z | --null-out] \\\n [-q | --quiet] [-r | --reverse] \\\n [--key=basename|dirname|path] [PATH ...]\n\npath sort returns the given paths in sorted order. They are sorted in\nthe same order as globs - alphabetically, but with runs of numerical\ndigits compared numerically.\n\nWith --reverse or -r the sort is reversed.\n\nWith --key= only the given part of the path is compared, e.g.\n--key=dirname causes only the dirname to be compared, --key=basename\nonly the basename and --key=path causes the entire path to be\ncompared (this is the default).\n\nWith --unique or -u the sort is deduplicated, meaning only the first\nof a run that have the same key is kept. So if you are sorting by\nbasename, then only the first of each basename is used.\n\nThe sort used is stable, so sorting first by basename and then by\ndirname works and causes the files to be grouped according to\ndirectory.\n\nIt currently returns 0 if it was given any paths.\n\n Examples\n\n > path sort 10-foo 2-bar\n 2-bar\n 10-foo\n\n > path sort --reverse 10-foo 2-bar\n 10-foo\n 2-bar\n\n > path sort --unique --key=basename $fish_function_path/*.fish\n # prints a list of all function files fish would use, sorted by name.\n\nCOMBINING PATH\npath is meant to be easy to combine with itself, other tools and\nfish.\n\nThis is why\n\n• path's output is automatically split by fish if it goes into a\n command substitution, so just doing (path ...) handles all paths,\n even those containing newlines, correctly\n\n• path has --null-in to handle null-delimited input (typically\n automatically detected!), and --null-out to pass on null-delimited\n output\n\nSome examples of combining path:\n\n # Expand all paths in the current directory, leave only executable files, and print their resolved path\n path filter -zZ -xf -- * | path resolve -z\n\n # The same thing, but using find (note -maxdepth needs to come first or find will scream)\n # (this also depends on your particular version of find)\n # Note the `-z` is unnecessary for any sensible version of find - if `path` sees a NULL,\n # it will split on NULL automatically.\n find . -maxdepth 1 -type f -executable -print0 | path resolve -z\n\n set -l paths (path filter -p exec $PATH/fish -Z | path resolve)", + "args": "path basename GENERAL_OPTIONS [PATH ...]\npath dirname GENERAL_OPTIONS [PATH ...]\npath extension GENERAL_OPTIONS [PATH ...]\npath filter GENERAL_OPTIONS [-v | --invert]\n [-d] [-f] [-l] [-r] [-w] [-x]\n [(-t | --type) TYPE] [(-p | --perm) PERMISSION] [PATH ...]\npath is GENERAL_OPTIONS [(-v | --invert)] [(-t | --type) TYPE]\n [-d] [-f] [-l] [-r] [-w] [-x]\n [(-p | --perm) PERMISSION] [PATH ...]\npath mtime GENERAL_OPTIONS [(-R | --relative)] [PATH ...]\npath normalize GENERAL_OPTIONS [PATH ...]\npath resolve GENERAL_OPTIONS [PATH ...]\npath change-extension GENERAL_OPTIONS EXTENSION [PATH ...]\npath sort GENERAL_OPTIONS [-r | --reverse]\n [-u | --unique] [--key=basename|dirname|path] [PATH ...]\n\nGENERAL_OPTIONS\n [-z | --null-in] [-Z | --null-out] [-q | --quiet]" + }, + "printf": { + "shortDescription": "Display formatted text", + "description": "The `printf` command formats and prints text according to a specified format string. Unlike `echo`, `printf` does not append a newline unless explicitly included in the format.", + "args": "FORMAT [ARGUMENT...]" + }, + "pwd": { + "shortDescription": "output the current working directory", + "description": "NOTE: This page documents the fish builtin pwd. To see the\ndocumentation on the pwd command you might have, use command man pwd.\n\npwd outputs (prints) the current working directory.\n\nThe following options are available:\n\n-L or --logical\n Output the logical working directory, without resolving\n symlinks (default behavior).\n\n-P or --physical\n Output the physical working directory, with symlinks resolved.\n\n-h or --help\n Displays help about using this command.\n\nSEE ALSO\nNavigate directories using the directory history or the directory\nstack", + "args": "pwd [-P | --physical]\npwd [-L | --logical]" + }, + "random": { + "shortDescription": "generate random number", + "description": "random generates a pseudo-random integer from a uniform distribution.\nThe range (inclusive) depends on the arguments.\n\nNo arguments indicate a range of 0 to 32767 (inclusive).\n\nIf one argument is specified, the internal engine will be seeded with\nthe argument for future invocations of random and no output will be\nproduced.\n\nTwo arguments indicate a range from START to END (both START and END\nincluded).\n\nThree arguments indicate a range from START to END with a spacing of\nSTEP between possible outputs.\n\nrandom choice will select one random item from the succeeding\narguments.\n\nThe -h or --help option displays help about using this command.\n\nNote that seeding the engine will NOT give the same result across\ndifferent systems.\n\nYou should not consider random cryptographically secure, or even\nstatistically accurate.\n\nEXAMPLE\nThe following code will count down from a random even number between\n10 and 20 to 1:\n\n for i in (seq (random 10 2 20) -1 1)\n echo $i\n end\n\nAnd this will open a random picture from any of the subdirectories:\n\n open (random choice **.jpg)\n\nOr, to only get even numbers from 2 to 20:\n\n random 2 2 20\n\nOr odd numbers from 1 to 3:\n\n random 1 2 3 # or 1 2 4", + "args": "random\nrandom SEED\nrandom START END\nrandom START STEP END\nrandom choice [ITEMS ...]" + }, + "read": { + "shortDescription": "read line of input into variables", + "description": "read reads from standard input and either writes the result back to\nstandard output (for use in command substitution), or stores the\nresult in one or more shell variables. By default, read reads a\nsingle line and splits it into variables on spaces or tabs.\nAlternatively, a null character or a maximum number of characters can\nbe used to terminate the input, and other delimiters can be given.\nUnlike other shells, there is no default variable (such as REPLY) for\nstoring the result - instead, it is printed on standard output.\n\nThe following options are available:\n\n-c CMD or --command CMD\n Sets the initial string in the interactive mode command buffer\n to CMD.\n\n-d or --delimiter DELIMITER\n Splits on DELIMITER. DELIMITER will be used as an entire\n string to split on, not a set of characters.\n\n-g or --global\n Makes the variables global.\n\n-s or --silent\n Masks characters written to the terminal, replacing them with\n asterisks. This is useful for reading things like passwords or\n other sensitive information.\n\n-f or --function\n Scopes the variable to the currently executing function. It is\n erased when the function ends.\n\n-l or --local\n Scopes the variable to the currently executing block. It is\n erased when the block ends. Outside of a block, this is the\n same as --function.\n\n-n or --nchars NCHARS\n Makes read return after reading NCHARS characters or the end\n of the line, whichever comes first.\n\n-p or --prompt PROMPT_CMD\n Uses the output of the shell command PROMPT_CMD as the prompt\n for the interactive mode. The default prompt command is\n set_color green; echo read; set_color normal; echo \"> \"\n\n-P or --prompt-str PROMPT_STR\n Uses the PROMPT_STR as the prompt for the interactive mode. It\n is equivalent to echo $PROMPT_STR and is provided solely to\n avoid the need to frame the prompt as a command. All special\n characters in the string are automatically escaped before\n being passed to the echo command.\n\n-R or --right-prompt RIGHT_PROMPT_CMD\n Uses the output of the shell command RIGHT_PROMPT_CMD as the\n right prompt for the interactive mode. There is no default\n right prompt command.\n\n-S or --shell\n Enables syntax highlighting, tab completions and command\n termination suitable for entering shellscript code in the\n interactive mode. NOTE: Prior to fish 3.0, the short opt for\n --shell was -s, but it has been changed for compatibility with\n bash's -s short opt for --silent.\n\n-t -or --tokenize\n Causes read to split the input into variables by the shell's\n tokenization rules. This means it will honor quotes and\n escaping. This option is of course incompatible with other\n options to control splitting like --delimiter and does not\n honor IFS (like fish's tokenizer). It saves the tokens in the\n manner they'd be passed to commands on the commandline, so\n e.g. a\\ b is stored as a b. Note that currently it leaves\n command substitutions intact along with the parentheses.\n\n-u or --unexport\n Prevents the variables from being exported to child processes\n (default behaviour).\n\n-U or --universal\n Causes the specified shell variable to be made universal.\n\n-x or --export\n Exports the variables to child processes.\n\n-a or --list\n Stores the result as a list in a single variable. This option\n is also available as --array for backwards compatibility.\n\n-z or --null\n Marks the end of the line with the NUL character, instead of\n newline. This also disables interactive mode.\n\n-L or --line\n Reads each line into successive variables, and stops after\n each variable has been filled. This cannot be combined with\n the --delimiter option.\n\nWithout the --line option, read reads a single line of input from\nstandard input, breaks it into tokens, and then assigns one token to\neach variable specified in VARIABLES. If there are more tokens than\nvariables, the complete remainder is assigned to the last variable.\n\nIf no option to determine how to split like --delimiter, --line or\n--tokenize is given, the variable IFS is used as a list of characters\nto split on. Relying on the use of IFS is deprecated and this\nbehaviour will be removed in future versions. The default value of\nIFS contains space, tab and newline characters. As a special case, if\nIFS is set to the empty string, each character of the input is\nconsidered a separate token.\n\nWith the --line option, read reads a line of input from standard\ninput into each provided variable, stopping when each variable has\nbeen filled. The line is not tokenized.\n\nIf no variable names are provided, read enters a special case that\nsimply provides redirection from standard input to standard output,\nuseful for command substitution. For instance, the fish shell command\nbelow can be used to read data that should be provided via a command\nline argument from the console instead of hardcoding it in the\ncommand itself, allowing the command to both be reused as-is in\nvarious contexts with different input values and preventing possibly\nsensitive text from being included in the shell history:\n\n mysql -uuser -p(read)\n\nWhen running in this mode, read does not split the input in any way\nand text is redirected to standard output without any further\nprocessing or manipulation.\n\nIf -a or --array is provided, only one variable name is allowed and\nthe tokens are stored as a list in this variable.\n\nSee the documentation for set for more details on the scoping rules\nfor variables.\n\nWhen read reaches the end-of-file (EOF) instead of the terminator,\nthe exit status is set to 1. Otherwise, it is set to 0.\n\nIn order to protect the shell from consuming too many system\nresources, read will only consume a maximum of 100 MiB (104857600\nbytes); if the terminator is not reached before this limit then\nVARIABLE is set to empty and the exit status is set to 122. This\nlimit can be altered with the fish_read_limit variable. If set to 0\n(zero), the limit is removed.\n\nEXAMPLE\nread has a few separate uses.\n\nThe following code stores the value 'hello' in the shell variable\nfoo.\n\n echo hello|read foo\n\nThe while command is a neat way to handle command output\nline-by-line:\n\n printf '%s\\n' line1 line2 line3 line4 | while read -l foo\n echo \"This is another line: $foo\"\n end\n\nDelimiters given via \"-d\" are taken as one string:\n\n echo a==b==c | read -d == -l a b c\n echo $a # a\n echo $b # b\n echo $c # c\n\n--tokenize honors quotes and escaping like the shell's argument\npassing:\n\n echo 'a\\ b' | read -t first second\n echo $first # outputs \"a b\", $second is empty\n\n echo 'a\"foo bar\"b (command echo wurst)*\" \"{a,b}' | read -lt -l a b c\n echo $a # outputs 'afoo barb' (without the quotes)\n echo $b # outputs '(command echo wurst)* {a,b}' (without the quotes)\n echo $c # nothing\n\nFor an example on interactive use, see Querying for user input.", + "args": "read [OPTIONS] [VARIABLE ...]" + }, + "realpath": { + "shortDescription": "Resolve and print the absolute path", + "description": "Convert each provided path to its absolute, canonical form by resolving symbolic links and relative path components.", + "args": "PATH..." + }, + "return": { + "shortDescription": "stop the current inner function", + "description": "return halts a currently running function. The exit status is set to\nN if it is given. If return is invoked outside of a function or dot\nscript it is equivalent to exit.\n\nIt is often added inside of a conditional block such as an if\nstatement or a switch statement to conditionally stop the executing\nfunction and return to the caller; it can also be used to specify the\nexit status of a function.\n\nIf at the top level of a script, it exits with the given status, like\nexit. If at the top level in an interactive session, it will set\nstatus, but not exit the shell.\n\nThe -h or --help option displays help about using this command.\n\nEXAMPLE\nAn implementation of the false command as a fish function:\n\n function false\n return 1\n end", + "args": "return [N]" + }, + "set": { + "shortDescription": "display and change shell variables", + "description": "set manipulates shell variables.\n\nIf both NAME and VALUE are provided, set assigns any values to\nvariable NAME. Variables in fish are lists, multiple values are\nallowed. One or more variable INDEX can be specified including\nranges (not for all options.)\n\nIf no VALUE is given, the variable will be set to the empty list.\n\nIf set is ran without arguments, it prints the names and values of\nall shell variables in sorted order. Passing scope or export flags\nallows filtering this to only matching variables, so set --local\nwould only show local variables.\n\nWith --erase and optionally a scope flag set will erase the matching\nvariable (or the variable of that name in the smallest possible\nscope).\n\nWith --show, set will describe the given variable names, explaining\nhow they have been defined - in which scope with which values and\noptions.\n\nThe following options control variable scope:\n\n-U or --universal\n Sets a universal variable. The variable will be immediately\n available to all the user's fish instances on the machine, and\n will be persisted across restarts of the shell.\n\n-f or --function\n Sets a variable scoped to the executing function. It is\n erased when the function ends.\n\n-l or --local\n Sets a locally-scoped variable in this block. It is erased\n when the block ends. Outside of a block, this is the same as\n --function.\n\n-g or --global\n Sets a globally-scoped variable. Global variables are\n available to all functions running in the same shell. They\n can be modified or erased.\n\nThese options modify how variables operate:\n\n--export or -x\n Causes the specified shell variable to be exported to child\n processes (making it an \"environment variable\").\n\n--unexport or -u\n Causes the specified shell variable to NOT be exported to\n child processes.\n\n--path Treat specified variable as a path variable; variable will be\n split on colons (:) and will be displayed joined by colons\n when quoted (echo \"$PATH\") or exported.\n\n--unpath\n Causes variable to no longer be treated as a path variable.\n Note: variables ending in \"PATH\" are automatically path\n variables.\n\nFurther options:\n\n-a or --append NAME VALUE ...\n Appends VALUES to the current set of values for variable NAME.\n Can be used with --prepend to both append and prepend at the\n same time. This cannot be used when assigning to a variable\n slice.\n\n-p or --prepend NAME VALUE ...\n Prepends VALUES to the current set of values for variable\n NAME. This can be used with --append to both append and\n prepend at the same time. This cannot be used when assigning\n to a variable slice.\n\n-e or --erase NAME*[*INDEX]\n Causes the specified shell variables to be erased. Supports\n erasing from multiple scopes at once. Individual items in a\n variable at INDEX in brackets can be specified.\n\n-q or --query NAME*[*INDEX]\n Test if the specified variable names are defined. If an INDEX\n is provided, check for items at that slot. Does not output\n anything, but the shell status is set to the number of\n variables specified that were not defined, up to a maximum of\n 255. If no variable was given, it also returns 255.\n\n-n or --names\n List only the names of all defined variables, not their value.\n The names are guaranteed to be sorted.\n\n-S or --show\n Shows information about the given variables. If no variable\n names are given then all variables are shown in sorted order.\n It shows the scopes the given variables are set in, along with\n the values in each and whether or not it is exported. No\n other flags can be used with this option.\n\n-L or --long\n Do not abbreviate long values when printing set variables.\n\n-h or --help\n Displays help about using this command.\n\nIf a variable is set to more than one value, the variable will be a\nlist with the specified elements. If a variable is set to zero\nelements, it will become a list with zero elements.\n\nIf the variable name is one or more list elements, such as PATH[1 3\n7], only those list elements specified will be changed. If you\nspecify a negative index when expanding or assigning to a list\nvariable, the index will be calculated from the end of the list. For\nexample, the index -1 means the last index of a list.\n\nThe scoping rules when creating or updating a variable are:\n\n• Variables may be explicitly set as universal, global, function, or\n local. Variables with the same name but in a different scope will\n not be changed.\n\n• If the scope of a variable is not explicitly set but a variable by\n that name has been previously defined, the scope of the existing\n variable is used. If the variable is already defined in multiple\n scopes, the variable with the narrowest scope will be updated.\n\n• If a variable's scope is not explicitly set and there is no\n existing variable by that name, the variable will be local to the\n currently executing function. Note that this is different from\n using the -l or --local flag, in which case the variable will be\n local to the most-inner currently executing block, while without\n them the variable will be local to the function as a whole. If no\n function is executing, the variable will be set in the global\n scope.\n\nThe exporting rules when creating or updating a variable are\nidentical to the scoping rules for variables:\n\n• Variables may be explicitly set to either exported or not exported.\n When an exported variable goes out of scope, it is unexported.\n\n• If a variable is not explicitly set to be exported or not exported,\n but has been previously defined, the previous exporting rule for\n the variable is kept.\n\n• If a variable is not explicitly set to be either exported or\n unexported and has never before been defined, the variable will not\n be exported.\n\nIn query mode, the scope to be examined can be specified. Whether\nthe variable has to be a path variable or exported can also be\nspecified.\n\nIn erase mode, if variable indices are specified, only the specified\nslices of the list variable will be erased.\n\nset requires all options to come before any other arguments. For\nexample, set flags -l will have the effect of setting the value of\nthe variable flags to '-l', not making the variable local.\n\nEXIT STATUS\nIn assignment mode, set does not modify the exit status, but passes\nalong whatever status was set, including by command substitutions.\nThis allows capturing the output and exit status of a subcommand,\nlike in if set output (command).\n\nIn query mode, the exit status is the number of variables that were\nnot found.\n\nIn erase mode, set exits with a zero exit status in case of success,\nwith a non-zero exit status if the commandline was invalid, if any of\nthe variables did not exist or was a special read-only variable.\n\nEXAMPLES\nPrint all global, exported variables:\n\n > set -gx\n\nSet the value of the variable $foo to be 'hi'.:\n\n > set foo hi\n\nAppend the value \"there\" to the variable $foo:\n\n > set -a foo there\n\nRemove $smurf from the scope:\n\n > set -e smurf\n\nRemove $smurf from the global and universal scopes:\n\n > set -e -Ug smurf\n\nChange the fourth element of the $PATH list to ~/bin:\n\n > set PATH[4] ~/bin\n\nOutputs the path to Python if type -p returns true:\n\n if set python_path (type -p python)\n echo \"Python is at $python_path\"\n end\n\nSetting a variable doesn't modify $status; a command substitution\nstill will, though:\n\n > echo $status\n 0\n > false\n > set foo bar\n > echo $status\n 1\n > true\n > set foo banana (false)\n > echo $status\n 1\n\nVAR=VALUE command sets a variable for just one command, like other\nshells. This runs fish with a temporary home directory:\n\n > HOME=(mktemp -d) fish\n\n(which is essentially the same as):\n\n > begin; set -lx HOME (mktemp -d); fish; end\n\nNOTES\n\n• Fish versions prior to 3.0 supported the syntax set PATH[1] PATH[4]\n /bin /sbin, which worked like set PATH[1 4] /bin /sbin.", + "args": "set\nset (-f | --function) (-l | local) (-g | --global) (-U | --universal)\nset [-Uflg] NAME [VALUE ...]\nset [-Uflg] NAME[[INDEX ...]] [VALUE ...]\nset (-a | --append) [-flgU] NAME VALUE ...\nset (-q | --query) (-e | --erase) [-flgU] [NAME][[INDEX]] ...]\nset (-S | --show) [NAME ...]" + }, + "set_color": { + "shortDescription": "set the terminal color", + "description": "set_color is used to control the color and styling of text in the\nterminal. VALUE describes that styling. VALUE can be a reserved color\nname like red or an RGB color value given as 3 or 6 hexadecimal\ndigits (\"F27\" or \"FF2277\"). A special keyword normal resets text\nformatting to terminal defaults.\n\nValid colors include:\n\n • black, red, green, yellow, blue, magenta, cyan, white\n\n • brblack, brred, brgreen, bryellow, brblue, brmagenta, brcyan,\n brwhite\n\nThe br- (as in 'bright') forms are full-brightness variants of the 8\nstandard-brightness colors on many terminals. brblack has higher\nbrightness than black - towards gray.\n\nAn RGB value with three or six hex digits, such as A0FF33 or f2f can\nbe used. Fish will choose the closest supported color. A three digit\nvalue is equivalent to specifying each digit twice; e.g., set_color\n2BC is the same as set_color 22BBCC. Hexadecimal RGB values can be in\nlower or uppercase. Depending on the capabilities of your terminal\n(and the level of support set_color has for it) the actual color may\nbe approximated by a nearby matching reserved color name or set_color\nmay not have an effect on color.\n\nA second color may be given as a desired fallback color. e.g.\nset_color 124212 brblue will instruct set_color to use brblue if a\nterminal is not capable of the exact shade of grey desired. This is\nvery useful when an 8 or 16 color terminal might otherwise not use a\ncolor.\n\nThe following options are available:\n\n-b or --background COLOR\n Sets the background color.\n\n-c or --print-colors\n Prints the given colors or a colored list of the 16 named\n colors.\n\n-o or --bold\n Sets bold mode.\n\n-d or --dim\n Sets dim mode.\n\n-i or --italics\n Sets italics mode.\n\n-r or --reverse\n Sets reverse mode.\n\n-u or --underline\n Sets underlined mode.\n\n-h or --help\n Displays help about using this command.\n\nUsing the normal keyword will reset foreground, background, and all\nformatting back to default.\n\nNOTES\n\n1. Using the normal keyword will reset both background and foreground\n colors to whatever is the default for the terminal.\n\n2. Setting the background color only affects subsequently written\n characters. Fish provides no way to set the background color for\n the entire terminal window. Configuring the window background\n color (and other attributes such as its opacity) has to be done\n using whatever mechanisms the terminal provides. Look for a config\n option.\n\n3. Some terminals use the --bold escape sequence to switch to a\n brighter color set rather than increasing the weight of text.\n\n4. set_color works by printing sequences of characters to standard\n output. If used in command substitution or a pipe, these\n characters will also be captured. This may or may not be\n desirable. Checking the exit status of isatty stdout before using\n set_color can be useful to decide not to colorize output in a\n script.\n\nEXAMPLES\n\n set_color red; echo \"Roses are red\"\n set_color blue; echo \"Violets are blue\"\n set_color 62A; echo \"Eggplants are dark purple\"\n set_color normal; echo \"Normal is nice\" # Resets the background too\n\nTERMINAL CAPABILITY DETECTION\nFish uses some heuristics to determine what colors a terminal\nsupports to avoid sending sequences that it won't understand.\n\nIn particular it will:\n\n• Enable 256 colors if TERM contains \"xterm\", except for known\n exceptions (like MacOS 10.6 Terminal.app)\n\n• Enable 24-bit (\"true-color\") even if the $TERM entry only reports\n 256 colors. This includes modern xterm, VTE-based terminals like\n Gnome Terminal, Konsole and iTerm2.\n\n• Detect support for italics, dim, reverse and other modes.\n\nIf terminfo reports 256 color support for a terminal, 256 color\nsupport will always be enabled.\n\nTo force true-color support on or off, set fish_term24bit to \"1\" for\non and 0 for off - set -g fish_term24bit 1.\n\nTo debug color palette problems, tput colors may be useful to see the\nnumber of colors in terminfo for a terminal. Fish launched as fish -d\nterm_support will include diagnostic messages that indicate the color\nsupport mode in use.\n\nThe set_color command uses the terminfo database to look up how to\nchange terminal colors on whatever terminal is in use. Some systems\nhave old and incomplete terminfo databases, and lack color\ninformation for terminals that support it. Fish assumes that all\nterminals can use the [ANSI\nX3.64](https://en.wikipedia.org/wiki/ANSI_escape_code) escape\nsequences if the terminfo definition indicates a color below 16 is\nnot supported.", + "args": "set_color [OPTIONS] VALUE" + }, + "source": { + "shortDescription": "evaluate contents of file", + "description": "source evaluates the commands of the specified FILE in the current\nshell as a new block of code. This is different from starting a new\nprocess to perform the commands (i.e. fish < FILE) since the commands\nwill be evaluated by the current shell, which means that changes in\nshell variables will affect the current shell. If additional\narguments are specified after the file name, they will be inserted\ninto the argv variable. The argv variable will not include the name\nof the sourced file.\n\nfish will search the working directory to resolve relative paths but\nwill not search PATH .\n\nIf no file is specified and stdin is not the terminal, or if the file\nname - is used, stdin will be read.\n\nThe exit status of source is the exit status of the last job to\nexecute. If something goes wrong while opening or reading the file,\nsource exits with a non-zero status.\n\n. (a single period) is an alias for the source command. The use of .\nis deprecated in favour of source, and . will be removed in a future\nversion of fish.\n\nsource creates a new local scope; set --local within a sourced block\nwill not affect variables in the enclosing scope.\n\nThe -h or --help option displays help about using this command.\n\nEXAMPLE\n\n source ~/.config/fish/config.fish\n # Causes fish to re-read its initialization file.\n\nCAVEATS\nIn fish versions prior to 2.3.0, the argv variable would have a\nsingle element (the name of the sourced file) if no arguments are\npresent. Otherwise, it would contain arguments without the name of\nthe sourced file. That behavior was very confusing and unlike other\nshells such as bash and zsh.", + "args": "source FILE [ARGUMENTS ...]\nSOMECOMMAND | source" + }, + "status": { + "shortDescription": "query fish runtime information", + "description": "With no arguments, status displays a summary of the current login and\njob control status of the shell.\n\nThe following operations (subcommands) are available:\n\nis-command-substitution, -c or --is-command-substitution\n Returns 0 if fish is currently executing a command\n substitution.\n\nis-block, -b or --is-block\n Returns 0 if fish is currently executing a block of code.\n\nis-breakpoint\n Returns 0 if fish is currently showing a prompt in the context\n of a breakpoint command. See also the fish_breakpoint_prompt\n function.\n\nis-interactive, -i or --is-interactive\n Returns 0 if fish is interactive - that is, connected to a\n keyboard.\n\nis-login, -l or --is-login\n Returns 0 if fish is a login shell - that is, if fish should\n perform login tasks such as setting up PATH.\n\nis-full-job-control or --is-full-job-control\n Returns 0 if full job control is enabled.\n\nis-interactive-job-control or --is-interactive-job-control\n Returns 0 if interactive job control is enabled.\n\nis-no-job-control or --is-no-job-control\n Returns 0 if no job control is enabled.\n\ncurrent-command\n Prints the name of the currently-running function or command,\n like the deprecated variable.\n\ncurrent-commandline\n Prints the entirety of the currently-running commandline,\n inclusive of all jobs and operators.\n\nfilename, current-filename, -f or --current-filename\n Prints the filename of the currently-running script. If the\n current script was called via a symlink, this will return the\n symlink. If the current script was received by piping into\n source, then this will return -.\n\nbasename\n Prints just the filename of the running script, without any\n path components before.\n\ndirname\n Prints just the path to the running script, without the actual\n filename itself. This can be relative to PWD (including just\n \".\"), depending on how the script was called. This is the same\n as passing the filename to dirname(3). It's useful if you want\n to use other files in the current script's directory or\n similar.\n\nfish-path\n Prints the absolute path to the currently executing instance\n of fish. This is a best-effort attempt and the exact output is\n down to what the platform gives fish. In some cases you might\n only get \"fish\".\n\nfunction or current-function\n Prints the name of the currently called function if able, when\n missing displays \"Not a function\" (or equivalent translated\n string).\n\nline-number, current-line-number, -n or --current-line-number\n Prints the line number of the currently running script.\n\nstack-trace, print-stack-trace, -t or --print-stack-trace\n Prints a stack trace of all function calls on the call stack.\n\njob-control, -j or --job-control CONTROL_TYPE\n Sets the job control type to CONTROL_TYPE, which can be none,\n full, or interactive.\n\nfeatures\n Lists all available feature flags.\n\ntest-feature FEATURE\n Returns 0 when FEATURE is enabled, 1 if it is disabled, and 2\n if it is not recognized.\n\nNOTES\nFor backwards compatibility most subcommands can also be specified as\na long or short option. For example, rather than status is-login you\ncan type status --is-login. The flag forms are deprecated and may be\nremoved in a future release (but not before fish 4.0).\n\nYou can only specify one subcommand per invocation even if you use\nthe flag form of the subcommand.", + "args": "status\nstatus is-login\nstatus is-interactive\nstatus is-block\nstatus is-breakpoint\nstatus is-command-substitution\nstatus is-no-job-control\nstatus is-full-job-control\nstatus is-interactive-job-control\nstatus current-command\nstatus current-commandline\nstatus filename\nstatus basename\nstatus dirname\nstatus fish-path\nstatus function\nstatus line-number\nstatus stack-trace\nstatus job-control CONTROL_TYPE\nstatus features\nstatus test-feature FEATURE" + }, + "string": { + "shortDescription": "manipulate strings", + "description": "string performs operations on strings.\n\nSTRING arguments are taken from the command line unless standard\ninput is connected to a pipe or a file, in which case they are read\nfrom standard input, one STRING per line. It is an error to supply\nSTRING arguments on the command line and on standard input.\n\nArguments beginning with - are normally interpreted as switches; --\ncauses the following arguments not to be treated as switches even if\nthey begin with -. Switches and required arguments are recognized\nonly on the command line.\n\nMost subcommands accept a -q or --quiet switch, which suppresses the\nusual output but exits with the documented status. In this case these\ncommands will quit early, without reading all of the available input.\n\nThe following subcommands are available.\n\nCOLLECT SUBCOMMAND\nstring collect [-a | --allow-empty] [-N | --no-trim-newlines] [STRING ...]\n\nstring collect collects its input into a single output argument,\nwithout splitting the output when used in a command substitution.\nThis is useful when trying to collect multiline output from another\ncommand into a variable. Exit status: 0 if any output argument is\nnon-empty, or 1 otherwise.\n\nA command like echo (cmd | string collect) is mostly equivalent to a\nquoted command substitution (echo \"$(cmd)\"). The main difference is\nthat the former evaluates to zero or one elements whereas the quoted\ncommand substitution always evaluates to one element due to string\ninterpolation.\n\nIf invoked with multiple arguments instead of input, string collect\npreserves each argument separately, where the number of output\narguments is equal to the number of arguments given to string\ncollect.\n\nAny trailing newlines on the input are trimmed, just as with \"$(cmd)\"\nsubstitution. Use --no-trim-newlines to disable this behavior, which\nmay be useful when running a command such as set contents (cat\nfilename | string collect -N).\n\nWith --allow-empty, string collect always prints one (empty)\nargument. This can be used to prevent an argument from disappearing.\n\n Examples\n\n > echo \"zero $(echo one\\ntwo\\nthree) four\"\n zero one\n two\n three four\n\n > echo \\\"(echo one\\ntwo\\nthree | string collect)\\\"\n \"one\n two\n three\"\n\n > echo \\\"(echo one\\ntwo\\nthree | string collect -N)\\\"\n \"one\n two\n three\n \"\n\n > echo foo(true | string collect --allow-empty)bar\n foobar\n\nESCAPE AND UNESCAPE SUBCOMMANDS\nstring escape [-n | --no-quoted] [--style=] [STRING ...]\nstring unescape [--style=] [STRING ...]\n\nstring escape escapes each STRING in one of three ways. The first is\n--style=script. This is the default. It alters the string such that\nit can be passed back to eval to produce the original argument again.\nBy default, all special characters are escaped, and quotes are used\nto simplify the output when possible. If -n or --no-quoted is given,\nthe simplifying quoted format is not used. Exit status: 0 if at least\none string was escaped, or 1 otherwise.\n\n--style=var ensures the string can be used as a variable name by hex\nencoding any non-alphanumeric characters. The string is first\nconverted to UTF-8 before being encoded.\n\n--style=url ensures the string can be used as a URL by hex encoding\nany character which is not legal in a URL. The string is first\nconverted to UTF-8 before being encoded.\n\n--style=regex escapes an input string for literal matching within a\nregex expression. The string is first converted to UTF-8 before being\nencoded.\n\nstring unescape performs the inverse of the string escape command. If\nthe string to be unescaped is not properly formatted it is ignored.\nFor example, doing string unescape --style=var (string escape\n--style=var $str) will return the original string. There is no\nsupport for unescaping --style=regex.\n\n Examples\n\n > echo \\x07 | string escape\n \\cg\n\n > string escape --style=var 'a1 b2'\\u6161\n a1_20_b2_E6_85_A1\n\nJOIN AND JOIN0 SUBCOMMANDS\nstring join [-q | --quiet] SEP [STRING ...]\nstring join0 [-q | --quiet] [STRING ...]\n\nstring join joins its STRING arguments into a single string separated\nby SEP, which can be an empty string. Exit status: 0 if at least one\njoin was performed, or 1 otherwise. If -n or --no-empty is specified,\nempty strings are excluded from consideration (e.g. string join -n +\na b \"\" c would expand to a+b+c not a+b++c).\n\nstring join0 joins its STRING arguments into a single string\nseparated by the zero byte (NUL), and adds a trailing NUL. This is\nmost useful in conjunction with tools that accept NUL-delimited\ninput, such as sort -z. Exit status: 0 if at least one join was\nperformed, or 1 otherwise.\n\nBecause Unix uses NUL as the string terminator, passing the output of\nstring join0 as an argument to a command (via a command substitution)\nwon't actually work. Fish will pass the correct bytes along, but the\ncommand won't be able to tell where the argument ends. This is a\nlimitation of Unix' argument passing.\n\n Examples\n\n > seq 3 | string join ...\n 1...2...3\n\n # Give a list of NUL-separated filenames to du (this is a GNU extension)\n > string join0 file1 file2 file\\nwith\\nmultiple\\nlines | du --files0-from=-\n\n # Just put the strings together without a separator\n > string join '' a b c\n abc\n\nLENGTH SUBCOMMAND\nstring length [-q | --quiet] [-V | --visible] [STRING ...]\n\nstring length reports the length of each string argument in\ncharacters. Exit status: 0 if at least one non-empty STRING was\ngiven, or 1 otherwise.\n\nWith -V or --visible, it uses the visible width of the arguments.\nThat means it will discount escape sequences fish knows about,\naccount for $fish_emoji_width and $fish_ambiguous_width. It will also\ncount each line (separated by \\n) on its own, and with a carriage\nreturn (\\r) count only the widest stretch on a line. The intent is to\nmeasure the number of columns the STRING would occupy in the current\nterminal.\n\n Examples\n\n > string length 'hello, world'\n 12\n\n > set str foo\n > string length -q $str; echo $status\n 0\n # Equivalent to test -n \"$str\"\n\n > string length --visible (set_color red)foobar\n # the set_color is discounted, so this is the width of \"foobar\"\n 6\n\n > string length --visible 🐟🐟🐟🐟\n # depending on $fish_emoji_width, this is either 4 or 8\n # in new terminals it should be\n 8\n\n > string length --visible abcdef\\r123\n # this displays as \"123def\", so the width is 6\n 6\n\n > string length --visible a\\nbc\n # counts \"a\" and \"bc\" as separate lines, so it prints width for each\n 1\n 2\n\nLOWER SUBCOMMAND\nstring lower [-q | --quiet] [STRING ...]\n\nstring lower converts each string argument to lowercase. Exit status:\n0 if at least one string was converted to lowercase, else 1. This\nmeans that in conjunction with the -q flag you can readily test\nwhether a string is already lowercase.\n\nMATCH SUBCOMMAND\nstring match [-a | --all] [-e | --entire] [-i | --ignore-case]\n [-g | --groups-only] [-r | --regex] [-n | --index]\n [-q | --quiet] [-v | --invert]\n PATTERN [STRING ...]\n\nstring match tests each STRING against PATTERN and prints matching\nsubstrings. Only the first match for each STRING is reported unless\n-a or --all is given, in which case all matches are reported.\n\nIf you specify the -e or --entire then each matching string is\nprinted including any prefix or suffix not matched by the pattern\n(equivalent to grep without the -o flag). You can, obviously, achieve\nthe same result by prepending and appending * or .* depending on\nwhether or not you have specified the --regex flag. The --entire flag\nis simply a way to avoid having to complicate the pattern in that\nfashion and make the intent of the string match clearer. Without\n--entire and --regex, a PATTERN will need to match the entire STRING\nbefore it will be reported.\n\nMatching can be made case-insensitive with --ignore-case or -i.\n\nIf --groups-only or -g is given, only the capturing groups will be\nreported - meaning the full match will be skipped. This is\nincompatible with --entire and --invert, and requires --regex. It is\nuseful as a simple cutting tool instead of string replace, so you can\nsimply choose \"this part\" of a string.\n\nIf --index or -n is given, each match is reported as a 1-based start\nposition and a length. By default, PATTERN is interpreted as a glob\npattern matched against each entire STRING argument. A glob pattern\nis only considered a valid match if it matches the entire STRING.\n\nIf --regex or -r is given, PATTERN is interpreted as a\nPerl-compatible regular expression, which does not have to match the\nentire STRING. For a regular expression containing capturing groups,\nmultiple items will be reported for each match, one for the entire\nmatch and one for each capturing group. With this, only the matching\npart of the STRING will be reported, unless --entire is given.\n\nWhen matching via regular expressions, string match automatically\nsets variables for all named capturing groups ((?expression)).\nIt will create a variable with the name of the group, in the default\nscope, for each named capturing group, and set it to the value of the\ncapturing group in the first matched argument. If a named capture\ngroup matched an empty string, the variable will be set to the empty\nstring (like set var \"\"). If it did not match, the variable will be\nset to nothing (like set var). When --regex is used with --all, this\nbehavior changes. Each named variable will contain a list of matches,\nwith the first match contained in the first element, the second match\nin the second, and so on. If the group was empty or did not match,\nthe corresponding element will be an empty string.\n\nIf --invert or -v is used the selected lines will be only those which\ndo not match the given glob pattern or regular expression.\n\nExit status: 0 if at least one match was found, or 1 otherwise.\n\n Match Glob Examples\n\n > string match '?' a\n a\n\n > string match 'a*b' axxb\n axxb\n\n > string match -i 'a??B' Axxb\n Axxb\n\n > string match -- '-*' -h foo --version bar\n # To match things that look like options, we need a `--`\n # to tell string its options end there.\n -h\n --version\n\n > echo 'ok?' | string match '*\\?'\n ok?\n\n # Note that only the second STRING will match here.\n > string match 'foo' 'foo1' 'foo' 'foo2'\n foo\n\n > string match -e 'foo' 'foo1' 'foo' 'foo2'\n foo1\n foo\n foo2\n\n > string match 'foo?' 'foo1' 'foo' 'foo2'\n foo1\n foo2\n\n Match Regex Examples\n\n > string match -r 'cat|dog|fish' 'nice dog'\n dog\n\n > string match -r -v \"c.*[12]\" {cat,dog}(seq 1 4)\n dog1\n dog2\n cat3\n dog3\n cat4\n dog4\n\n > string match -r -- '-.*' -h foo --version bar\n # To match things that look like options, we need a `--`\n # to tell string its options end there.\n -h\n --version\n\n > string match -r '(\\d\\d?):(\\d\\d):(\\d\\d)' 2:34:56\n 2:34:56\n 2\n 34\n 56\n\n > string match -r '^(\\w{2,4})\\1$' papa mud murmur\n papa\n pa\n murmur\n mur\n\n > string match -r -a -n at ratatat\n 2 2\n 4 2\n 6 2\n\n > string match -r -i '0x[0-9a-f]{1,8}' 'int magic = 0xBadC0de;'\n 0xBadC0de\n\n > echo $version\n 3.1.2-1575-ga2ff32d90\n > string match -rq '(?\\d+).(?\\d+).(?\\d+)' -- $version\n > echo \"You are using fish $major!\"\n You are using fish 3!\n\n > string match -raq ' *(?[^.!?]+)(?[.!?])?' \"hello, friend. goodbye\"\n > printf \"%s\\n\" -- $sentence\n hello, friend\n goodbye\n > printf \"%s\\n\" -- $punctuation\n .\n\n > string match -rq '(?hello)' 'hi'\n > count $word\n 0\n\nPAD AND SHORTEN SUBCOMMANDS\nstring pad [-r | --right] [(-c | --char) CHAR] [(-w | --width) INTEGER]\n [STRING ...]\n\nstring pad extends each STRING to the given visible width by adding\nCHAR to the left. That means the width of all visible characters\nadded together, excluding escape sequences and accounting for\nfish_emoji_width and fish_ambiguous_width. It is the amount of\ncolumns in a terminal the STRING occupies.\n\nThe escape sequences reflect what fish knows about, and how it\ncomputes its output. Your terminal might support more escapes, or not\nsupport escape sequences that fish knows about.\n\nIf -r or --right is given, add the padding after a string.\n\nIf -c or --char is given, pad with CHAR instead of whitespace.\n\nThe output is padded to the maximum width of all input strings. If -w\nor --width is given, use at least that.\n\n > string pad -w 10 abc abcdef\n abc\n abcdef\n\n > string pad --right --char=🐟 \"fish are pretty\" \"rich. \"\n fish are pretty\n rich. 🐟🐟🐟🐟\n\n > string pad -w$COLUMNS (date)\n # Prints the current time on the right edge of the screen.\n\nSEE ALSO\n\n• The printf command can do simple padding, for example printf %10s\\n\n works like string pad -w10.\n\n• string length with the --visible option can be used to show what\n fish thinks the width is.\nstring shorten [(-c | --char) CHARS] [(-m | --max) INTEGER]\n [-N | --no-newline] [-l | --left] [-q | --quiet] [STRING ...]\n\nstring shorten truncates each STRING to the given visible width and\nadds an ellipsis to indicate it. \"Visible width\" means the width of\nall visible characters added together, excluding escape sequences and\naccounting for fish_emoji_width and fish_ambiguous_width. It is the\namount of columns in a terminal the STRING occupies.\n\nThe escape sequences reflect what fish knows about, and how it\ncomputes its output. Your terminal might support more escapes, or not\nsupport escape sequences that fish knows about.\n\nIf -m or --max is given, truncate at the given width. Otherwise, the\nlowest non-zero width of all input strings is used. A max of 0 means\nno shortening takes place, all STRINGs are printed as-is.\n\nIf -N or --no-newline is given, only the first line (or last line\nwith --left) of each STRING is used, and an ellipsis is added if it\nwas multiline. This only works for STRINGs being given as arguments,\nmultiple lines given on stdin will be interpreted as separate STRINGs\ninstead.\n\nIf -c or --char is given, add CHAR instead of an ellipsis. This can\nalso be empty or more than one character.\n\nIf -l or --left is given, remove text from the left on instead, so\nthis prints the longest suffix of the string that fits. With\n--no-newline, this will take from the last line instead of the first.\n\nIf -q or --quiet is given, string shorten only runs for the return\nvalue - if anything would be shortened, it returns 0, else 1.\n\nThe default ellipsis is …. If fish thinks your system is incapable\nbecause of your locale, it will use ... instead.\n\nThe return value is 0 if any shortening occured, 1 otherwise.\n\n > string shorten foo foobar\n # No width was given, we infer, and \"foo\" is the shortest.\n foo\n fo…\n\n > string shorten --char=\"...\" foo foobar\n # The target width is 3 because of \"foo\",\n # and our ellipsis is 3 too, so we can't really show anything.\n # This is the default ellipsis if your locale doesn't allow \"…\".\n foo\n ...\n\n > string shorten --char=\"\" --max 4 abcdef 123456\n # Leaving the char empty makes us not add an ellipsis\n # So this truncates at 4 columns:\n abcd\n 1234\n\n > touch \"a multiline\"\\n\"file\"\n > for file in *; string shorten -N -- $file; end\n # Shorten the multiline file so we only show one line per file:\n a multiline…\n\n > ss -p | string shorten -m$COLUMNS -c \"\"\n # `ss` from Linux' iproute2 shows socket information, but prints extremely long lines.\n # This shortens input so it fits on the screen without overflowing lines.\n\n > git branch | string match -rg '^\\* (.*)' | string shorten -m20\n # Take the current git branch and shorten it at 20 columns.\n # Here the branch is \"builtin-path-with-expand\"\n builtin-path-with-e…\n\n > git branch | string match -rg '^\\* (.*)' | string shorten -m20 --left\n # Taking 20 columns from the right instead:\n …in-path-with-expand\n\nSEE ALSO\n\n• string's pad subcommand does the inverse of this command, adding\n padding to a specific width instead.\n\n• The printf command can do simple padding, for example printf %10s\\n\n works like string pad -w10.\n\n• string length with the --visible option can be used to show what\n fish thinks the width is.\n\nREPEAT SUBCOMMAND\nstring repeat [(-n | --count) COUNT] [(-m | --max) MAX] [-N | --no-newline]\n [-q | --quiet] [STRING ...]\n\nstring repeat repeats the STRING -n or --count times. The -m or --max\noption will limit the number of outputted characters (excluding the\nnewline). This option can be used by itself or in conjunction with\n--count. If both --count and --max are present, max char will be\noutputed unless the final repeated string size is less than max, in\nthat case, the string will repeat until count has been reached. Both\n--count and --max will accept a number greater than or equal to zero,\nin the case of zero, nothing will be outputed. If -N or --no-newline\nis given, the output won't contain a newline character at the end.\nExit status: 0 if yielded string is not empty, 1 otherwise.\n\n Examples\n Repeat Examples\n\n > string repeat -n 2 'foo '\n foo foo\n\n > echo foo | string repeat -n 2\n foofoo\n\n > string repeat -n 2 -m 5 'foo'\n foofo\n\n > string repeat -m 5 'foo'\n foofo\n\nREPLACE SUBCOMMAND\nstring replace [-a | --all] [-f | --filter] [-i | --ignore-case]\n [-r | --regex] [-q | --quiet] PATTERN REPLACEMENT [STRING ...]\n\nstring replace is similar to string match but replaces\nnon-overlapping matching substrings with a replacement string and\nprints the result. By default, PATTERN is treated as a literal\nsubstring to be matched.\n\nIf -r or --regex is given, PATTERN is interpreted as a\nPerl-compatible regular expression, and REPLACEMENT can contain\nC-style escape sequences like t as well as references to capturing\ngroups by number or name as $n or ${n}.\n\nIf you specify the -f or --filter flag then each input string is\nprinted only if a replacement was done. This is useful where you\nwould otherwise use this idiom: a_cmd | string match pattern | string\nreplace pattern new_pattern. You can instead just write a_cmd |\nstring replace --filter pattern new_pattern.\n\nExit status: 0 if at least one replacement was performed, or 1\notherwise.\n\n Replace Literal Examples\n\n > string replace is was 'blue is my favorite'\n blue was my favorite\n\n > string replace 3rd last 1st 2nd 3rd\n 1st\n 2nd\n last\n\n > string replace -a ' ' 'spaces to underscores'\n spaces_to_underscores\n\n Replace Regex Examples\n\n > string replace -r -a '[^\\d.]+' ' ' '0 one two 3.14 four 5x'\n 0 3.14 5\n\n > string replace -r '(\\w+)\\s+(\\w+)' '$2 $1 $$' 'left right'\n right left $\n\n > string replace -r '\\s*newline\\s*' '\\n' 'put a newline here'\n put a\n here\n\nSPLIT AND SPLIT0 SUBCOMMANDS\nstring split [(-f | --fields) FIELDS] [(-m | --max) MAX] [-n | --no-empty]\n [-q | --quiet] [-r | --right] SEP [STRING ...]\nstring split0 [(-f | --fields) FIELDS] [(-m | --max) MAX] [-n | --no-empty]\n [-q | --quiet] [-r | --right] [STRING ...]\n\nstring split splits each STRING on the separator SEP, which can be an\nempty string. If -m or --max is specified, at most MAX splits are\ndone on each STRING. If -r or --right is given, splitting is\nperformed right-to-left. This is useful in combination with -m or\n--max. With -n or --no-empty, empty results are excluded from\nconsideration (e.g. hello\\n\\nworld would expand to two strings and\nnot three). Exit status: 0 if at least one split was performed, or 1\notherwise.\n\nUse -f or --fields to print out specific fields. FIELDS is a\ncomma-separated string of field numbers and/or spans. Each field is\none-indexed, and will be printed on separate lines. If a given field\ndoes not exist, then the command exits with status 1 and does not\nprint anything, unless --allow-empty is used.\n\nSee also the --delimiter option of the read command.\n\nstring split0 splits each STRING on the zero byte (NUL). Options are\nthe same as string split except that no separator is given.\n\nsplit0 has the important property that its output is not further\nsplit when used in a command substitution, allowing for the command\nsubstitution to produce elements containing newlines. This is most\nuseful when used with Unix tools that produce zero bytes, such as\nfind -print0 or sort -z. See split0 examples below.\n\n Examples\n\n > string split . example.com\n example\n com\n\n > string split -r -m1 / /usr/local/bin/fish\n /usr/local/bin\n fish\n\n > string split '' abc\n a\n b\n c\n\n > string split --allow-empty -f1,3-4,5 '' abcd\n a\n c\n d\n\n NUL Delimited Examples\n\n > # Count files in a directory, without being confused by newlines.\n > count (find . -print0 | string split0)\n 42\n\n > # Sort a list of elements which may contain newlines\n > set foo beta alpha\\ngamma\n > set foo (string join0 $foo | sort -z | string split0)\n > string escape $foo[1]\n alpha\\ngamma\n\nSUB SUBCOMMAND\nstring sub [(-s | --start) START] [(-e | --end) END] [(-l | --length) LENGTH]\n [-q | --quiet] [STRING ...]\n\nstring sub prints a substring of each string argument. The start/end\nof the substring can be specified with -s/-e or --start/--end\nfollowed by a 1-based index value. Positive index values are relative\nto the start of the string and negative index values are relative to\nthe end of the string. The default start value is 1. The length of\nthe substring can be specified with -l or --length. If the length or\nend is not specified, the substring continues to the end of each\nSTRING. Exit status: 0 if at least one substring operation was\nperformed, 1 otherwise. --length is mutually exclusive with --end.\n\n Examples\n\n > string sub --length 2 abcde\n ab\n\n > string sub -s 2 -l 2 abcde\n bc\n\n > string sub --start=-2 abcde\n de\n\n > string sub --end=3 abcde\n abc\n\n > string sub -e -1 abcde\n abcd\n\n > string sub -s 2 -e -1 abcde\n bcd\n\n > string sub -s -3 -e -2 abcde\n c\n\nTRIM SUBCOMMAND\nstring trim [-l | --left] [-r | --right] [(-c | --chars) CHARS]\n [-q | --quiet] [STRING ...]\n\nstring trim removes leading and trailing whitespace from each STRING.\nIf -l or --left is given, only leading whitespace is removed. If -r\nor --right is given, only trailing whitespace is trimmed. The -c or\n--chars switch causes the characters in CHARS to be removed instead\nof whitespace. Exit status: 0 if at least one character was trimmed,\nor 1 otherwise.\n\n Examples\n\n > string trim ' abc '\n abc\n\n > string trim --right --chars=yz xyzzy zany\n x\n zan\n\nUPPER SUBCOMMAND\nstring upper [-q | --quiet] [STRING ...]\n\nstring upper converts each string argument to uppercase. Exit status:\n0 if at least one string was converted to uppercase, else 1. This\nmeans that in conjunction with the -q flag you can readily test\nwhether a string is already uppercase.\n\nREGULAR EXPRESSIONS\nBoth the match and replace subcommand support regular expressions\nwhen used with the -r or --regex option. The dialect is that of\nPCRE2.\n\nIn general, special characters are special by default, so a+ matches\none or more \"a\"s, while a\\+ matches an \"a\" and then a \"+\". (a+)\nmatches one or more \"a\"s in a capturing group ((?:XXXX) denotes a\nnon-capturing group). For the replacement parameter of replace, $n\nrefers to the n-th group of the match. In the match parameter, \\n\n(e.g. \\1) refers back to groups.\n\nSome features include repetitions:\n\n• * refers to 0 or more repetitions of the previous expression\n\n• + 1 or more\n\n• ? 0 or 1.\n\n• {n} to exactly n (where n is a number)\n\n• {n,m} at least n, no more than m.\n\n• {n,} n or more\n\nCharacter classes, some of the more important:\n\n• . any character except newline\n\n• \\d a decimal digit and \\D, not a decimal digit\n\n• \\s whitespace and \\S, not whitespace\n\n• \\w a \"word\" character and \\W, a \"non-word\" character\n\n• [...] (where \"...\" is some characters) is a character set\n\n• [^...] is the inverse of the given character set\n\n• [x-y] is the range of characters from x-y\n\n• [[:xxx:]] is a named character set\n\n• [[:^xxx:]] is the inverse of a named character set\n\n• [[:alnum:]] : \"alphanumeric\"\n\n• [[:alpha:]] : \"alphabetic\"\n\n• [[:ascii:]] : \"0-127\"\n\n• [[:blank:]] : \"space or tab\"\n\n• [[:cntrl:]] : \"control character\"\n\n• [[:digit:]] : \"decimal digit\"\n\n• [[:graph:]] : \"printing, excluding space\"\n\n• [[:lower:]] : \"lower case letter\"\n\n• [[:print:]] : \"printing, including space\"\n\n• [[:punct:]] : \"printing, excluding alphanumeric\"\n\n• [[:space:]] : \"white space\"\n\n• [[:upper:]] : \"upper case letter\"\n\n• [[:word:]] : \"same as w\"\n\n• [[:xdigit:]] : \"hexadecimal digit\"\n\nGroups:\n\n• (...) is a capturing group\n\n• (?:...) is a non-capturing group\n\n• \\n is a backreference (where n is the number of the group, starting\n with 1)\n\n• $n is a reference from the replacement expression to a group in the\n match expression.\n\nAnd some other things:\n\n• \\b denotes a word boundary, \\B is not a word boundary.\n\n• ^ is the start of the string or line, $ the end.\n\n• | is \"alternation\", i.e. the \"or\".\n\nCOMPARISON TO OTHER TOOLS\nMost operations string supports can also be done by external tools.\nSome of these include grep, sed and cut.\n\nIf you are familiar with these, it is useful to know how string\ndiffers from them.\n\nIn contrast to these classics, string reads input either from stdin\nor as arguments. string also does not deal with files, so it requires\nredirections to be used with them.\n\nIn contrast to grep, string's match defaults to glob-mode, while\nreplace defaults to literal matching. If set to regex-mode, they use\nPCRE regular expressions, which is comparable to grep's -P option.\nmatch defaults to printing just the match, which is like grep with -o\n(use --entire to enable grep-like behavior).\n\nLike sed's s/old/new/ command, string replace still prints strings\nthat don't match. sed's -n in combination with a /p modifier or\ncommand is like string replace -f.\n\nstring split somedelimiter is a replacement for tr somedelimiter \\n.", + "args": "string collect [-a | --allow-empty] [-N | --no-trim-newlines] [STRING ...]\nstring escape [-n | --no-quoted] [--style=] [STRING ...]\nstring join [-q | --quiet] [-n | --no-empty] SEP [STRING ...]\nstring join0 [-q | --quiet] [STRING ...]\nstring length [-q | --quiet] [STRING ...]\nstring lower [-q | --quiet] [STRING ...]\nstring match [-a | --all] [-e | --entire] [-i | --ignore-case]\n [-g | --groups-only] [-r | --regex] [-n | --index]\n [-q | --quiet] [-v | --invert]\n PATTERN [STRING ...]\nstring pad [-r | --right] [(-c | --char) CHAR] [(-w | --width) INTEGER]\n [STRING ...]\nstring repeat [(-n | --count) COUNT] [(-m | --max) MAX] [-N | --no-newline]\n [-q | --quiet] [STRING ...]\nstring replace [-a | --all] [-f | --filter] [-i | --ignore-case]\n [-r | --regex] [-q | --quiet] PATTERN REPLACE [STRING ...]\nstring shorten [(-c | --char) CHARS] [(-m | --max) INTEGER]\n [-N | --no-newline] [-l | --left] [-q | --quiet] [STRING ...]\nstring split [(-f | --fields) FIELDS] [(-m | --max) MAX] [-n | --no-empty]\n [-q | --quiet] [-r | --right] SEP [STRING ...]\nstring split0 [(-f | --fields) FIELDS] [(-m | --max) MAX] [-n | --no-empty]\n [-q | --quiet] [-r | --right] [STRING ...]\nstring sub [(-s | --start) START] [(-e | --end) END] [(-l | --length) LENGTH]\n [-q | --quiet] [STRING ...]\nstring trim [-l | --left] [-r | --right] [(-c | --chars) CHARS]\n [-q | --quiet] [STRING ...]\nstring unescape [--style=] [STRING ...]\nstring upper [-q | --quiet] [STRING ...]" + }, + "switch": { + "shortDescription": "conditionally execute a block of commands", + "description": "switch performs one of several blocks of commands, depending on\nwhether a specified value equals one of several globbed values. case\nis used together with the switch statement in order to determine\nwhich block should be executed.\n\nEach case command is given one or more parameters. The first case\ncommand with a parameter that matches the string specified in the\nswitch command will be evaluated. case parameters may contain globs.\nThese need to be escaped or quoted in order to avoid regular glob\nexpansion using filenames.\n\nNote that fish does not fall through on case statements. Only the\nfirst matching case is executed.\n\nNote that break cannot be used to exit a case/switch block early like\nin other languages. It can only be used in loops.\n\nNote that command substitutions in a case statement will be evaluated\neven if its body is not taken. All substitutions, including command\nsubstitutions, must be performed before the value can be compared\nagainst the parameter.\n\nEXAMPLE\nIf the variable $animal contains the name of an animal, the following\ncode would attempt to classify it:\n\n switch $animal\n case cat\n echo evil\n case wolf dog human moose dolphin whale\n echo mammal\n case duck goose albatross\n echo bird\n case shark trout stingray\n echo fish\n case '*'\n echo I have no idea what a $animal is\n end\n\nIf the above code was run with $animal set to whale, the output would\nbe mammal.", + "args": "switch VALUE; [case [GLOB ...]; [COMMANDS ...]; ...] end" + }, + "test": { + "shortDescription": "Evaluate conditional expressions", + "description": "The `test` command evaluates conditional expressions and sets the exit status to 0 if the expression is true, and 1 if it is false. It supports various operators to evaluate expressions related to strings, numbers, and file attributes.", + "args": "EXPRESSION" + }, + "time": { + "shortDescription": "measure how long a command or block takes", + "description": "NOTE: This page documents the fish keyword time. To see the\ndocumentation on the time command you might have, use command man\ntime.\n\ntime causes fish to measure how long a command takes and print the\nresults afterwards. The command can be a simple fish command or a\nblock. The results can not currently be redirected.\n\nFor checking timing after a command has completed, check\n$CMD_DURATION.\n\nYour system most likely also has a time command. To use that use\nsomething like command time, as in command time sleep 10. Because\nit's not inside fish, it won't have access to fish functions and\nwon't be able to time blocks and such.\n\nHOW TO INTERPRET THE OUTPUT\nTime outputs a few different values. Let's look at an example:\n\n > time string repeat -n 10000000 y\\n | command grep y >/dev/null\n _______________________________________________________\n Executed in 805.98 millis fish external\n usr time 798.88 millis 763.88 millis 34.99 millis\n sys time 141.22 millis 40.20 millis 101.02 millis\n\nThe time after \"Executed in\" is what is known as the \"wall-clock\ntime\". It is simply a measure of how long it took from the start of\nthe command until it finished. Typically it is reasonably close to\nCMD_DURATION, except for a slight skew because the two are taken at\nslightly different times.\n\nThe other times are all measures of CPU time. That means they measure\nhow long the CPU was used in this part, and they count multiple cores\nseparately. So a program with four threads using all CPU for a second\nwill have a time of 4 seconds.\n\nThe \"usr\" time is how much CPU time was spent inside the program\nitself, the \"sys\" time is how long was spent in the kernel on behalf\nof that program.\n\nThe \"fish\" time is how much CPU was spent in fish, the \"external\"\ntime how much was spent in external commands.\n\nSo in this example, since string is a builtin, everything that string\nrepeat did is accounted to fish. Any time it spends doing syscalls\nlike write() is accounted for in the fish/sys time.\n\nAnd grep here is explicitly invoked as an external command, so its\ntimes will be counted in the \"external\" column.\n\nNote that, as in this example, the CPU times can add up to more than\nthe execution time. This is because things can be done in parallel -\ngrep can match while string repeat writes.\n\nEXAMPLE\n(for obvious reasons exact results will vary on your system)\n\n > time sleep 1s\n\n _______________________________________________________\n Executed in 1,01 secs fish external\n usr time 2,32 millis 0,00 micros 2,32 millis\n sys time 0,88 millis 877,00 micros 0,00 millis\n\n > time for i in 1 2 3; sleep 1s; end\n\n _______________________________________________________\n Executed in 3,01 secs fish external\n usr time 9,16 millis 2,94 millis 6,23 millis\n sys time 0,23 millis 0,00 millis 0,23 millis\n\nInline variable assignments need to follow the time keyword:\n\n > time a_moment=1.5m sleep $a_moment\n\n _______________________________________________________\n Executed in 90.00 secs fish external\n usr time 4.62 millis 4.62 millis 0.00 millis\n sys time 2.35 millis 0.41 millis 1.95 millis", + "args": "time COMMAND" + }, + "true": { + "shortDescription": "Return a successful result", + "description": "The `true` command always returns a successful (zero) exit status. It is often used in scripts and conditional statements where an unconditional success result is needed." + }, + "type": { + "shortDescription": "locate a command and describe its type", + "description": "With no options, type indicates how each NAME would be interpreted if\nused as a command name.\n\nThe following options are available:\n\n-a or --all\n Prints all of possible definitions of the specified names.\n\n-s or --short\n Suppresses function expansion when used with no options or\n with -a/--all.\n\n-f or --no-functions\n Suppresses function and builtin lookup.\n\n-t or --type\n Prints function, builtin, or file if NAME is a shell function,\n builtin, or disk file, respectively.\n\n-p or --path\n Prints the path to NAME if NAME resolves to an executable file\n in PATH, the path to the script containing the definition of\n the function NAME if NAME resolves to a function loaded from a\n file on disk (i.e. not interactively defined at the prompt),\n or nothing otherwise.\n\n-P or --force-path\n Returns the path to the executable file NAME, presuming NAME\n is found in the PATH environment variable, or nothing\n otherwise. --force-path explicitly resolves only the path to\n executable files in PATH, regardless of whether NAME is\n shadowed by a function or builtin with the same name.\n\n-q or --query\n Suppresses all output; this is useful when testing the exit\n status. For compatibility with old fish versions this is also\n --quiet.\n\n-h or --help\n Displays help about using this command.\n\nThe -q, -p, -t and -P flags (and their long flag aliases) are\nmutually exclusive. Only one can be specified at a time.\n\ntype returns 0 if at least one entry was found, 1 otherwise, and 2\nfor invalid options or option combinations.\n\nEXAMPLE\n\n > type fg\n fg is a builtin", + "args": "type [OPTIONS] NAME [...]" + }, + "ulimit": { + "shortDescription": "set or get resource usage limits", + "description": "ulimit sets or outputs the resource usage limits of the shell and any\nprocesses spawned by it. If a new limit value is omitted, the current\nvalue of the limit of the resource is printed; otherwise, the\nspecified limit is set to the new value.\n\nUse one of the following switches to specify which resource limit to\nset or report:\n\n-b or --socket-buffers\n The maximum size of socket buffers.\n\n-c or --core-size\n The maximum size of core files created. By setting this limit\n to zero, core dumps can be disabled.\n\n-d or --data-size\n The maximum size of a process' data segment.\n\n-e or --nice\n Controls the maximum nice value; on Linux, this value is\n subtracted from 20 to give the effective value.\n\n-f or --file-size\n The maximum size of files created by a process.\n\n-i or --pending-signals\n The maximum number of signals that may be queued.\n\n-l or --lock-size\n The maximum size that may be locked into memory.\n\n-m or --resident-set-size\n The maximum resident set size.\n\n-n or --file-descriptor-count\n The maximum number of open file descriptors.\n\n-q or --queue-size\n The maximum size of data in POSIX message queues.\n\n-r or --realtime-priority\n The maximum realtime scheduling priority.\n\n-s or --stack-size\n The maximum stack size.\n\n-t or --cpu-time\n The maximum amount of CPU time in seconds.\n\n-u or --process-count\n The maximum number of processes available to the current user.\n\n-w or --swap-size\n The maximum swap space available to the current user.\n\n-v or --virtual-memory-size\n The maximum amount of virtual memory available to the shell.\n\n-y or --realtime-maxtime\n The maximum contiguous realtime CPU time in microseconds.\n\n-K or --kernel-queues\n The maximum number of kqueues (kernel queues) for the current\n user.\n\n-P or --ptys\n The maximum number of pseudo-terminals for the current user.\n\n-T or --threads\n The maximum number of simultaneous threads for the current\n user.\n\nNote that not all these limits are available in all operating\nsystems; consult the documentation for setrlimit in your operating\nsystem.\n\nThe value of limit can be a number in the unit specified for the\nresource or one of the special values hard, soft, or unlimited, which\nstand for the current hard limit, the current soft limit, and no\nlimit, respectively.\n\nIf limit is given, it is the new value of the specified resource. If\nno option is given, then -f is assumed. Values are in kilobytes,\nexcept for -t, which is in seconds and -n and -u, which are unscaled\nvalues. The exit status is 0 unless an invalid option or argument is\nsupplied, or an error occurs while setting a new limit.\n\nulimit also accepts the following options that determine what type of\nlimit to set:\n\n-H or --hard\n Sets hard resource limit.\n\n-S or --soft\n Sets soft resource limit.\n\nA hard limit can only be decreased. Once it is set it cannot be\nincreased; a soft limit may be increased up to the value of the hard\nlimit. If neither -H nor -S is specified, both the soft and hard\nlimits are updated when assigning a new limit value, and the soft\nlimit is used when reporting the current value.\n\nThe following additional options are also understood by ulimit:\n\n-a or --all\n Prints all current limits.\n\n-h or --help\n Displays help about using this command.\n\nThe fish implementation of ulimit should behave identically to the\nimplementation in bash, except for these differences:\n\n• Fish ulimit supports GNU-style long options for all switches.\n\n• Fish ulimit does not support the -p option for getting the pipe\n size. The bash implementation consists of a compile-time check that\n empirically guesses this number by writing to a pipe and waiting\n for SIGPIPE. Fish does not do this because this method of\n determining pipe size is unreliable. Depending on bash version,\n there may also be further additional limits to set in bash that do\n not exist in fish.\n\n• Fish ulimit does not support getting or setting multiple limits in\n one command, except reporting all values using the -a switch.\n\nEXAMPLE\nulimit -Hs 64 sets the hard stack size limit to 64 kB.", + "args": "ulimit [OPTIONS] [LIMIT]" + }, + "wait": { + "shortDescription": "wait for jobs to complete", + "description": "wait waits for child jobs to complete.\n\nIf a PID is specified, the command waits for the job that the process\nwith that process ID belongs to.\n\nIf a PROCESS_NAME is specified, the command waits for the jobs that\nthe matched processes belong to.\n\nIf neither a pid nor a process name is specified, the command waits\nfor all background jobs.\n\nIf the -n or --any flag is provided, the command returns as soon as\nthe first job completes. If it is not provided, it returns after all\njobs complete.\n\nThe -h or --help option displays help about using this command.\n\nEXAMPLE\n\n sleep 10 &\n wait $last_pid\n\nspawns sleep in the background, and then waits until it finishes.\n\n for i in (seq 1 5); sleep 10 &; end\n wait\n\nspawns five jobs in the background, and then waits until all of them\nfinishes.\n\n for i in (seq 1 5); sleep 10 &; end\n hoge &\n wait sleep\n\nspawns five jobs and hoge in the background, and then waits until all\nsleeps finish, and doesn't wait for hoge finishing.", + "args": "wait [-n | --any] [PID | PROCESS_NAME] ..." + }, + "while": { + "shortDescription": "perform a set of commands multiple times", + "description": "while repeatedly executes CONDITION, and if the exit status is 0,\nthen executes COMMANDS.\n\nThe exit status of the while loop is the exit status of the last\niteration of the COMMANDS executed, or 0 if none were executed. (This\nmatches other shells and is POSIX-compatible.)\n\nYou can use and or or for complex conditions. Even more complex\ncontrol can be achieved with while true containing a break.\n\nThe -h or --help option displays help about using this command.\n\nEXAMPLE\n\n while test -f foo.txt; or test -f bar.txt ; echo file exists; sleep 10; end\n # outputs 'file exists' at 10 second intervals,\n # as long as the file foo.txt or bar.txt exists.", + "args": "while CONDITION; COMMANDS; end" + } +} as const; \ No newline at end of file diff --git a/extensions/terminal-suggest/src/shell/zsh.ts b/extensions/terminal-suggest/src/shell/zsh.ts index 7919a1163098..64ba4ed3d42c 100644 --- a/extensions/terminal-suggest/src/shell/zsh.ts +++ b/extensions/terminal-suggest/src/shell/zsh.ts @@ -7,10 +7,9 @@ import * as vscode from 'vscode'; import type { ICompletionResource } from '../types'; import { execHelper, getAliasesHelper } from './common'; import { type ExecOptionsWithStringEncoding } from 'node:child_process'; -import * as path from 'path'; -import * as fs from 'fs'; +import { zshBuiltinsCommandDescriptionsCache } from './zshBuiltinsCache'; -let zshBuiltinsCommandDescriptionsCache: Map | undefined; +const commandDescriptionsCache: Map | undefined = parseCache(zshBuiltinsCommandDescriptionsCache); export async function getZshGlobals(options: ExecOptionsWithStringEncoding, existingCommands?: Set): Promise<(string | ICompletionResource)[]> { return [ @@ -20,7 +19,8 @@ export async function getZshGlobals(options: ExecOptionsWithStringEncoding, exis } async function getAliases(options: ExecOptionsWithStringEncoding): Promise { - return getAliasesHelper('zsh', ['-ic', 'alias'], /^(?[a-zA-Z0-9\._:-]+)=(?['"]?)(?.+?)\k$/, options); + const args = process.platform === 'darwin' ? ['-icl', 'alias'] : ['-ic', 'alias']; + return getAliasesHelper('zsh', args, /^(?[a-zA-Z0-9\._:-]+)=(?['"]?)(?.+?)\k$/, options); } async function getBuiltins( @@ -39,29 +39,15 @@ async function getBuiltins( }); } - if (!zshBuiltinsCommandDescriptionsCache) { - const cacheFilePath = path.join(__dirname, 'zshBuiltinsCache.json'); - if (fs.existsSync(cacheFilePath)) { - try { - const cacheFileContent = fs.readFileSync(cacheFilePath, 'utf8'); - const cacheObject = JSON.parse(cacheFileContent); - zshBuiltinsCommandDescriptionsCache = new Map(Object.entries(cacheObject)); - } catch (e) { - console.error('Failed to load zsh builtins cache', e); - } - } else { - console.warn('zsh builtins cache not found'); - } - } - for (const cmd of zshBuiltinsCommandDescriptionsCache?.keys() ?? []) { + for (const cmd of commandDescriptionsCache?.keys() ?? []) { if (typeof cmd === 'string') { try { const result = getCommandDescription(cmd); completions.push({ label: { label: cmd, description: result?.description }, detail: result?.args, - documentation: result?.documentation, + documentation: new vscode.MarkdownString(result?.documentation), kind: vscode.TerminalCompletionItemKind.Method }); @@ -83,7 +69,7 @@ export function getCommandDescription(command: string): { documentation?: string if (!zshBuiltinsCommandDescriptionsCache) { return undefined; } - const result = zshBuiltinsCommandDescriptionsCache?.get(command); + const result = commandDescriptionsCache?.get(command); if (result?.shortDescription) { return { description: result.shortDescription, @@ -98,3 +84,14 @@ export function getCommandDescription(command: string): { documentation?: string }; } } + +function parseCache(cache: Object): Map | undefined { + if (!cache) { + return undefined; + } + const result = new Map(); + for (const [key, value] of Object.entries(cache)) { + result.set(key, value); + } + return result; +} diff --git a/extensions/terminal-suggest/src/shell/zshBuiltinsCache.json b/extensions/terminal-suggest/src/shell/zshBuiltinsCache.json deleted file mode 100644 index 8bb679a13618..000000000000 --- a/extensions/terminal-suggest/src/shell/zshBuiltinsCache.json +++ /dev/null @@ -1,422 +0,0 @@ -{ - "-": { - "description": "See the section `Precommand Modifiers' in zshmisc(1).", - "args": "- simple command" - }, - ".": { - "shortDescription": "Source a file", - "description": "Read commands from file and execute them in the current shell environment. If file does not contain a slash, or if PATH_DIRS is set, the shell looks in the components of $path to find the directory containing file. Files in the current directory are not read unless `.' appears somewhere in $path. If a file named `file.zwc' is found, is newer than file, and is the compiled form (created with the zcompile builtin) of file, then commands are read from that file instead of file. If any arguments arg are given, they become the positional parameters; the old positional parameters are restored when the file is done executing. However, if no arguments are given, the positional parameters remain those of the calling context, and no restoring is done. If file was not found the return status is 127; if file was found but contained a syntax error the return status is 126; else the return status is the exit status of the last command executed.", - "args": ". file [ arg ... ]" - }, - ":": { - "shortDescription": "No effect", - "description": "This command does nothing, although normal argument expansions is performed which may have effects on shell parameters. A zero exit status is returned.", - "args": ": [ arg ... ]" - }, - "alias": { - "shortDescription": "Define or view aliases", - "description": "For each name with a corresponding value, define an alias with that value. A trailing space in value causes the next word to be checked for alias expansion. If the -g flag is present, define a global alias; global aliases are expanded even if they do not occur in command position: % perldoc --help 2>&1 | grep 'built-in functions' -f Search Perl built-in functions % alias -g HG='--help 2>&1 | grep' % perldoc HG 'built-in functions' -f Search Perl built-in functions If the -s flag is present, define a suffix alias: if the command word on a command line is in the form `text.name', where text is any non-empty string, it is replaced by the text `value text.name'. Note that name is treated as a literal string, not a pattern. A trailing space in value is not special in this case. For example, alias -s ps='gv --' will cause the command `*.ps' to be expanded to `gv -- *.ps'. As alias expansion is carried out earlier than globbing, the `*.ps' will then be expanded. Suffix aliases constitute a different name space from other aliases (so in the above example it is still possible to create an alias for the command ps) and the two sets are never listed together. For each name with no value, print the value of name, if any. With no arguments, print all currently defined aliases other than suffix aliases. If the -m flag is given the arguments are taken as patterns (they should be quoted to preserve them from being interpreted as glob patterns), and the aliases matching these patterns are printed. When printing aliases and one of the -g, -r or -s flags is present, restrict the printing to global, regular or suffix aliases, respectively; a regular alias is one which is neither a global nor a suffix alias. Using `+' instead of `-', or ending the option list with a single `+', prevents the values of the aliases from being printed. If the -L flag is present, then print each alias in a manner suitable for putting in a startup script. The exit status is nonzero if a name (with no value) is given for which no alias has been defined. For more on aliases, include common problems, see the section ALIASING in zshmisc(1).", - "args": "alias [ {+|-}gmrsL ] [ name[=value] ... ]" - }, - "autoload": { - "shortDescription": "Autoload a function", - "description": "See the section `Autoloading Functions' in zshmisc(1) for full details. The fpath parameter will be searched to find the function definition when the function is first referenced. If name consists of an absolute path, the function is defined to load from the file given (searching as usual for dump files in the given location). The name of the function is the basename (non-directory part) of the file. It is normally an error if the function is not found in the given location; however, if the option -d is given, searching for the function defaults to $fpath. If a function is loaded by absolute path, any functions loaded from it that are marked for autoload without an absolute path have the load path of the parent function temporarily prepended to $fpath. If the option -r or -R is given, the function is searched for immediately and the location is recorded internally for use when the function is executed; a relative path is expanded using the value of $PWD. This protects against a change to $fpath after the call to autoload. With -r, if the function is not found, it is silently left unresolved until execution; with -R, an error message is printed and command processing aborted immediately the search fails, i.e. at the autoload command rather than at function execution.. The flag -X may be used only inside a shell function. It causes the calling function to be marked for autoloading and then immediately loaded and executed, with the current array of positional parameters as arguments. This replaces the previous definition of the function. If no function definition is found, an error is printed and the function remains undefined and marked for autoloading. If an argument is given, it is used as a directory (i.e. it does not include the name of the function) in which the function is to be found; this may be combined with the -d option to allow the function search to default to $fpath if it is not in the given location. The flag +X attempts to load each name as an autoloaded function, but does not execute it. The exit status is zero (success) if the function was not previously defined and a definition for it was found. This does not replace any existing definition of the function. The exit status is nonzero (failure) if the function was already defined or when no definition was found. In the latter case the function remains undefined and marked for autoloading. If ksh-style autoloading is enabled, the function created will contain the contents of the file plus a call to the function itself appended to it, thus giving normal ksh autoloading behaviour on the first call to the function. If the -m flag is also given each name is treated as a pattern and all functions already marked for autoload that match the pattern are loaded. With the -t flag, turn on execution tracing; with -T, turn on execution tracing only for the current function, turning it off on entry to any called functions that do not also have tracing enabled. With the -U flag, alias expansion is suppressed when the function is loaded. With the -w flag, the names are taken as names of files compiled with the zcompile builtin, and all functions defined in them are marked for autoloading. The flags -z and -k mark the function to be autoloaded using the zsh or ksh style, as if the option KSH_AUTOLOAD were unset or were set, respectively. The flags override the setting of the option at the time the function is loaded. Note that the autoload command makes no attempt to ensure the shell options set during the loading or execution of the file have any particular value. For this, the emulate command can be used: emulate zsh -c 'autoload -Uz func' arranges that when func is loaded the shell is in native zsh emulation, and this emulation is also applied when func is run. Some of the functions of autoload are also provided by functions -u or functions -U, but autoload is a more comprehensive interface.", - "args": "autoload [ {+|-}RTUXdkmrtWz ] [ -w ] [ name ... ]" - }, - "job": { - "description": "Bring each specified job in turn to the foreground. If no job is specified, resume the current job.", - "args": "job ..." - }, - "bindkey": { - "shortDescription": "Manipulate keymap names", - "description": "See the section `Zle Builtins' in zshzle(1).", - "args": "bindkey undefined" - }, - "break": { - "shortDescription": "Exit from a loop", - "description": "Exit from an enclosing for, while, until, select or repeat loop. If an arithmetic expression n is specified, then break n levels instead of just one.", - "args": "break [ n ]" - }, - "builtin": { - "shortDescription": "Executes a builtin", - "description": "Executes the builtin name, with the given args.", - "args": "builtin name [ args ... ]" - }, - "cd": { - "description": "Change the current directory. In the first form, change the current directory to arg, or to the value of $HOME if arg is not specified. If arg is `-', change to the previous directory. Otherwise, if arg begins with a slash, attempt to change to the directory given by arg. If arg does not begin with a slash, the behaviour depends on whether the current directory `.' occurs in the list of directories contained in the shell parameter cdpath. If it does not, first attempt to change to the directory arg under the current directory, and if that fails but cdpath is set and contains at least one element attempt to change to the directory arg under each component of cdpath in turn until successful. If `.' occurs in cdpath, then cdpath is searched strictly in order so that `.' is only tried at the appropriate point. The order of testing cdpath is modified if the option POSIX_CD is set, as described in the documentation for the option. If no directory is found, the option CDABLE_VARS is set, and a parameter named arg exists whose value begins with a slash, treat its value as the directory. In that case, the parameter is added to the named directory hash table. The second form of cd substitutes the string new for the string old in the name of the current directory, and tries to change to this new directory. The third form of cd extracts an entry from the directory stack, and changes to that directory. An argument of the form `+n' identifies a stack entry by counting from the left of the list shown by the dirs command, starting with zero. An argument of the form `-n' counts from the right. If the PUSHD_MINUS option is set, the meanings of `+' and `-' in this context are swapped. If the POSIX_CD option is set, this form of cd is not recognised and will be interpreted as the first form. If the -q (quiet) option is specified, the hook function chpwd and the functions in the array chpwd_functions are not called. This is useful for calls to cd that do not change the environment seen by an interactive user. If the -s option is specified, cd refuses to change the current directory if the given pathname contains symlinks. If the -P option is given or the CHASE_LINKS option is set, symbolic links are resolved to their true values. If the -L option is given symbolic links are retained in the directory (and not resolved) regardless of the state of the CHASE_LINKS option.", - "args": "cd [ -qsLP ] {+|-}n" - }, - "command": { - "description": "The simple command argument is taken as an external command instead of a function or builtin and is executed. If the POSIX_BUILTINS option is set, builtins will also be executed but certain special properties of them are suppressed. The -p flag causes a default path to be searched instead of that in $path. With the -v flag, command is similar to whence and with -V, it is equivalent to whence -v. See also the section `Precommand Modifiers' in zshmisc(1).", - "args": "command [ -pvV ] simple command" - }, - "comparguments": { - "shortDescription": "Complete arguments", - "description": "See the section `The zsh/computil Module' in zshmodules(1).", - "args": "comparguments undefined" - }, - "compcall": { - "shortDescription": "Complete call", - "description": "See the section `The zsh/compctl Module' in zshmodules(1).", - "args": "compcall undefined" - }, - "compctl": { - "shortDescription": "Complete control", - "description": "See the section `The zsh/compctl Module' in zshmodules(1).", - "args": "compctl undefined" - }, - "compdescribe": { - "shortDescription": "Complete describe", - "description": "See the section `The zsh/computil Module' in zshmodules(1).", - "args": "compdescribe undefined" - }, - "compfiles": { - "shortDescription": "Complete files", - "description": "See the section `The zsh/computil Module' in zshmodules(1).", - "args": "compfiles undefined" - }, - "compgroups": { - "shortDescription": "Complete groups", - "description": "See the section `The zsh/computil Module' in zshmodules(1).", - "args": "compgroups undefined" - }, - "compquote": { - "shortDescription": "Complete quote", - "description": "See the section `The zsh/computil Module' in zshmodules(1).", - "args": "compquote undefined" - }, - "comptags": { - "shortDescription": "Complete tags", - "description": "See the section `The zsh/computil Module' in zshmodules(1).", - "args": "comptags undefined" - }, - "comptry": { - "shortDescription": "Complete try", - "description": "See the section `The zsh/computil Module' in zshmodules(1).", - "args": "comptry undefined" - }, - "compvalues": { - "shortDescription": "Complete values", - "description": "See the section `The zsh/computil Module' in zshmodules(1).", - "args": "compvalues undefined" - }, - "continue": { - "shortDescription": "Resume the next loop iteration", - "description": "Resume the next iteration of the enclosing for, while, until, select or repeat loop. If an arithmetic expression n is specified, break out of n-1 loops and resume at the nth enclosing loop.", - "args": "continue [ n ]" - }, - "declare": { - "shortDescription": "Set or display parameter attributes/values", - "description": "Same as typeset.", - "args": "declare undefined" - }, - "dirs": { - "shortDescription": "Interact with directory stack", - "description": "With no arguments, print the contents of the directory stack. Directories are added to this stack with the pushd command, and removed with the cd or popd commands. If arguments are specified, load them onto the directory stack, replacing anything that was there, and push the current directory onto the stack. -c clear the directory stack. -l print directory names in full instead of using of using ~ expressions (see Dynamic and Static named directories in zshexpn(1)). -p print directory entries one per line. -v number the directories in the stack when printing.", - "args": "dirs [ -lpv ]" - }, - "disable": { - "shortDescription": "Disable shell features", - "description": "Temporarily disable the named hash table elements or patterns. The default is to disable builtin commands. This allows you to use an external command with the same name as a builtin command. The -a option causes disable to act on regular or global aliases. The -s option causes disable to act on suffix aliases. The -f option causes disable to act on shell functions. The -r options causes disable to act on reserved words. Without arguments all disabled hash table elements from the corresponding hash table are printed. With the -m flag the arguments are taken as patterns (which should be quoted to prevent them from undergoing filename expansion), and all hash table elements from the corresponding hash table matching these patterns are disabled. Disabled objects can be enabled with the enable command. With the option -p, name ... refer to elements of the shell's pattern syntax as described in the section `Filename Generation'. Certain elements can be disabled separately, as given below. Note that patterns not allowed by the current settings for the options EXTENDED_GLOB, KSH_GLOB and SH_GLOB are never enabled, regardless of the setting here. For example, if EXTENDED_GLOB is not active, the pattern ^ is ineffective even if `disable -p \"^\"' has not been issued. The list below indicates any option settings that restrict the use of the pattern. It should be noted that setting SH_GLOB has a wider effect than merely disabling patterns as certain expressions, in particular those involving parentheses, are parsed differently. The following patterns may be disabled; all the strings need quoting on the command line to prevent them from being interpreted immediately as patterns and the patterns are shown below in single quotes as a reminder. '?' The pattern character ? wherever it occurs, including when preceding a parenthesis with KSH_GLOB. '*' The pattern character * wherever it occurs, including recursive globbing and when preceding a parenthesis with KSH_GLOB. '[' Character classes. '<' (NO_SH_GLOB) Numeric ranges. '|' (NO_SH_GLOB) Alternation in grouped patterns, case statements, or KSH_GLOB parenthesised expressions. '(' (NO_SH_GLOB) Grouping using single parentheses. Disabling this does not disable the use of parentheses for KSH_GLOB where they are introduced by a special character, nor for glob qualifiers (use `setopt NO_BARE_GLOB_QUAL' to disable glob qualifiers that use parentheses only). '~' (EXTENDED_GLOB) Exclusion in the form A~B. '^' (EXTENDED_GLOB) Exclusion in the form A^B. '#' (EXTENDED_GLOB) The pattern character # wherever it occurs, both for repetition of a previous pattern and for indicating globbing flags. '?(' (KSH_GLOB) The grouping form ?(...). Note this is also disabled if '?' is disabled. '*(' (KSH_GLOB) The grouping form *(...). Note this is also disabled if '*' is disabled. '+(' (KSH_GLOB) The grouping form +(...). '!(' (KSH_GLOB) The grouping form !(...). '@(' (KSH_GLOB) The grouping form @(...).", - "args": "disable [ -afmprs ] name ..." - }, - "echo": { - "shortDescription": "Write on standard output", - "description": "Write each arg on the standard output, with a space separating each one. If the -n flag is not present, print a newline at the end. echo recognizes the following escape sequences: \\a bell character \\b backspace \\c suppress subsequent characters and final newline \\e escape \\f form feed \\n linefeed (newline) \\r carriage return \\t horizontal tab \\v vertical tab \\\\ backslash \\0NNN character code in octal \\xNN character code in hexadecimal \\uNNNN unicode character code in hexadecimal \\UNNNNNNNN unicode character code in hexadecimal The -E flag, or the BSD_ECHO option, can be used to disable these escape sequences. In the latter case, -e flag can be used to enable them. Note that for standards compliance a double dash does not terminate option processing; instead, it is printed directly. However, a single dash does terminate option processing, so the first dash, possibly following options, is not printed, but everything following it is printed as an argument. The single dash behaviour is different from other shells. For a more portable way of printing text, see printf, and for a more controllable way of printing text within zsh, see print.", - "args": "echo [ -neE ] [ arg ... ]" - }, - "emulate": { - "description": "Without any argument print current emulation mode. With single argument set up zsh options to emulate the specified shell as much as possible. csh will never be fully emulated. If the argument is not one of the shells listed above, zsh will be used as a default; more precisely, the tests performed on the argument are the same as those used to determine the emulation at startup based on the shell name, see the section COMPATIBILITY in zsh(1) . In addition to setting shell options, the command also restores the pristine state of pattern enables, as if all patterns had been enabled using enable -p. If the emulate command occurs inside a function that has been marked for execution tracing with functions -t then the xtrace option will be turned on regardless of emulation mode or other options. Note that code executed inside the function by the ., source, or eval commands is not considered to be running directly from the function, hence does not provoke this behaviour. If the -R switch is given, all settable options are reset to their default value corresponding to the specified emulation mode, except for certain options describing the interactive environment; otherwise, only those options likely to cause portability problems in scripts and functions are altered. If the -L switch is given, the options LOCAL_OPTIONS, LOCAL_PATTERNS and LOCAL_TRAPS will be set as well, causing the effects of the emulate command and any setopt, disable -p or enable -p, and trap commands to be local to the immediately surrounding shell function, if any; normally these options are turned off in all emulation modes except ksh. The -L switch is mutually exclusive with the use of -c in flags. If there is a single argument and the -l switch is given, the options that would be set or unset (the latter indicated with the prefix `no') are listed. -l can be combined with -L or -R and the list will be modified in the appropriate way. Note the list does not depend on the current setting of options, i.e. it includes all options that may in principle change, not just those that would actually change. The flags may be any of the invocation-time flags described in the section INVOCATION in zsh(1), except that `-o EMACS' and `-o VI' may not be used. Flags such as `+r'/`+o RESTRICTED' may be prohibited in some circumstances. If -c arg appears in flags, arg is evaluated while the requested emulation is temporarily in effect. In this case the emulation mode and all options are restored to their previous values before emulate returns. The -R switch may precede the name of the shell to emulate; note this has a meaning distinct from including -R in flags. Use of -c enables `sticky' emulation mode for functions defined within the evaluated expression: the emulation mode is associated thereafter with the function so that whenever the function is executed the emulation (respecting the -R switch, if present) and all options are set (and pattern disables cleared) before entry to the function, and the state is restored after exit. If the function is called when the sticky emulation is already in effect, either within an `emulate shell -c' expression or within another function with the same sticky emulation, entry and exit from the function do not cause options to be altered (except due to standard processing such as the LOCAL_OPTIONS option). This also applies to functions marked for autoload within the sticky emulation; the appropriate set of options will be applied at the point the function is loaded as well as when it is run. For example: emulate sh -c 'fni() { setopt cshnullglob; } fno() { fni; }' fno The two functions fni and fno are defined with sticky sh emulation. fno is then executed, causing options associated with emulations to be set to their values in sh. fno then calls fni; because fni is also marked for sticky sh emulation, no option changes take place on entry to or exit from it. Hence the option cshnullglob, turned off by sh emulation, will be turned on within fni and remain on return to fno. On exit from fno, the emulation mode and all options will be restored to the state they were in before entry to the temporary emulation. The documentation above is typically sufficient for the intended purpose of executing code designed for other shells in a suitable environment. More detailed rules follow. 1. The sticky emulation environment provided by `emulate shell -c' is identical to that provided by entry to a function marked for sticky emulation as a consequence of being defined in such an environment. Hence, for example, the sticky emulation is inherited by subfunctions defined within functions with sticky emulation. 2. No change of options takes place on entry to or exit from functions that are not marked for sticky emulation, other than those that would normally take place, even if those functions are called within sticky emulation. 3. No special handling is provided for functions marked for autoload nor for functions present in wordcode created by the zcompile command. 4. The presence or absence of the -R switch to emulate corresponds to different sticky emulation modes, so for example `emulate sh -c', `emulate -R sh -c' and `emulate csh -c' are treated as three distinct sticky emulations. 5. Difference in shell options supplied in addition to the basic emulation also mean the sticky emulations are different, so for example `emulate zsh -c' and `emulate zsh -o cbases -c' are treated as distinct sticky emulations.", - "args": "emulate [ -lLR ] [ {zsh|sh|ksh|csh} [ flags ... ] ]" - }, - "enable": { - "shortDescription": "Enable shell features", - "description": "Enable the named hash table elements, presumably disabled earlier with disable. The default is to enable builtin commands. The -a option causes enable to act on regular or global aliases. The -s option causes enable to act on suffix aliases. The -f option causes enable to act on shell functions. The -r option causes enable to act on reserved words. Without arguments all enabled hash table elements from the corresponding hash table are printed. With the -m flag the arguments are taken as patterns (should be quoted) and all hash table elements from the corresponding hash table matching these patterns are enabled. Enabled objects can be disabled with the disable builtin command. enable -p reenables patterns disabled with disable -p. Note that it does not override globbing options; for example, `enable -p \"~\"' does not cause the pattern character ~ to be active unless the EXTENDED_GLOB option is also set. To enable all possible patterns (so that they may be individually disabled with disable -p), use `setopt EXTENDED_GLOB KSH_GLOB NO_SH_GLOB'.", - "args": "enable [ -afmprs ] name ..." - }, - "eval": { - "shortDescription": "Execute arguments in shell", - "description": "Read the arguments as input to the shell and execute the resulting command(s) in the current shell process. The return status is the same as if the commands had been executed directly by the shell; if there are no args or they contain no commands (i.e. are an empty string or whitespace) the return status is zero.", - "args": "eval [ arg ... ]" - }, - "exec": { - "shortDescription": "Replace shell with command", - "description": "Replace the current shell with command rather than forking. If command is a shell builtin command or a shell function, the shell executes it, and exits when the command is complete. With -c clear the environment; with -l prepend - to the argv[0] string of the command executed (to simulate a login shell); with -a argv0 set the argv[0] string of the command executed. See the section `Precommand Modifiers' in zshmisc(1). If the option POSIX_BUILTINS is set, command is never interpreted as a shell builtin command or shell function. This means further precommand modifiers such as builtin and noglob are also not interpreted within the shell. Hence command is always found by searching the command path. If command is omitted but any redirections are specified, then the redirections will take effect in the current shell.", - "args": "exec [ -cl ] [ -a argv0 ] [ command [ arg ... ] ]" - }, - "exit": { - "shortDescription": "Exit the shell", - "description": "Exit the shell with the exit status specified by an arithmetic expression n; if none is specified, use the exit status from the last command executed. An EOF condition will also cause the shell to exit, unless the IGNORE_EOF option is set. See notes at the end of the section JOBS in zshmisc(1) for some possibly unexpected interactions of the exit command with jobs.", - "args": "exit [ n ]" - }, - "export": { - "shortDescription": "Export to environment", - "description": "The specified names are marked for automatic export to the environment of subsequently executed commands. Equivalent to typeset -gx. If a parameter specified does not already exist, it is created in the global scope.", - "args": "export [ name[=value] ... ]" - }, - "false": { - "shortDescription": "Return exit status of 1", - "description": "Do nothing and return an exit status of 1.", - "args": "false [ arg ... ]" - }, - "fc": { - "shortDescription": "Fix command", - "description": "The fc command controls the interactive history mechanism. Note that reading and writing of history options is only performed if the shell is interactive. Usually this is detected automatically, but it can be forced by setting the interactive option when starting the shell. The first two forms of this command select a range of events from first to last from the history list. The arguments first and last may be specified as a number or as a string. A negative number is used as an offset to the current history event number. A string specifies the most recent event beginning with the given string. All substitutions old=new, if any, are then performed on the text of the events. The range of events selected by numbers can be narrowed further by the following flags. -I restricts to only internal events (not from $HISTFILE) -L restricts to only local events (not from other shells, see SHARE_HISTORY in zshoptions(1) -- note that $HISTFILE is considered local when read at startup) -m takes the first argument as a pattern (which should be quoted) and only the history events matching this pattern are considered If first is not specified, it will be set to -1 (the most recent event), or to -16 if the -l flag is given. If last is not specified, it will be set to first, or to -1 if the -l flag is given. However, if the current event has added entries to the history with `print -s' or `fc -R', then the default last for -l includes all new history entries since the current event began. When the -l flag is given, the resulting events are listed on standard output. Otherwise the editor program specified by -e ename is invoked on a file containing these history events. If -e is not given, the value of the parameter FCEDIT is used; if that is not set the value of the parameter EDITOR is used; if that is not set a builtin default, usually `vi' is used. If ename is `-', no editor is invoked. When editing is complete, the edited command is executed. The flag `-s' is equivalent to `-e -'. The flag -r reverses the order of the events and the flag -n suppresses event numbers when listing. Also when listing, -d prints timestamps for each event -f prints full time-date stamps in the US `MM/DD/YY hh:mm' format -E prints full time-date stamps in the European `dd.mm.yyyy hh:mm' format -i prints full time-date stamps in ISO8601 `yyyy-mm-dd hh:mm' format -t fmt prints time and date stamps in the given format; fmt is formatted with the strftime function with the zsh extensions described for the %D{string} prompt format in the section EXPANSION OF PROMPT SEQUENCES in zshmisc(1). The resulting formatted string must be no more than 256 characters or will not be printed -D prints elapsed times; may be combined with one of the options above `fc -p' pushes the current history list onto a stack and switches to a new history list. If the -a option is also specified, this history list will be automatically popped when the current function scope is exited, which is a much better solution than creating a trap function to call `fc -P' manually. If no arguments are specified, the history list is left empty, $HISTFILE is unset, and $HISTSIZE & $SAVEHIST are set to their default values. If one argument is given, $HISTFILE is set to that filename, $HISTSIZE & $SAVEHIST are left unchanged, and the history file is read in (if it exists) to initialize the new list. If a second argument is specified, $HISTSIZE & $SAVEHIST are instead set to the single specified numeric value. Finally, if a third argument is specified, $SAVEHIST is set to a separate value from $HISTSIZE. You are free to change these environment values for the new history list however you desire in order to manipulate the new history list. `fc -P' pops the history list back to an older list saved by `fc -p'. The current list is saved to its $HISTFILE before it is destroyed (assuming that $HISTFILE and $SAVEHIST are set appropriately, of course). The values of $HISTFILE, $HISTSIZE, and $SAVEHIST are restored to the values they had when `fc -p' was called. Note that this restoration can conflict with making these variables \"local\", so your best bet is to avoid local declarations for these variables in functions that use `fc -p'. The one other guaranteed-safe combination is declaring these variables to be local at the top of your function and using the automatic option (-a) with `fc -p'. Finally, note that it is legal to manually pop a push marked for automatic popping if you need to do so before the function exits. `fc -R' reads the history from the given file, `fc -W' writes the history out to the given file, and `fc -A' appends the history out to the given file. If no filename is specified, the $HISTFILE is assumed. If the -I option is added to -R, only those events that are not already contained within the internal history list are added. If the -I option is added to -A or -W, only those events that are new since last incremental append/write to the history file are appended/written. In any case, the created file will have no more than $SAVEHIST entries.", - "args": "fc -ARWI [ filename ]" - }, - "float": { - "shortDescription": "Floating point arithmetic", - "description": "Equivalent to typeset -E, except that options irrelevant to floating point numbers are not permitted.", - "args": "float [ {+|-}Hghlprtux ] [ {+|-}EFLRZ [ n ] ] [ name[=value] ... ]" - }, - "functions": { - "shortDescription": "List functions", - "description": "Equivalent to typeset -f, with the exception of the -c, -x, -M and -W options. For functions -u and functions -U, see autoload, which provides additional options. For functions -t and functions -T, see typeset -f. The -x option indicates that any functions output will have each leading tab for indentation, added by the shell to show syntactic structure, expanded to the given number num of spaces. num can also be 0 to suppress all indentation. The -W option turns on the option WARN_NESTED_VAR for the named function or functions only. The option is turned off at the start of nested functions (apart from anonoymous functions) unless the called function also has the -W attribute. The -c option causes oldfn to be copied to newfn. The copy is efficiently handled internally by reference counting. If oldfn was marked for autoload it is first loaded and if this fails the copy fails. Either function may subsequently be redefined without affecting the other. A typical idiom is that oldfn is the name of a library shell function which is then redefined to call newfn, thereby installing a modified version of the function. The -M and +M flags Use of the -M option may not be combined with any of the options handled by typeset -f. functions -M mathfn defines mathfn as the name of a mathematical function recognised in all forms of arithmetical expressions; see the section `Arithmetic Evaluation' in zshmisc(1). By default mathfn may take any number of comma-separated arguments. If min is given, it must have exactly min args; if min and max are both given, it must have at least min and at most max args. max may be -1 to indicate that there is no upper limit. By default the function is implemented by a shell function of the same name; if shellfn is specified it gives the name of the corresponding shell function while mathfn remains the name used in arithmetical expressions. The name of the function in $0 is mathfn (not shellfn as would usually be the case), provided the option FUNCTION_ARGZERO is in effect. The positional parameters in the shell function correspond to the arguments of the mathematical function call. The result of the last arithmetical expression evaluated inside the shell function gives the result of the mathematical function. This is not limited to arithmetic substitutions of the form $((...)), but also includes arithmetical expressions evaluated in any other way, including by the let builtin, by ((...)) statements, and even by the return builtin and by array subscripts. Therefore, care must be taken not to use syntactical constructs that perform arithmetic evaluation after evaluating what is to be the result of the function. For example: # WRONG zmath_cube() { (( $1 * $1 * $1 )) return 0 } functions -M cube 1 1 zmath_cube print $(( cube(3) )) This will print `0' because of the return. Commenting the return out would lead to a different problem: the ((...)) statement would become the last statement in the function, so the return status ($?) of the function would be non-zero (indicating failure) whenever the arithmetic result of the function would happen to be zero (numerically): # WRONG zmath_cube() { (( $1 * $1 * $1 )) } functions -M cube 1 1 zmath_cube print $(( cube(0) )) Instead, the true builtin can be used: # RIGHT zmath_cube() { (( $1 * $1 * $1 )) true } functions -M cube 1 1 zmath_cube print $(( cube(3) )) If the additional option -s is given to functions -M, the argument to the function is a single string: anything between the opening and matching closing parenthesis is passed to the function as a single argument, even if it includes commas or white space. The minimum and maximum argument specifiers must therefore be 1 if given. An empty argument list is passed as a zero-length string. Thus, the following string function takes a single argument, including the commas, and prints 11: stringfn() { (( $#1 )); true } functions -Ms stringfn print $(( stringfn(foo,bar,rod) )) functions -M with no arguments lists all such user-defined functions in the same form as a definition. With the additional option -m and a list of arguments, all functions whose mathfn matches one of the pattern arguments are listed. function +M removes the list of mathematical functions; with the additional option -m the arguments are treated as patterns and all functions whose mathfn matches the pattern are removed. Note that the shell function implementing the behaviour is not removed (regardless of whether its name coincides with mathfn).", - "args": "functions +M [ -m ] mathfn ..." - }, - "getln": { - "description": "Read the top value from the buffer stack and put it in the shell parameter name. Equivalent to read -zr.", - "args": "getln [ -AclneE ] name ..." - }, - "getopts": { - "shortDescription": "Parse positional parameters", - "description": "Checks the args for legal options. If the args are omitted, use the positional parameters. A valid option argument begins with a `+' or a `-'. An argument not beginning with a `+' or a `-', or the argument `--', ends the options. Note that a single `-' is not considered a valid option argument. optstring contains the letters that getopts recognizes. If a letter is followed by a `:', that option requires an argument. The options can be separated from the argument by blanks. Each time it is invoked, getopts places the option letter it finds in the shell parameter name, prepended with a `+' when arg begins with a `+'. The index of the next arg is stored in OPTIND. The option argument, if any, is stored in OPTARG. The first option to be examined may be changed by explicitly assigning to OPTIND. OPTIND has an initial value of 1, and is normally set to 1 upon entry to a shell function and restored upon exit. (The POSIX_BUILTINS option disables this, and also changes the way the value is calculated to match other shells.) OPTARG is not reset and retains its value from the most recent call to getopts. If either of OPTIND or OPTARG is explicitly unset, it remains unset, and the index or option argument is not stored. The option itself is still stored in name in this case. A leading `:' in optstring causes getopts to store the letter of any invalid option in OPTARG, and to set name to `?' for an unknown option and to `:' when a required argument is missing. Otherwise, getopts sets name to `?' and prints an error message when an option is invalid. The exit status is nonzero when there are no more options.", - "args": "getopts optstring name [ arg ... ]" - }, - "hash": { - "shortDescription": "Remember command locations", - "description": "hash can be used to directly modify the contents of the command hash table, and the named directory hash table. Normally one would modify these tables by modifying one's PATH (for the command hash table) or by creating appropriate shell parameters (for the named directory hash table). The choice of hash table to work on is determined by the -d option; without the option the command hash table is used, and with the option the named directory hash table is used. A command name starting with a / is never hashed, whether by explicit use of the hash command or otherwise. Such a command is always found by direct look up in the file system. Given no arguments, and neither the -r or -f options, the selected hash table will be listed in full. The -r option causes the selected hash table to be emptied. It will be subsequently rebuilt in the normal fashion. The -f option causes the selected hash table to be fully rebuilt immediately. For the command hash table this hashes all the absolute directories in the PATH, and for the named directory hash table this adds all users' home directories. These two options cannot be used with any arguments. The -m option causes the arguments to be taken as patterns (which should be quoted) and the elements of the hash table matching those patterns are printed. This is the only way to display a limited selection of hash table elements. For each name with a corresponding value, put `name' in the selected hash table, associating it with the pathname `value'. In the command hash table, this means that whenever `name' is used as a command argument, the shell will try to execute the file given by `value'. In the named directory hash table, this means that `value' may be referred to as `~name'. For each name with no corresponding value, attempt to add name to the hash table, checking what the appropriate value is in the normal manner for that hash table. If an appropriate value can't be found, then the hash table will be unchanged. The -v option causes hash table entries to be listed as they are added by explicit specification. If has no effect if used with -f. If the -L flag is present, then each hash table entry is printed in the form of a call to hash.", - "args": "hash [ -Ldfmrv ] [ name[=value] ] ..." - }, - "history": { - "shortDescription": "Command history", - "description": "Same as fc -l.", - "args": "history undefined" - }, - "integer": { - "shortDescription": "Integer arithmetic", - "description": "Equivalent to typeset -i, except that options irrelevant to integers are not permitted.", - "args": "integer [ {+|-}Hghlprtux ] [ {+|-}LRZi [ n ] ] [ name[=value] ... ]" - }, - "jobs": { - "shortDescription": "List active jobs", - "description": "Lists information about each given job, or all jobs if job is omitted. The -l flag lists process IDs, and the -p flag lists process groups. If the -r flag is specified only running jobs will be listed and if the -s flag is given only stopped jobs are shown. If the -d flag is given, the directory from which the job was started (which may not be the current directory of the job) will also be shown. The -Z option replaces the shell's argument and environment space with the given string, truncated if necessary to fit. This will normally be visible in ps (ps(1)) listings. This feature is typically used by daemons, to indicate their state. Full job control is only available in the top-level interactive shell, not in commands run in the left hand side of pipelines or within the (...) construct. However, a snapshot of the job state at that point is taken, so it is still possible to use the jobs builtin, or any parameter providing job information. This gives information about the state of jobs at the point the subshell was created. If background processes are created within the subshell, then instead information about those processes is provided. For example, sleep 10 & # Job in background ( # Shell forks jobs # Shows information about \"sleep 10 &\" sleep 5 & # Process in background (no job control) jobs # Shows information about \"sleep 5 &\" )", - "args": "jobs -Z string" - }, - "kill": { - "shortDescription": "Send a signal to a process", - "description": "Sends either SIGTERM or the specified signal to the given jobs or processes. Signals are given by number or by names, with or without the `SIG' prefix. If the signal being sent is not `KILL' or `CONT', then the job will be sent a `CONT' signal if it is stopped. The argument job can be the process ID of a job not in the job list. In the second form, kill -l, if sig is not specified the signal names are listed. Otherwise, for each sig that is a name, the corresponding signal number is listed. For each sig that is a signal number or a number representing the exit status of a process which was terminated or stopped by a signal the name of the signal is printed. On some systems, alternative signal names are allowed for a few signals. Typical examples are SIGCHLD and SIGCLD or SIGPOLL and SIGIO, assuming they correspond to the same signal number. kill -l will only list the preferred form, however kill -l alt will show if the alternative form corresponds to a signal number. For example, under Linux kill -l IO and kill -l POLL both output 29, hence kill -IO and kill -POLL have the same effect. Many systems will allow process IDs to be negative to kill a process group or zero to kill the current process group.", - "args": "kill -l [ sig ... ]" - }, - "let": { - "shortDescription": "Evaluate arithmetic expression", - "description": "Evaluate each arg as an arithmetic expression. See the section `Arithmetic Evaluation' in zshmisc(1) for a description of arithmetic expressions. The exit status is 0 if the value of the last expression is nonzero, 1 if it is zero, and 2 if an error occurred.", - "args": "let arg ..." - }, - "limit": { - "shortDescription": "Set or display resource limits", - "description": "Set or display resource limits. Unless the -s flag is given, the limit applies only the children of the shell. If -s is given without other arguments, the resource limits of the current shell is set to the previously set resource limits of the children. If limit is not specified, print the current limit placed on resource, otherwise set the limit to the specified value. If the -h flag is given, use hard limits instead of soft limits. If no resource is given, print all limits. When looping over multiple resources, the shell will abort immediately if it detects a badly formed argument. However, if it fails to set a limit for some other reason it will continue trying to set the remaining limits. resource can be one of: addressspace Maximum amount of address space used. aiomemorylocked Maximum amount of memory locked in RAM for AIO operations. aiooperations Maximum number of AIO operations. cachedthreads Maximum number of cached threads. coredumpsize Maximum size of a core dump. cputime Maximum CPU seconds per process. datasize Maximum data size (including stack) for each process. descriptors Maximum value for a file descriptor. filesize Largest single file allowed. kqueues Maximum number of kqueues allocated. maxproc Maximum number of processes. maxpthreads Maximum number of threads per process. memorylocked Maximum amount of memory locked in RAM. memoryuse Maximum resident set size. msgqueue Maximum number of bytes in POSIX message queues. posixlocks Maximum number of POSIX locks per user. pseudoterminals Maximum number of pseudo-terminals. resident Maximum resident set size. sigpending Maximum number of pending signals. sockbufsize Maximum size of all socket buffers. stacksize Maximum stack size for each process. swapsize Maximum amount of swap used. vmemorysize Maximum amount of virtual memory. Which of these resource limits are available depends on the system. resource can be abbreviated to any unambiguous prefix. It can also be an integer, which corresponds to the integer defined for the resource by the operating system. If argument corresponds to a number which is out of the range of the resources configured into the shell, the shell will try to read or write the limit anyway, and will report an error if this fails. As the shell does not store such resources internally, an attempt to set the limit will fail unless the -s option is present. limit is a number, with an optional scaling factor, as follows: nh hours nk kilobytes (default) nm megabytes or minutes ng gigabytes [mm:]ss minutes and seconds The limit command is not made available by default when the shell starts in a mode emulating another shell. It can be made available with the command `zmodload -F zsh/rlimits b:limit'.", - "args": "limit [ -hs ] [ resource [ limit ] ] ..." - }, - "local": { - "shortDescription": "Create a local variable", - "description": "Same as typeset, except that the options -g, and -f are not permitted. In this case the -x option does not force the use of -g, i.e. exported variables will be local to functions.", - "args": "local [ {+|-}AHUahlprtux ] [ {+|-}EFLRZi [ n ] ] [ name[=value] ... ]" - }, - "logout": { - "shortDescription": "Exit the shell", - "description": "Same as exit, except that it only works in a login shell.", - "args": "logout [ n ]" - }, - "noglob": { - "shortDescription": "Disable filename expansion", - "description": "See the section `Precommand Modifiers' in zshmisc(1).", - "args": "noglob simple command" - }, - "popd": { - "shortDescription": "Remove directory from stack", - "description": "Remove an entry from the directory stack, and perform a cd to the new top directory. With no argument, the current top entry is removed. An argument of the form `+n' identifies a stack entry by counting from the left of the list shown by the dirs command, starting with zero. An argument of the form -n counts from the right. If the PUSHD_MINUS option is set, the meanings of `+' and `-' in this context are swapped. If the -q (quiet) option is specified, the hook function chpwd and the functions in the array $chpwd_functions are not called, and the new directory stack is not printed. This is useful for calls to popd that do not change the environment seen by an interactive user.", - "args": "popd [ -q ] [ {+|-}n ]" - }, - "print": { - "shortDescription": "Print arguments", - "description": "With the `-f' option the arguments are printed as described by printf. With no flags or with the flag `-', the arguments are printed on the standard output as described by echo, with the following differences: the escape sequence `\\M-x' (or `\\Mx') metafies the character x (sets the highest bit), `\\C-x' (or `\\Cx') produces a control character (`\\C-@' and `\\C-?' give the characters NULL and delete), a character code in octal is represented by `\\NNN' (instead of `\\0NNN'), and `\\E' is a synonym for `\\e'. Finally, if not in an escape sequence, `\\' escapes the following character and is not printed. -a Print arguments with the column incrementing first. Only useful with the -c and -C options. -b Recognize all the escape sequences defined for the bindkey command, see the section `Zle Builtins' in zshzle(1). -c Print the arguments in columns. Unless -a is also given, arguments are printed with the row incrementing first. -C cols Print the arguments in cols columns. Unless -a is also given, arguments are printed with the row incrementing first. -D Treat the arguments as paths, replacing directory prefixes with ~ expressions corresponding to directory names, as appropriate. -i If given together with -o or -O, sorting is performed case-independently. -l Print the arguments separated by newlines instead of spaces. Note: if the list of arguments is empty, print -l will still output one empty line. To print a possibly-empty list of arguments one per line, use print -C1, as in `print -rC1 -- \"$list[@]\"'. -m Take the first argument as a pattern (should be quoted), and remove it from the argument list together with subsequent arguments that do not match this pattern. -n Do not add a newline to the output. -N Print the arguments separated and terminated by nulls. Again, print -rNC1 -- \"$list[@]\" is a canonical way to print an arbitrary list as null-delimited records. -o Print the arguments sorted in ascending order. -O Print the arguments sorted in descending order. -p Print the arguments to the input of the coprocess. -P Perform prompt expansion (see EXPANSION OF PROMPT SEQUENCES in zshmisc(1)). In combination with `-f', prompt escape sequences are parsed only within interpolated arguments, not within the format string. -r Ignore the escape conventions of echo. -R Emulate the BSD echo command, which does not process escape sequences unless the -e flag is given. The -n flag suppresses the trailing newline. Only the -e and -n flags are recognized after -R; all other arguments and options are printed. -s Place the results in the history list instead of on the standard output. Each argument to the print command is treated as a single word in the history, regardless of its content. -S Place the results in the history list instead of on the standard output. In this case only a single argument is allowed; it will be split into words as if it were a full shell command line. The effect is similar to reading the line from a history file with the HIST_LEX_WORDS option active. -u n Print the arguments to file descriptor n. -v name Store the printed arguments as the value of the parameter name. -x tab-stop Expand leading tabs on each line of output in the printed string assuming a tab stop every tab-stop characters. This is appropriate for formatting code that may be indented with tabs. Note that leading tabs of any argument to print, not just the first, are expanded, even if print is using spaces to separate arguments (the column count is maintained across arguments but may be incorrect on output owing to previous unexpanded tabs). The start of the output of each print command is assumed to be aligned with a tab stop. Widths of multibyte characters are handled if the option MULTIBYTE is in effect. This option is ignored if other formatting options are in effect, namely column alignment or printf style, or if output is to a special location such as shell history or the command line editor. -X tab-stop This is similar to -x, except that all tabs in the printed string are expanded. This is appropriate if tabs in the arguments are being used to produce a table format. -z Push the arguments onto the editing buffer stack, separated by spaces. If any of `-m', `-o' or `-O' are used in combination with `-f' and there are no arguments (after the removal process in the case of `-m') then nothing is printed.", - "args": "print [ -abcDilmnNoOpPrsSz ] [ -u n ] [ -f format ] [ -C cols ]" - }, - "printf": { - "shortDescription": "Format and print data", - "description": "Print the arguments according to the format specification. Formatting rules are the same as used in C. The same escape sequences as for echo are recognised in the format. All C conversion specifications ending in one of csdiouxXeEfgGn are handled. In addition to this, `%b' can be used instead of `%s' to cause escape sequences in the argument to be recognised and `%q' can be used to quote the argument in such a way that allows it to be reused as shell input. With the numeric format specifiers, if the corresponding argument starts with a quote character, the numeric value of the following character is used as the number to print; otherwise the argument is evaluated as an arithmetic expression. See the section `Arithmetic Evaluation' in zshmisc(1) for a description of arithmetic expressions. With `%n', the corresponding argument is taken as an identifier which is created as an integer parameter. Normally, conversion specifications are applied to each argument in order but they can explicitly specify the nth argument is to be used by replacing `%' by `%n$' and `*' by `*n$'. It is recommended that you do not mix references of this explicit style with the normal style and the handling of such mixed styles may be subject to future change. If arguments remain unused after formatting, the format string is reused until all arguments have been consumed. With the print builtin, this can be suppressed by using the -r option. If more arguments are required by the format than have been specified, the behaviour is as if zero or an empty string had been specified as the argument. The -v option causes the output to be stored as the value of the parameter name, instead of printed. If name is an array and the format string is reused when consuming arguments then one array element will be used for each use of the format string.", - "args": "printf [ -v name ] format [ arg ... ]" - }, - "pushd": { - "shortDescription": "Add directory to stack", - "description": "Change the current directory, and push the old current directory onto the directory stack. In the first form, change the current directory to arg. If arg is not specified, change to the second directory on the stack (that is, exchange the top two entries), or change to $HOME if the PUSHD_TO_HOME option is set or if there is only one entry on the stack. Otherwise, arg is interpreted as it would be by cd. The meaning of old and new in the second form is also the same as for cd. The third form of pushd changes directory by rotating the directory list. An argument of the form `+n' identifies a stack entry by counting from the left of the list shown by the dirs command, starting with zero. An argument of the form `-n' counts from the right. If the PUSHD_MINUS option is set, the meanings of `+' and `-' in this context are swapped. If the -q (quiet) option is specified, the hook function chpwd and the functions in the array $chpwd_functions are not called, and the new directory stack is not printed. This is useful for calls to pushd that do not change the environment seen by an interactive user. If the option -q is not specified and the shell option PUSHD_SILENT is not set, the directory stack will be printed after a pushd is performed. The options -s, -L and -P have the same meanings as for the cd builtin.", - "args": "pushd [ -qsLP ] {+|-}n" - }, - "pushln": { - "shortDescription": "Push arguments onto the buffer", - "description": "Equivalent to print -nz.", - "args": "pushln [ arg ... ]" - }, - "pwd": { - "shortDescription": "Print working directory", - "description": "Print the absolute pathname of the current working directory. If the -r or the -P flag is specified, or the CHASE_LINKS option is set and the -L flag is not given, the printed path will not contain symbolic links.", - "args": "pwd [ -rLP ]" - }, - "read": { - "description": "Read one line and break it into fields using the characters in $IFS as separators, except as noted below. The first field is assigned to the first name, the second field to the second name, etc., with leftover fields assigned to the last name. If name is omitted then REPLY is used for scalars and reply for arrays. -r Raw mode: a `\\' at the end of a line does not signify line continuation and backslashes in the line don't quote the following character and are not removed. -s Don't echo back characters if reading from the terminal. -q Read only one character from the terminal and set name to `y' if this character was `y' or `Y' and to `n' otherwise. With this flag set the return status is zero only if the character was `y' or `Y'. This option may be used with a timeout (see -t); if the read times out, or encounters end of file, status 2 is returned. Input is read from the terminal unless one of -u or -p is present. This option may also be used within zle widgets. -k [ num ] Read only one (or num) characters. All are assigned to the first name, without word splitting. This flag is ignored when -q is present. Input is read from the terminal unless one of -u or -p is present. This option may also be used within zle widgets. Note that despite the mnemonic `key' this option does read full characters, which may consist of multiple bytes if the option MULTIBYTE is set. -z Read one entry from the editor buffer stack and assign it to the first name, without word splitting. Text is pushed onto the stack with `print -z' or with push-line from the line editor (see zshzle(1)). This flag is ignored when the -k or -q flags are present. -e -E The input read is printed (echoed) to the standard output. If the -e flag is used, no input is assigned to the parameters. -A The first name is taken as the name of an array and all words are assigned to it. -c -l These flags are allowed only if called inside a function used for completion (specified with the -K flag to compctl). If the -c flag is given, the words of the current command are read. If the -l flag is given, the whole line is assigned as a scalar. If both flags are present, -l is used and -c is ignored. -n Together with -c, the number of the word the cursor is on is read. With -l, the index of the character the cursor is on is read. Note that the command name is word number 1, not word 0, and that when the cursor is at the end of the line, its character index is the length of the line plus one. -u n Input is read from file descriptor n. -p Input is read from the coprocess. -d delim Input is terminated by the first character of delim instead of by newline. -t [ num ] Test if input is available before attempting to read. If num is present, it must begin with a digit and will be evaluated to give a number of seconds, which may be a floating point number; in this case the read times out if input is not available within this time. If num is not present, it is taken to be zero, so that read returns immediately if no input is available. If no input is available, return status 1 and do not set any variables. This option is not available when reading from the editor buffer with -z, when called from within completion with -c or -l, with -q which clears the input queue before reading, or within zle where other mechanisms should be used to test for input. Note that read does not attempt to alter the input processing mode. The default mode is canonical input, in which an entire line is read at a time, so usually `read -t' will not read anything until an entire line has been typed. However, when reading from the terminal with -k input is processed one key at a time; in this case, only availability of the first character is tested, so that e.g. `read -t -k 2' can still block on the second character. Use two instances of `read -t -k' if this is not what is wanted. If the first argument contains a `?', the remainder of this word is used as a prompt on standard error when the shell is interactive. The value (exit status) of read is 1 when an end-of-file is encountered, or when -c or -l is present and the command is not called from a compctl function, or as described for -q. Otherwise the value is 0. The behavior of some combinations of the -k, -p, -q, -u and -z flags is undefined. Presently -q cancels all the others, -p cancels -u, -k cancels -z, and otherwise -z cancels both -p and -u. The -c or -l flags cancel any and all of -kpquz.", - "args": "read [ -rszpqAclneE ] [ -t [ num ] ] [ -k [ num ] ] [ -d delim ]" - }, - "readonly": { - "shortDescription": "Mark variables as read-only", - "description": "Same as typeset -r. With the POSIX_BUILTINS option set, same as typeset -gr.", - "args": "readonly undefined" - }, - "return": { - "description": "Causes a shell function or `.' script to return to the invoking script with the return status specified by an arithmetic expression n. For example, the following prints `42': () { integer foo=40; return \"foo + 2\" } echo $? If n is omitted, the return status is that of the last command executed. If return was executed from a trap in a TRAPNAL function, the effect is different for zero and non-zero return status. With zero status (or after an implicit return at the end of the trap), the shell will return to whatever it was previously processing; with a non-zero status, the shell will behave as interrupted except that the return status of the trap is retained. Note that the numeric value of the signal which caused the trap is passed as the first argument, so the statement `return \"128+$1\"' will return the same status as if the signal had not been trapped.", - "args": "return [ n ]" - }, - "set": { - "description": "Set the options for the shell and/or set the positional parameters, or declare and set an array. If the -s option is given, it causes the specified arguments to be sorted before assigning them to the positional parameters (or to the array name if -A is used). With +s sort arguments in descending order. For the meaning of the other flags, see zshoptions(1). Flags may be specified by name using the -o option. If no option name is supplied with -o, the current option states are printed: see the description of setopt below for more information on the format. With +o they are printed in a form that can be used as input to the shell. If the -A flag is specified, name is set to an array containing the given args; if no name is specified, all arrays are printed together with their values. If +A is used and name is an array, the given arguments will replace the initial elements of that array; if no name is specified, all arrays are printed without their values. The behaviour of arguments after -A name or +A name depends on whether the option KSH_ARRAYS is set. If it is not set, all arguments following name are treated as values for the array, regardless of their form. If the option is set, normal option processing continues at that point; only regular arguments are treated as values for the array. This means that set -A array -x -- foo sets array to `-x -- foo' if KSH_ARRAYS is not set, but sets the array to foo and turns on the option `-x' if it is set. If the -A flag is not present, but there are arguments beyond the options, the positional parameters are set. If the option list (if any) is terminated by `--', and there are no further arguments, the positional parameters will be unset. If no arguments and no `--' are given, then the names and values of all parameters are printed on the standard output. If the only argument is `+', the names of all parameters are printed. For historical reasons, `set -' is treated as `set +xv' and `set - args' as `set +xv -- args' when in any other emulation mode than zsh's native mode.", - "args": "set [ {+|-}options | {+|-}o [ option_name ] ] ... [ {+|-}A [ name ] ]" - }, - "setopt": { - "description": "Set the options for the shell. All options specified either with flags or by name are set. If no arguments are supplied, the names of all options currently set are printed. The form is chosen so as to minimize the differences from the default options for the current emulation (the default emulation being native zsh, shown as in zshoptions(1)). Options that are on by default for the emulation are shown with the prefix no only if they are off, while other options are shown without the prefix no and only if they are on. In addition to options changed from the default state by the user, any options activated automatically by the shell (for example, SHIN_STDIN or INTERACTIVE) will be shown in the list. The format is further modified by the option KSH_OPTION_PRINT, however the rationale for choosing options with or without the no prefix remains the same in this case. If the -m flag is given the arguments are taken as patterns (which should be quoted to protect them from filename expansion), and all options with names matching these patterns are set. Note that a bad option name does not cause execution of subsequent shell code to be aborted; this is behaviour is different from that of `set -o'. This is because set is regarded as a special builtin by the POSIX standard, but setopt is not.", - "args": "setopt [ {+|-}options | {+|-}o option_name ] [ -m ] [ name ... ]" - }, - "shift": { - "shortDescription": "Shift positional parameters", - "description": "The positional parameters ${n+1} ... are renamed to $1 ..., where n is an arithmetic expression that defaults to 1. If any names are given then the arrays with these names are shifted instead of the positional parameters. If the option -p is given arguments are instead removed (popped) from the end rather than the start of the array.", - "args": "shift [ -p ] [ n ] [ name ... ]" - }, - "source": { - "shortDescription": "Source a file", - "description": "Same as `.', except that the current directory is always searched and is always searched first, before directories in $path.", - "args": "source file [ arg ... ]" - }, - "suspend": { - "description": "Suspend the execution of the shell (send it a SIGTSTP) until it receives a SIGCONT. Unless the -f option is given, this will refuse to suspend a login shell.", - "args": "suspend [ -f ]" - }, - "[": { - "description": "Like the system version of test. Added for compatibility; use conditional expressions instead (see the section `Conditional Expressions'). The main differences between the conditional expression syntax and the test and [ builtins are: these commands are not handled syntactically, so for example an empty variable expansion may cause an argument to be omitted; syntax errors cause status 2 to be returned instead of a shell error; and arithmetic operators expect integer arguments rather than arithmetic expressions. The command attempts to implement POSIX and its extensions where these are specified. Unfortunately there are intrinsic ambiguities in the syntax; in particular there is no distinction between test operators and strings that resemble them. The standard attempts to resolve these for small numbers of arguments (up to four); for five or more arguments compatibility cannot be relied on. Users are urged wherever possible to use the `[[' test syntax which does not have these ambiguities.", - "args": "[ [ arg ... ] ]" - }, - "times": { - "shortDescription": "Display shell times", - "description": "for processes run from the shell.", - "args": "times Print the accumulated user and system times for the shell and" - }, - "trap": { - "shortDescription": "Set signal handlers", - "description": "arg is a series of commands (usually quoted to protect it from immediate evaluation by the shell) to be read and executed when the shell receives any of the signals specified by one or more sig args. Each sig can be given as a number, or as the name of a signal either with or without the string SIG in front (e.g. 1, HUP, and SIGHUP are all the same signal). If arg is `-', then the specified signals are reset to their defaults, or, if no sig args are present, all traps are reset. If arg is an empty string, then the specified signals are ignored by the shell (and by the commands it invokes). If arg is omitted but one or more sig args are provided (i.e. the first argument is a valid signal number or name), the effect is the same as if arg had been specified as `-'. The trap command with no arguments prints a list of commands associated with each signal. If sig is ZERR then arg will be executed after each command with a nonzero exit status. ERR is an alias for ZERR on systems that have no SIGERR signal (this is the usual case). If sig is DEBUG then arg will be executed before each command if the option DEBUG_BEFORE_CMD is set (as it is by default), else after each command. Here, a `command' is what is described as a `sublist' in the shell grammar, see the section SIMPLE COMMANDS & PIPELINES in zshmisc(1). If DEBUG_BEFORE_CMD is set various additional features are available. First, it is possible to skip the next command by setting the option ERR_EXIT; see the description of the ERR_EXIT option in zshoptions(1). Also, the shell parameter ZSH_DEBUG_CMD is set to the string corresponding to the command to be executed following the trap. Note that this string is reconstructed from the internal format and may not be formatted the same way as the original text. The parameter is unset after the trap is executed. If sig is 0 or EXIT and the trap statement is executed inside the body of a function, then the command arg is executed after the function completes. The value of $? at the start of execution is the exit status of the shell or the return status of the function exiting. If sig is 0 or EXIT and the trap statement is not executed inside the body of a function, then the command arg is executed when the shell terminates; the trap runs before any zshexit hook functions. ZERR, DEBUG, and EXIT traps are not executed inside other traps. ZERR and DEBUG traps are kept within subshells, while other traps are reset. Note that traps defined with the trap builtin are slightly different from those defined as `TRAPNAL () { ... }', as the latter have their own function environment (line numbers, local variables, etc.) while the former use the environment of the command in which they were called. For example, trap 'print $LINENO' DEBUG will print the line number of a command executed after it has run, while TRAPDEBUG() { print $LINENO; } will always print the number zero. Alternative signal names are allowed as described under kill above. Defining a trap under either name causes any trap under an alternative name to be removed. However, it is recommended that for consistency users stick exclusively to one name or another.", - "args": "trap [ arg ] [ sig ... ]" - }, - "true": { - "shortDescription": "Return exit status of 0", - "description": "Do nothing and return an exit status of 0.", - "args": "true [ arg ... ]" - }, - "ttyctl": { - "shortDescription": "Control terminal attributes", - "description": "The -f option freezes the tty (i.e. terminal or terminal emulator), and -u unfreezes it. When the tty is frozen, no changes made to the tty settings by external programs will be honored by the shell, except for changes in the size of the screen; the shell will simply reset the settings to their previous values as soon as each command exits or is suspended. Thus, stty and similar programs have no effect when the tty is frozen. Freezing the tty does not cause the current state to be remembered: instead, it causes future changes to the state to be blocked. Without options it reports whether the terminal is frozen or not. Note that, regardless of whether the tty is frozen or not, the shell needs to change the settings when the line editor starts, so unfreezing the tty does not guarantee settings made on the command line are preserved. Strings of commands run between editing the command line will see a consistent tty state. See also the shell variable STTY for a means of initialising the tty before running external commands and/or freezing the tty around a single command.", - "args": "ttyctl [ -fu ]" - }, - "type": { - "shortDescription": "Describe a command", - "description": "Equivalent to whence -v.", - "args": "type [ -wfpamsS ] name ..." - }, - "typeset": { - "shortDescription": "Set or display parameter attributes/values", - "description": "Set or display attributes and values for shell parameters. Except as noted below for control flags that change the behavior, a parameter is created for each name that does not already refer to one. When inside a function, a new parameter is created for every name (even those that already exist), and is unset again when the function completes. See `Local Parameters' in zshparam(1). The same rules apply to special shell parameters, which retain their special attributes when made local. For each name=value assignment, the parameter name is set to value. If the assignment is omitted and name does not refer to an existing parameter, a new parameter is intialized to empty string, zero, or empty array (as appropriate), unless the shell option TYPESET_TO_UNSET is set. When that option is set, the parameter attributes are recorded but the parameter remains unset. If the shell option TYPESET_SILENT is not set, for each remaining name that refers to a parameter that is already set, the name and value of the parameter are printed in the form of an assignment. Nothing is printed for newly-created parameters, or when any attribute flags listed below are given along with the name. Using `+' instead of minus to introduce an attribute turns it off. If no name is present, the names and values of all parameters are printed. In this case the attribute flags restrict the display to only those parameters that have the specified attributes, and using `+' rather than `-' to introduce the flag suppresses printing of the values of parameters when there is no parameter name. All forms of the command handle scalar assignment. Array assignment is possible if any of the reserved words declare, export, float, integer, local, readonly or typeset is matched when the line is parsed (N.B. not when it is executed). In this case the arguments are parsed as assignments, except that the `+=' syntax and the GLOB_ASSIGN option are not supported, and scalar values after = are not split further into words, even if expanded (regardless of the setting of the KSH_TYPESET option; this option is obsolete). Examples of the differences between command and reserved word parsing: # Reserved word parsing typeset svar=$(echo one word) avar=(several words) The above creates a scalar parameter svar and an array parameter avar as if the assignments had been svar=\"one word\" avar=(several words) On the other hand: # Normal builtin interface builtin typeset svar=$(echo two words) The builtin keyword causes the above to use the standard builtin interface to typeset in which argument parsing is performed in the same way as for other commands. This example creates a scalar svar containing the value two and another scalar parameter words with no value. An array value in this case would either cause an error or be treated as an obscure set of glob qualifiers. Arbitrary arguments are allowed if they take the form of assignments after command line expansion; however, these only perform scalar assignment: var='svar=val' typeset $var The above sets the scalar parameter svar to the value val. Parentheses around the value within var would not cause array assignment as they will be treated as ordinary characters when $var is substituted. Any non-trivial expansion in the name part of the assignment causes the argument to be treated in this fashion: typeset {var1,var2,var3}=name The above syntax is valid, and has the expected effect of setting the three parameters to the same value, but the command line is parsed as a set of three normal command line arguments to typeset after expansion. Hence it is not possible to assign to multiple arrays by this means. Note that each interface to any of the commands may be disabled separately. For example, `disable -r typeset' disables the reserved word interface to typeset, exposing the builtin interface, while `disable typeset' disables the builtin. Note that disabling the reserved word interface for typeset may cause problems with the output of `typeset -p', which assumes the reserved word interface is available in order to restore array and associative array values. Unlike parameter assignment statements, typeset's exit status on an assignment that involves a command substitution does not reflect the exit status of the command substitution. Therefore, to test for an error in a command substitution, separate the declaration of the parameter from its initialization: # WRONG typeset var1=$(exit 1) || echo \"Trouble with var1\" # RIGHT typeset var1 && var1=$(exit 1) || echo \"Trouble with var1\" To initialize a parameter param to a command output and mark it readonly, use typeset -r param or readonly param after the parameter assignment statement. If no attribute flags are given, and either no name arguments are present or the flag +m is used, then each parameter name printed is preceded by a list of the attributes of that parameter (array, association, exported, float, integer, readonly, or undefined for autoloaded parameters not yet loaded). If +m is used with attribute flags, and all those flags are introduced with +, the matching parameter names are printed but their values are not. The following control flags change the behavior of typeset: + If `+' appears by itself in a separate word as the last option, then the names of all parameters (functions with -f) are printed, but the values (function bodies) are not. No name arguments may appear, and it is an error for any other options to follow `+'. The effect of `+' is as if all attribute flags which precede it were given with a `+' prefix. For example, `typeset -U +' is equivalent to `typeset +U' and displays the names of all arrays having the uniqueness attribute, whereas `typeset -f -U +' displays the names of all autoloadable functions. If + is the only option, then type information (array, readonly, etc.) is also printed for each parameter, in the same manner as `typeset +m \"*\"'. -g The -g (global) means that any resulting parameter will not be restricted to local scope. Note that this does not necessarily mean that the parameter will be global, as the flag will apply to any existing parameter (even if unset) from an enclosing function. This flag does not affect the parameter after creation, hence it has no effect when listing existing parameters, nor does the flag +g have any effect except in combination with -m (see below). -m If the -m flag is given the name arguments are taken as patterns (use quoting to prevent these from being interpreted as file patterns). With no attribute flags, all parameters (or functions with the -f flag) with matching names are printed (the shell option TYPESET_SILENT is not used in this case). If the +g flag is combined with -m, a new local parameter is created for every matching parameter that is not already local. Otherwise -m applies all other flags or assignments to the existing parameters. Except when assignments are made with name=value, using +m forces the matching parameters and their attributes to be printed, even inside a function. Note that -m is ignored if no patterns are given, so `typeset -m' displays attributes but `typeset -a +m' does not. -p [ n ] If the -p option is given, parameters and values are printed in the form of a typeset command with an assignment, regardless of other flags and options. Note that the -H flag on parameters is respected; no value will be shown for these parameters. -p may be followed by an optional integer argument. Currently only the value 1 is supported. In this case arrays and associative arrays are printed with newlines between indented elements for readability. -T [ scalar[=value] array[=(value ...)] [ sep ] ] This flag has a different meaning when used with -f; see below. Otherwise the -T option requires zero, two, or three arguments to be present. With no arguments, the list of parameters created in this fashion is shown. With two or three arguments, the first two are the name of a scalar and of an array parameter (in that order) that will be tied together in the manner of $PATH and $path. The optional third argument is a single-character separator which will be used to join the elements of the array to form the scalar; if absent, a colon is used, as with $PATH. Only the first character of the separator is significant; any remaining characters are ignored. Multibyte characters are not yet supported. Only one of the scalar and array parameters may be assigned an initial value (the restrictions on assignment forms described above also apply). Both the scalar and the array may be manipulated as normal. If one is unset, the other will automatically be unset too. There is no way of untying the variables without unsetting them, nor of converting the type of one of them with another typeset command; +T does not work, assigning an array to scalar is an error, and assigning a scalar to array sets it to be a single-element array. Note that both `typeset -xT ...' and `export -T ...' work, but only the scalar will be marked for export. Setting the value using the scalar version causes a split on all separators (which cannot be quoted). It is possible to apply -T to two previously tied variables but with a different separator character, in which case the variables remain joined as before but the separator is changed. When an existing scalar is tied to a new array, the value of the scalar is preserved but no attribute other than export will be preserved. Attribute flags that transform the final value (-L, -R, -Z, -l, -u) are only applied to the expanded value at the point of a parameter expansion expression using `$'. They are not applied when a parameter is retrieved internally by the shell for any purpose. The following attribute flags may be specified: -A The names refer to associative array parameters; see `Array Parameters' in zshparam(1). -L [ n ] Left justify and remove leading blanks from the value when the parameter is expanded. If n is nonzero, it defines the width of the field. If n is zero, the width is determined by the width of the value of the first assignment. In the case of numeric parameters, the length of the complete value assigned to the parameter is used to determine the width, not the value that would be output. The width is the count of characters, which may be multibyte characters if the MULTIBYTE option is in effect. Note that the screen width of the character is not taken into account; if this is required, use padding with parameter expansion flags ${(ml...)...} as described in `Parameter Expansion Flags' in zshexpn(1). When the parameter is expanded, it is filled on the right with blanks or truncated if necessary to fit the field. Note truncation can lead to unexpected results with numeric parameters. Leading zeros are removed if the -Z flag is also set. -R [ n ] Similar to -L, except that right justification is used; when the parameter is expanded, the field is left filled with blanks or truncated from the end. May not be combined with the -Z flag. -U For arrays (but not for associative arrays), keep only the first occurrence of each duplicated value. This may also be set for tied parameters (see -T) or colon-separated special parameters like PATH or FIGNORE, etc. Note the flag takes effect on assignment, and the type of the variable being assigned to is determinative; for variables with shared values it is therefore recommended to set the flag for all interfaces, e.g. `typeset -U PATH path'. This flag has a different meaning when used with -f; see below. -Z [ n ] Specially handled if set along with the -L flag. Otherwise, similar to -R, except that leading zeros are used for padding instead of blanks if the first non-blank character is a digit. Numeric parameters are specially handled: they are always eligible for padding with zeroes, and the zeroes are inserted at an appropriate place in the output. -a The names refer to array parameters. An array parameter may be created this way, but it may be assigned to in the typeset statement only if the reserved word form of typeset is enabled (as it is by default). When displaying, both normal and associative arrays are shown. -f The names refer to functions rather than parameters. No assignments can be made, and the only other valid flags are -t, -T, -k, -u, -U and -z. The flag -t turns on execution tracing for this function; the flag -T does the same, but turns off tracing for any named (not anonymous) function called from the present one, unless that function also has the -t or -T flag. The -u and -U flags cause the function to be marked for autoloading; -U also causes alias expansion to be suppressed when the function is loaded. See the description of the `autoload' builtin for details. Note that the builtin functions provides the same basic capabilities as typeset -f but gives access to a few extra options; autoload gives further additional options for the case typeset -fu and typeset -fU. -h Hide: only useful for special parameters (those marked `' in the table in zshparam(1)), and for local parameters with the same name as a special parameter, though harmless for others. A special parameter with this attribute will not retain its special effect when made local. Thus after `typeset -h PATH', a function containing `typeset PATH' will create an ordinary local parameter without the usual behaviour of PATH. Alternatively, the local parameter may itself be given this attribute; hence inside a function `typeset -h PATH' creates an ordinary local parameter and the special PATH parameter is not altered in any way. It is also possible to create a local parameter using `typeset +h special', where the local copy of special will retain its special properties regardless of having the -h attribute. Global special parameters loaded from shell modules (currently those in zsh/mapfile and zsh/parameter) are automatically given the -h attribute to avoid name clashes. -H Hide value: specifies that typeset will not display the value of the parameter when listing parameters; the display for such parameters is always as if the `+' flag had been given. Use of the parameter is in other respects normal, and the option does not apply if the parameter is specified by name, or by pattern with the -m option. This is on by default for the parameters in the zsh/parameter and zsh/mapfile modules. Note, however, that unlike the -h flag this is also useful for non-special parameters. -i [ n ] Use an internal integer representation. If n is nonzero it defines the output arithmetic base, otherwise it is determined by the first assignment. Bases from 2 to 36 inclusive are allowed. -E [ n ] Use an internal double-precision floating point representation. On output the variable will be converted to scientific notation. If n is nonzero it defines the number of significant figures to display; the default is ten. -F [ n ] Use an internal double-precision floating point representation. On output the variable will be converted to fixed-point decimal notation. If n is nonzero it defines the number of digits to display after the decimal point; the default is ten. -l Convert the result to lower case whenever the parameter is expanded. The value is not converted when assigned. -r The given names are marked readonly. Note that if name is a special parameter, the readonly attribute can be turned on, but cannot then be turned off. If the POSIX_BUILTINS option is set, the readonly attribute is more restrictive: unset variables can be marked readonly and cannot then be set; furthermore, the readonly attribute cannot be removed from any variable. It is still possible to change other attributes of the variable though, some of which like -U or -Z would affect the value. More generally, the readonly attribute should not be relied on as a security mechanism. Note that in zsh (like in pdksh but unlike most other shells) it is still possible to create a local variable of the same name as this is considered a different variable (though this variable, too, can be marked readonly). Special variables that have been made readonly retain their value and readonly attribute when made local. -t Tags the named parameters. Tags have no special meaning to the shell. This flag has a different meaning when used with -f; see above. -u Convert the result to upper case whenever the parameter is expanded. The value is not converted when assigned. This flag has a different meaning when used with -f; see above. -x Mark for automatic export to the environment of subsequently executed commands. If the option GLOBAL_EXPORT is set, this implies the option -g, unless +g is also explicitly given; in other words the parameter is not made local to the enclosing function. This is for compatibility with previous versions of zsh.", - "args": "typeset -f [ {+|-}TUkmtuz ] [ + ] [ name ... ]" - }, - "ulimit": { - "shortDescription": "Set or display resource limits", - "description": "Set or display resource limits of the shell and the processes started by the shell. The value of limit can be a number in the unit specified below or one of the values `unlimited', which removes the limit on the resource, or `hard', which uses the current value of the hard limit on the resource. By default, only soft limits are manipulated. If the -H flag is given use hard limits instead of soft limits. If the -S flag is given together with the -H flag set both hard and soft limits. If no options are used, the file size limit (-f) is assumed. If limit is omitted the current value of the specified resources are printed. When more than one resource value is printed, the limit name and unit is printed before each value. When looping over multiple resources, the shell will abort immediately if it detects a badly formed argument. However, if it fails to set a limit for some other reason it will continue trying to set the remaining limits. Not all the following resources are supported on all systems. Running ulimit -a will show which are supported. -a Lists all of the current resource limits. -b Socket buffer size in bytes (N.B. not kilobytes) -c 512-byte blocks on the size of core dumps. -d Kilobytes on the size of the data segment. -f 512-byte blocks on the size of files written. -i The number of pending signals. -k The number of kqueues allocated. -l Kilobytes on the size of locked-in memory. -m Kilobytes on the size of physical memory. -n open file descriptors. -p The number of pseudo-terminals. -q Bytes in POSIX message queues. -r Maximum real time priority. On some systems where this is not available, such as NetBSD, this has the same effect as -T for compatibility with sh. -s Kilobytes on the size of the stack. -T The number of simultaneous threads available to the user. -t CPU seconds to be used. -u The number of processes available to the user. -v Kilobytes on the size of virtual memory. On some systems this refers to the limit called `address space'. -w Kilobytes on the size of swapped out memory. -x The number of locks on files. A resource may also be specified by integer in the form `-N resource', where resource corresponds to the integer defined for the resource by the operating system. This may be used to set the limits for resources known to the shell which do not correspond to option letters. Such limits will be shown by number in the output of `ulimit -a'. The number may alternatively be out of the range of limits compiled into the shell. The shell will try to read or write the limit anyway, and will report an error if this fails.", - "args": "ulimit [ -HSa ] [ { -bcdfiklmnpqrsTtvwx | -N resource } [ limit ] ... ]" - }, - "umask": { - "shortDescription": "Set file creation mask", - "description": "The umask is set to mask. mask can be either an octal number or a symbolic value as described in the chmod(1) man page. If mask is omitted, the current value is printed. The -S option causes the mask to be printed as a symbolic value. Otherwise, the mask is printed as an octal number. Note that in the symbolic form the permissions you specify are those which are to be allowed (not denied) to the users specified.", - "args": "umask [ -S ] [ mask ]" - }, - "unalias": { - "shortDescription": "Removes aliases", - "description": "Removes aliases. This command works the same as unhash -a, except that the -a option removes all regular or global aliases, or with -s all suffix aliases: in this case no name arguments may appear. The options -m (remove by pattern) and -s without -a (remove listed suffix aliases) behave as for unhash -a. Note that the meaning of -a is different between unalias and unhash.", - "args": "unalias [ -ams ] name ..." - }, - "unfunction": { - "shortDescription": "Remove function definition", - "description": "Same as unhash -f.", - "args": "unfunction undefined" - }, - "unhash": { - "shortDescription": "Remove command from hash table", - "description": "Remove the element named name from an internal hash table. The default is remove elements from the command hash table. The -a option causes unhash to remove regular or global aliases; note when removing a global aliases that the argument must be quoted to prevent it from being expanded before being passed to the command. The -s option causes unhash to remove suffix aliases. The -f option causes unhash to remove shell functions. The -d options causes unhash to remove named directories. If the -m flag is given the arguments are taken as patterns (should be quoted) and all elements of the corresponding hash table with matching names will be removed.", - "args": "unhash [ -adfms ] name ..." - }, - "unlimit": { - "shortDescription": "Remove resource limits", - "description": "The resource limit for each resource is set to the hard limit. If the -h flag is given and the shell has appropriate privileges, the hard resource limit for each resource is removed. The resources of the shell process are only changed if the -s flag is given. The unlimit command is not made available by default when the shell starts in a mode emulating another shell. It can be made available with the command `zmodload -F zsh/rlimits b:unlimit'.", - "args": "unlimit [ -hs ] resource ..." - }, - "unset": { - "shortDescription": "Unset values and attributes of variables", - "description": "Each named parameter is unset. Local parameters remain local even if unset; they appear unset within scope, but the previous value will still reappear when the scope ends. Individual elements of associative array parameters may be unset by using subscript syntax on name, which should be quoted (or the entire command prefixed with noglob) to protect the subscript from filename generation. If the -m flag is specified the arguments are taken as patterns (should be quoted) and all parameters with matching names are unset. Note that this cannot be used when unsetting associative array elements, as the subscript will be treated as part of the pattern. The -v flag specifies that name refers to parameters. This is the default behaviour. unset -f is equivalent to unfunction.", - "args": "unset [ -fmv ] name ..." - }, - "unsetopt": { - "shortDescription": "Unset shell options", - "description": "Unset the options for the shell. All options specified either with flags or by name are unset. If no arguments are supplied, the names of all options currently unset are printed. If the -m flag is given the arguments are taken as patterns (which should be quoted to preserve them from being interpreted as glob patterns), and all options with names matching these patterns are unset.", - "args": "unsetopt [ {+|-}options | {+|-}o option_name ] [ name ... ]" - }, - "wait": { - "description": "Wait for the specified jobs or processes. If job is not given then all currently active child processes are waited for. Each job can be either a job specification or the process ID of a job in the job table. The exit status from this command is that of the job waited for. If job represents an unknown job or process ID, a warning is printed (unless the POSIX_BUILTINS option is set) and the exit status is 127. It is possible to wait for recent processes (specified by process ID, not by job) that were running in the background even if the process has exited. Typically the process ID will be recorded by capturing the value of the variable $! immediately after the process has been started. There is a limit on the number of process IDs remembered by the shell; this is given by the value of the system configuration parameter CHILD_MAX. When this limit is reached, older process IDs are discarded, least recently started processes first. Note there is no protection against the process ID wrapping, i.e. if the wait is not executed soon enough there is a chance the process waited for is the wrong one. A conflict implies both process IDs have been generated by the shell, as other processes are not recorded, and that the user is potentially interested in both, so this problem is intrinsic to process IDs.", - "args": "wait [ job ... ]" - }, - "whence": { - "shortDescription": "Locate a command", - "description": "For each name, indicate how it would be interpreted if used as a command name. If name is not an alias, built-in command, external command, shell function, hashed command, or a reserved word, the exit status shall be non-zero, and -- if -v, -c, or -w was passed -- a message will be written to standard output. (This is different from other shells that write that message to standard error.) whence is most useful when name is only the last path component of a command, i.e. does not include a `/'; in particular, pattern matching only succeeds if just the non-directory component of the command is passed. -v Produce a more verbose report. -c Print the results in a csh-like format. This takes precedence over -v. -w For each name, print `name: word' where word is one of alias, builtin, command, function, hashed, reserved or none, according as name corresponds to an alias, a built-in command, an external command, a shell function, a command defined with the hash builtin, a reserved word, or is not recognised. This takes precedence over -v and -c. -f Causes the contents of a shell function to be displayed, which would otherwise not happen unless the -c flag were used. -p Do a path search for name even if it is an alias, reserved word, shell function or builtin. -a Do a search for all occurrences of name throughout the command path. Normally only the first occurrence is printed. -m The arguments are taken as patterns (pattern characters should be quoted), and the information is displayed for each command matching one of these patterns. -s If a pathname contains symlinks, print the symlink-free pathname as well. -S As -s, but if the pathname had to be resolved by following multiple symlinks, the intermediate steps are printed, too. The symlink resolved at each step might be anywhere in the path. -x num Expand tabs when outputting shell functions using the -c option. This has the same effect as the -x option to the functions builtin.", - "args": "whence [ -vcwfpamsS ] [ -x num ] name ..." - }, - "where": { - "shortDescription": "Locate a command", - "description": "Equivalent to whence -ca.", - "args": "where [ -wpmsS ] [ -x num ] name ..." - }, - "which": { - "shortDescription": "Locate a command", - "description": "Equivalent to whence -c.", - "args": "which [ -wpamsS ] [ -x num ] name ..." - }, - "zcompile": { - "shortDescription": "Compile functions", - "description": "This builtin command can be used to compile functions or scripts, storing the compiled form in a file, and to examine files containing the compiled form. This allows faster autoloading of functions and sourcing of scripts by avoiding parsing of the text when the files are read. The first form (without the -c, -a or -t options) creates a compiled file. If only the file argument is given, the output file has the name `file.zwc' and will be placed in the same directory as the file. The shell will load the compiled file instead of the normal function file when the function is autoloaded; see the section `Autoloading Functions' in zshmisc(1) for a description of how autoloaded functions are searched. The extension .zwc stands for `zsh word code'. If there is at least one name argument, all the named files are compiled into the output file given as the first argument. If file does not end in .zwc, this extension is automatically appended. Files containing multiple compiled functions are called `digest' files, and are intended to be used as elements of the FPATH/fpath special array. The second form, with the -c or -a options, writes the compiled definitions for all the named functions into file. For -c, the names must be functions currently defined in the shell, not those marked for autoloading. Undefined functions that are marked for autoloading may be written by using the -a option, in which case the fpath is searched and the contents of the definition files for those functions, if found, are compiled into file. If both -c and -a are given, names of both defined functions and functions marked for autoloading may be given. In either case, the functions in files written with the -c or -a option will be autoloaded as if the KSH_AUTOLOAD option were unset. The reason for handling loaded and not-yet-loaded functions with different options is that some definition files for autoloading define multiple functions, including the function with the same name as the file, and, at the end, call that function. In such cases the output of `zcompile -c' does not include the additional functions defined in the file, and any other initialization code in the file is lost. Using `zcompile -a' captures all this extra information. If the -m option is combined with -c or -a, the names are used as patterns and all functions whose names match one of these patterns will be written. If no name is given, the definitions of all functions currently defined or marked as autoloaded will be written. Note the second form cannot be used for compiling functions that include redirections as part of the definition rather than within the body of the function; for example fn1() { { ... } >~/logfile } can be compiled but fn1() { ... } >~/logfile cannot. It is possible to use the first form of zcompile to compile autoloadable functions that include the full function definition instead of just the body of the function. The third form, with the -t option, examines an existing compiled file. Without further arguments, the names of the original files compiled into it are listed. The first line of output shows the version of the shell which compiled the file and how the file will be used (i.e. by reading it directly or by mapping it into memory). With arguments, nothing is output and the return status is set to zero if definitions for all names were found in the compiled file, and non-zero if the definition for at least one name was not found. Other options: -U Aliases are not expanded when compiling the named files. -R When the compiled file is read, its contents are copied into the shell's memory, rather than memory-mapped (see -M). This happens automatically on systems that do not support memory mapping. When compiling scripts instead of autoloadable functions, it is often desirable to use this option; otherwise the whole file, including the code to define functions which have already been defined, will remain mapped, consequently wasting memory. -M The compiled file is mapped into the shell's memory when read. This is done in such a way that multiple instances of the shell running on the same host will share this mapped file. If neither -R nor -M is given, the zcompile builtin decides what to do based on the size of the compiled file. -k -z These options are used when the compiled file contains functions which are to be autoloaded. If -z is given, the function will be autoloaded as if the KSH_AUTOLOAD option is not set, even if it is set at the time the compiled file is read, while if the -k is given, the function will be loaded as if KSH_AUTOLOAD is set. These options also take precedence over any -k or -z options specified to the autoload builtin. If neither of these options is given, the function will be loaded as determined by the setting of the KSH_AUTOLOAD option at the time the compiled file is read. These options may also appear as many times as necessary between the listed names to specify the loading style of all following functions, up to the next -k or -z. The created file always contains two versions of the compiled format, one for big-endian machines and one for small-endian machines. The upshot of this is that the compiled file is machine independent and if it is read or mapped, only one half of the file is actually used (and mapped).", - "args": "zcompile -t file [ name ... ]" - }, - "zformat": { - "shortDescription": "Format strings", - "description": "See the section `The zsh/zutil Module' in zshmodules(1).", - "args": "zformat undefined" - }, - "zmodload": { - "description": "Performs operations relating to zsh's loadable modules. Loading of modules while the shell is running (`dynamical loading') is not available on all operating systems, or on all installations on a particular operating system, although the zmodload command itself is always available and can be used to manipulate modules built into versions of the shell executable without dynamical loading. Without arguments the names of all currently loaded binary modules are printed. The -L option causes this list to be in the form of a series of zmodload commands. Forms with arguments are: zmodload [ -is ] name ... zmodload -u [ -i ] name ... In the simplest case, zmodload loads a binary module. The module must be in a file with a name consisting of the specified name followed by a standard suffix, usually `.so' (`.sl' on HPUX). If the module to be loaded is already loaded the duplicate module is ignored. If zmodload detects an inconsistency, such as an invalid module name or circular dependency list, the current code block is aborted. If it is available, the module is loaded if necessary, while if it is not available, non-zero status is silently returned. The option -i is accepted for compatibility but has no effect. The named module is searched for in the same way a command is, using $module_path instead of $path. However, the path search is performed even when the module name contains a `/', which it usually does. There is no way to prevent the path search. If the module supports features (see below), zmodload tries to enable all features when loading a module. If the module was successfully loaded but not all features could be enabled, zmodload returns status 2. If the option -s is given, no error is printed if the module was not available (though other errors indicating a problem with the module are printed). The return status indicates if the module was loaded. This is appropriate if the caller considers the module optional. With -u, zmodload unloads modules. The same name must be given that was given when the module was loaded, but it is not necessary for the module to exist in the file system. The -i option suppresses the error if the module is already unloaded (or was never loaded). Each module has a boot and a cleanup function. The module will not be loaded if its boot function fails. Similarly a module can only be unloaded if its cleanup function runs successfully. zmodload -F [ -almLe -P param ] module [ [+-]feature ... ] zmodload -F allows more selective control over the features provided by modules. With no options apart from -F, the module named module is loaded, if it was not already loaded, and the list of features is set to the required state. If no features are specified, the module is loaded, if it was not already loaded, but the state of features is unchanged. Each feature may be preceded by a + to turn the feature on, or - to turn it off; the + is assumed if neither character is present. Any feature not explicitly mentioned is left in its current state; if the module was not previously loaded this means any such features will remain disabled. The return status is zero if all features were set, 1 if the module failed to load, and 2 if some features could not be set (for example, a parameter couldn't be added because there was a different parameter of the same name) but the module was loaded. The standard features are builtins, conditions, parameters and math functions; these are indicated by the prefix `b:', `c:' (`C:' for an infix condition), `p:' and `f:', respectively, followed by the name that the corresponding feature would have in the shell. For example, `b:strftime' indicates a builtin named strftime and p:EPOCHSECONDS indicates a parameter named EPOCHSECONDS. The module may provide other (`abstract') features of its own as indicated by its documentation; these have no prefix. With -l or -L, features provided by the module are listed. With -l alone, a list of features together with their states is shown, one feature per line. With -L alone, a zmodload -F command that would cause enabled features of the module to be turned on is shown. With -lL, a zmodload -F command that would cause all the features to be set to their current state is shown. If one of these combinations is given with the option -P param then the parameter param is set to an array of features, either features together with their state or (if -L alone is given) enabled features. With the option -L the module name may be omitted; then a list of all enabled features for all modules providing features is printed in the form of zmodload -F commands. If -l is also given, the state of both enabled and disabled features is output in that form. A set of features may be provided together with -l or -L and a module name; in that case only the state of those features is considered. Each feature may be preceded by + or - but the character has no effect. If no set of features is provided, all features are considered. With -e, the command first tests that the module is loaded; if it is not, status 1 is returned. If the module is loaded, the list of features given as an argument is examined. Any feature given with no prefix is simply tested to see if the module provides it; any feature given with a prefix + or - is tested to see if is provided and in the given state. If the tests on all features in the list succeed, status 0 is returned, else status 1. With -m, each entry in the given list of features is taken as a pattern to be matched against the list of features provided by the module. An initial + or - must be given explicitly. This may not be combined with the -a option as autoloads must be specified explicitly. With -a, the given list of features is marked for autoload from the specified module, which may not yet be loaded. An optional + may appear before the feature name. If the feature is prefixed with -, any existing autoload is removed. The options -l and -L may be used to list autoloads. Autoloading is specific to individual features; when the module is loaded only the requested feature is enabled. Autoload requests are preserved if the module is subsequently unloaded until an explicit `zmodload -Fa module -feature' is issued. It is not an error to request an autoload for a feature of a module that is already loaded. When the module is loaded each autoload is checked against the features actually provided by the module; if the feature is not provided the autoload request is deleted. A warning message is output; if the module is being loaded to provide a different feature, and that autoload is successful, there is no effect on the status of the current command. If the module is already loaded at the time when zmodload -Fa is run, an error message is printed and status 1 returned. zmodload -Fa can be used with the -l, -L, -e and -P options for listing and testing the existence of autoloadable features. In this case -l is ignored if -L is specified. zmodload -FaL with no module name lists autoloads for all modules. Note that only standard features as described above can be autoloaded; other features require the module to be loaded before enabling. zmodload -d [ -L ] [ name ] zmodload -d name dep ... zmodload -ud name [ dep ... ] The -d option can be used to specify module dependencies. The modules named in the second and subsequent arguments will be loaded before the module named in the first argument. With -d and one argument, all dependencies for that module are listed. With -d and no arguments, all module dependencies are listed. This listing is by default in a Makefile-like format. The -L option changes this format to a list of zmodload -d commands. If -d and -u are both used, dependencies are removed. If only one argument is given, all dependencies for that module are removed. zmodload -ab [ -L ] zmodload -ab [ -i ] name [ builtin ... ] zmodload -ub [ -i ] builtin ... The -ab option defines autoloaded builtins. It defines the specified builtins. When any of those builtins is called, the module specified in the first argument is loaded and all its features are enabled (for selective control of features use `zmodload -F -a' as described above). If only the name is given, one builtin is defined, with the same name as the module. -i suppresses the error if the builtin is already defined or autoloaded, but not if another builtin of the same name is already defined. With -ab and no arguments, all autoloaded builtins are listed, with the module name (if different) shown in parentheses after the builtin name. The -L option changes this format to a list of zmodload -a commands. If -b is used together with the -u option, it removes builtins previously defined with -ab. This is only possible if the builtin is not yet loaded. -i suppresses the error if the builtin is already removed (or never existed). Autoload requests are retained if the module is subsequently unloaded until an explicit `zmodload -ub builtin' is issued. zmodload -ac [ -IL ] zmodload -ac [ -iI ] name [ cond ... ] zmodload -uc [ -iI ] cond ... The -ac option is used to define autoloaded condition codes. The cond strings give the names of the conditions defined by the module. The optional -I option is used to define infix condition names. Without this option prefix condition names are defined. If given no condition names, all defined names are listed (as a series of zmodload commands if the -L option is given). The -uc option removes definitions for autoloaded conditions. zmodload -ap [ -L ] zmodload -ap [ -i ] name [ parameter ... ] zmodload -up [ -i ] parameter ... The -p option is like the -b and -c options, but makes zmodload work on autoloaded parameters instead. zmodload -af [ -L ] zmodload -af [ -i ] name [ function ... ] zmodload -uf [ -i ] function ... The -f option is like the -b, -p, and -c options, but makes zmodload work on autoloaded math functions instead. zmodload -a [ -L ] zmodload -a [ -i ] name [ builtin ... ] zmodload -ua [ -i ] builtin ... Equivalent to -ab and -ub. zmodload -e [ -A ] [ string ... ] The -e option without arguments lists all loaded modules; if the -A option is also given, module aliases corresponding to loaded modules are also shown. If arguments are provided, nothing is printed; the return status is set to zero if all strings given as arguments are names of loaded modules and to one if at least on string is not the name of a loaded module. This can be used to test for the availability of things implemented by modules. In this case, any aliases are automatically resolved and the -A flag is not used. zmodload -A [ -L ] [ modalias[=module] ... ] For each argument, if both modalias and module are given, define modalias to be an alias for the module module. If the module modalias is ever subsequently requested, either via a call to zmodload or implicitly, the shell will attempt to load module instead. If module is not given, show the definition of modalias. If no arguments are given, list all defined module aliases. When listing, if the -L flag was also given, list the definition as a zmodload command to recreate the alias. The existence of aliases for modules is completely independent of whether the name resolved is actually loaded as a module: while the alias exists, loading and unloading the module under any alias has exactly the same effect as using the resolved name, and does not affect the connection between the alias and the resolved name which can be removed either by zmodload -R or by redefining the alias. Chains of aliases (i.e. where the first resolved name is itself an alias) are valid so long as these are not circular. As the aliases take the same format as module names, they may include path separators: in this case, there is no requirement for any part of the path named to exist as the alias will be resolved first. For example, `any/old/alias' is always a valid alias. Dependencies added to aliased modules are actually added to the resolved module; these remain if the alias is removed. It is valid to create an alias whose name is one of the standard shell modules and which resolves to a different module. However, if a module has dependencies, it will not be possible to use the module name as an alias as the module will already be marked as a loadable module in its own right. Apart from the above, aliases can be used in the zmodload command anywhere module names are required. However, aliases will not be shown in lists of loaded modules with a bare `zmodload'. zmodload -R modalias ... For each modalias argument that was previously defined as a module alias via zmodload -A, delete the alias. If any was not defined, an error is caused and the remainder of the line is ignored. Note that zsh makes no distinction between modules that were linked into the shell and modules that are loaded dynamically. In both cases this builtin command has to be used to make available the builtins and other things defined by modules (unless the module is autoloaded on these definitions). This is true even for systems that don't support dynamic loading of modules.", - "args": "zmodload -R modalias ..." - }, - "zparseopts": { - "shortDescription": "Parse options", - "description": "See the section `The zsh/zutil Module' in zshmodules(1).", - "args": "zparseopts undefined" - }, - "zregexparse": { - "description": "See the section `The zsh/zutil Module' in zshmodules(1).", - "args": "zregexparse undefined" - }, - "zsocket": { - "shortDescription": "Zsh socket interface", - "description": "See the section `The zsh/net/socket Module' in zshmodules(1).", - "args": "zsocket undefined" - } -} \ No newline at end of file diff --git a/extensions/terminal-suggest/src/shell/zshBuiltinsCache.ts b/extensions/terminal-suggest/src/shell/zshBuiltinsCache.ts new file mode 100644 index 000000000000..6626192ce987 --- /dev/null +++ b/extensions/terminal-suggest/src/shell/zshBuiltinsCache.ts @@ -0,0 +1,543 @@ + +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export const zshBuiltinsCommandDescriptionsCache = { + ".": { + "shortDescription": "Source a file", + "description": ": Read commands from *file* and execute them in the current shell environment.\n\n If *file* does not contain a slash, or if **PATH_DIRS** is set, the shell looks in the components of **\\$path** to find the directory containing *file*. Files in the current directory are not read unless `**.**` appears somewhere in **\\$path**. If a file named `*file***.zwc**` is found, is newer than *file*, and is the compiled form (created with the **zcompile** builtin) of *file*, then commands are read from that file instead of *file*.\n\n If any arguments *arg* are given, they become the positional parameters; the old positional parameters are restored when the *file* is done executing. However, if no arguments are given, the positional parameters remain those of the calling context, and no restoring is done.\n\n If *file* was not found the return status is 127; if *file* was found but contained a syntax error the return status is 126; else the return status is the exit status of the last command executed.", + "args": ". file [ arg ... ]" + }, + ":": { + "shortDescription": "No effect", + "description": ": This command does nothing, although normal argument expansions is performed which may have effects on shell parameters. A zero exit status is returned.", + "args": ": [ arg ... ]" + }, + "alias": { + "shortDescription": "Define or view aliases", + "description": ": For each *name* with a corresponding *value*, define an alias with that value. A trailing space in *value* causes the next word to be checked for alias expansion. If the **-g** flag is present, define a global alias; global aliases are expanded even if they do not occur in command position:\n\n > % perldoc --help 2>&1 | grep 'built-in functions'\n > -f Search Perl built-in functions\n > % alias -g HG='--help 2>&1 | grep'\n > % perldoc HG 'built-in functions'\n > -f Search Perl built-in functions\n\n If the **-s** flag is present, define a suffix alias: if the command word on a command line is in the form `*text***.***name*`, where *text* is any non-empty string, it is replaced by the text `*value* *text***.***name*`. Note that *name* is treated as a literal string, not a pattern. A trailing space in *value* is not special in this case. For example,\n\n > alias -s ps='gv --'\n\n will cause the command `**\\*.ps**` to be expanded to `**gv \\-- \\*.ps**`. As alias expansion is carried out earlier than globbing, the `**\\*.ps**` will then be expanded. Suffix aliases constitute a different name space from other aliases (so in the above example it is still possible to create an alias for the command **ps**) and the two sets are never listed together.\n\n For each *name* with no *value*, print the value of *name*, if any. With no arguments, print all currently defined aliases other than suffix aliases. If the **-m** flag is given the arguments are taken as patterns (they should be quoted to preserve them from being interpreted as glob patterns), and the aliases matching these patterns are printed. When printing aliases and one of the **-g**, **-r** or **-s** flags is present, restrict the printing to global, regular or suffix aliases, respectively; a regular alias is one which is neither a global nor a suffix alias. Using `**+**` instead of `**-**`, or ending the option list with a single `**+**`, prevents the values of the aliases from being printed.\n\n If the **-L** flag is present, then print each alias in a manner suitable for putting in a startup script. The exit status is nonzero if a *name* (with no *value*) is given for which no alias has been defined.\n\n For more on aliases, include common problems, see the section ALIASING in *zshmisc*(1).", + "args": "alias [ {+|-}gmrsL ] [ name[=value] ... ]" + }, + "autoload": { + "shortDescription": "Autoload a function", + "description": ": See the section `Autoloading Functions` in *zshmisc*(1) for full details. The **fpath** parameter will be searched to find the function definition when the function is first referenced.\n\n If *name* consists of an absolute path, the function is defined to load from the file given (searching as usual for dump files in the given location). The name of the function is the basename (non-directory part) of the file. It is normally an error if the function is not found in the given location; however, if the option **-d** is given, searching for the function defaults to **\\$fpath**. If a function is loaded by absolute path, any functions loaded from it that are marked for **autoload** without an absolute path have the load path of the parent function temporarily prepended to **\\$fpath**.\n\n If the option **-r** or **-R** is given, the function is searched for immediately and the location is recorded internally for use when the function is executed; a relative path is expanded using the value of **\\$PWD**. This protects against a change to **\\$fpath** after the call to **autoload**. With **-r**, if the function is not found, it is silently left unresolved until execution; with **-R**, an error message is printed and command processing aborted immediately the search fails, i.e. at the **autoload** command rather than at function execution..\n\n The flag **-X** may be used only inside a shell function. It causes the calling function to be marked for autoloading and then immediately loaded and executed, with the current array of positional parameters as arguments. This replaces the previous definition of the function. If no function definition is found, an error is printed and the function remains undefined and marked for autoloading. If an argument is given, it is used as a directory (i.e. it does not include the name of the function) in which the function is to be found; this may be combined with the **-d** option to allow the function search to default to **\\$fpath** if it is not in the given location.\n\n The flag **+X** attempts to load each *name* as an autoloaded function, but does *not* execute it. The exit status is zero (success) if the function was not previously defined *and* a definition for it was found. This does *not* replace any existing definition of the function. The exit status is nonzero (failure) if the function was already defined or when no definition was found. In the latter case the function remains undefined and marked for autoloading. If ksh-style autoloading is enabled, the function created will contain the contents of the file plus a call to the function itself appended to it, thus giving normal ksh autoloading behaviour on the first call to the function. If the **-m** flag is also given each *name* is treated as a pattern and all functions already marked for autoload that match the pattern are loaded.\n\n With the **-t** flag, turn on execution tracing; with **-T**, turn on execution tracing only for the current function, turning it off on entry to any called functions that do not also have tracing enabled.\n\n With the **-U** flag, alias expansion is suppressed when the function is loaded.\n\n With the **-w** flag, the *name*s are taken as names of files compiled with the **zcompile** builtin, and all functions defined in them are marked for autoloading.\n\n The flags **-z** and **-k** mark the function to be autoloaded using the zsh or ksh style, as if the option **KSH_AUTOLOAD** were unset or were set, respectively. The flags override the setting of the option at the time the function is loaded.\n\n Note that the **autoload** command makes no attempt to ensure the shell options set during the loading or execution of the file have any particular value. For this, the **emulate** command can be used:\n\n > emulate zsh -c 'autoload -Uz func'\n\n arranges that when *func is loaded the shell is in native* **zsh** emulation, and this emulation is also applied when *func is run.*\n\n Some of the functions of **autoload** *are also provided by* **functions** -u *or* **functions -U***, but* **autoload** *is a more comprehensive* interface.", + "args": "autoload [ {+|-}RTUXdkmrtWz ] [ -w ] [ name ... ]" + }, + "bg": { + "shortDescription": "Put a job in the background", + "description": "*job \\...* **&** Put each specified *job in the background,* or the current job if none is specified.", + "args": "bg [ job ... ]" + }, + "bindkey": { + "shortDescription": "Manipulate keymap names", + "description": ": See the section `Zle Builtins` in *zshzle(1).*", + "args": "bindkey" + }, + "break": { + "shortDescription": "Exit from a loop", + "description": ": Exit from an enclosing **for***,* **while***,* **until***,* **select** *or* **repeat** *loop. If an arithmetic expression n* is specified, then break *n levels instead of just one.*", + "args": "break [ n ]" + }, + "builtin": { + "shortDescription": "Executes a builtin", + "description": ": Executes the builtin *name, with the given args.*", + "args": "builtin name [ args ... ]" + }, + "bye": { + "shortDescription": "Exit the shell", + "description": ": Same as **exit***.*", + "args": "bye" + }, + "cap": { + "shortDescription": "Manipulating POSIX capability sets", + "description": ": See the section `The zsh/cap Module` in *zshmodules(1).*", + "args": "cap" + }, + "cd": { + "shortDescription": "Change the current directory", + "description": "**cd** *\\[* **-qsLP** *\\] {***+***\\|***-***}n* Change the current directory. In the first form, change the current directory to *arg, or to the value of* **\\$HOME** *if* *arg is not specified. If arg is `***-***`, change to the* previous directory.\n\n> Otherwise, if *arg begins with a slash, attempt to change to the* directory given by *arg.*\n>\n> If *arg does not begin with a slash, the behaviour depends on whether* the current directory `**.***` occurs in the list of directories contained* in the shell parameter **cdpath***. If it does not, first attempt to change* to the directory *arg under the current directory, and if that fails* but **cdpath** *is set and contains at least one element attempt to change* to the directory *arg under each component of* **cdpath** *in turn until* successful. If `**.***` occurs in* **cdpath***, then* **cdpath** *is searched* strictly in order so that `**.***` is only tried at the appropriate point.*\n>\n> The order of testing **cdpath** *is modified if the option* **POSIX_CD** is set, as described in the documentation for the option.\n>\n> If no directory is found, the option **CDABLE_VARS** *is set, and a* parameter named *arg exists whose value begins with a slash, treat its* value as the directory. In that case, the parameter is added to the named directory hash table.\n>\n> The second form of **cd** *substitutes the string new* for the string *old in the name of the current directory,* and tries to change to this new directory.\n>\n> The third form of **cd** *extracts an entry from the directory* stack, and changes to that directory. An argument of the form `**+***n` identifies a stack entry by counting from the left* of the list shown by the **dirs** *command, starting with zero.* An argument of the form `**-***n` counts from the right.* If the **PUSHD_MINUS** *option is set, the meanings of `***+***`* and `**-***` in this context are swapped.* If the **POSIX_CD** *option is set, this form of* **cd** *is not recognised* and will be interpreted as the first form.\n>\n> If the **-q** *(quiet) option is specified, the hook function* **chpwd** and the functions in the array **chpwd_functions** *are not called.* This is useful for calls to **cd** *that do not change the environment* seen by an interactive user.\n>\n> If the **-s** *option is specified,* **cd** *refuses to change the current* directory if the given pathname contains symlinks. If the **-P** *option* is given or the **CHASE_LINKS** *option is set, symbolic links are resolved* to their true values. If the **-L** *option is given symbolic links are* retained in the directory (and not resolved) regardless of the state of the **CHASE_LINKS** *option.*", + "args": "cd [ -qsLP ] [ arg ]\n\ncd [ -qsLP ] old new" + }, + "chdir": { + "shortDescription": "Change the current directory", + "description": ": Same as **cd***.*", + "args": "chdir" + }, + "clone": { + "shortDescription": "Clone shell onto another terminal", + "description": ": See the section `The zsh/clone Module` in *zshmodules(1).*", + "args": "clone" + }, + "command": { + "shortDescription": "Execute a command", + "description": ": The simple command argument is taken as an external command instead of a function or builtin and is executed. If the **POSIX_BUILTINS** *option* is set, builtins will also be executed but certain special properties of them are suppressed. The **-p** *flag causes a default path to be* searched instead of that in **\\$path***. With the* **-v** *flag,* **command** is similar to **whence** *and with* **-V***, it is equivalent to* **whence** -v*.*\n\n See also the section `Precommand Modifiers` in *zshmisc(1).*", + "args": "command [ -pvV ] simple command" + }, + "comparguments": { + "shortDescription": "Complete arguments", + "description": ": See the section `The zsh/computil Module` in *zshmodules(1).*", + "args": "comparguments" + }, + "compcall": { + "shortDescription": "Complete call", + "description": ": See the section `The zsh/compctl Module` in *zshmodules(1).*", + "args": "compcall" + }, + "compctl": { + "shortDescription": "Complete control", + "description": ": See the section `The zsh/compctl Module` in *zshmodules(1).*", + "args": "compctl" + }, + "compdescribe": { + "shortDescription": "Complete describe", + "description": ": See the section `The zsh/computil Module` in *zshmodules(1).*", + "args": "compdescribe" + }, + "compfiles": { + "shortDescription": "Complete files", + "description": ": See the section `The zsh/computil Module` in *zshmodules(1).*", + "args": "compfiles" + }, + "compgroups": { + "shortDescription": "Complete groups", + "description": ": See the section `The zsh/computil Module` in *zshmodules(1).*", + "args": "compgroups" + }, + "compquote": { + "shortDescription": "Complete quote", + "description": ": See the section `The zsh/computil Module` in *zshmodules(1).*", + "args": "compquote" + }, + "comptags": { + "shortDescription": "Complete tags", + "description": ": See the section `The zsh/computil Module` in *zshmodules(1).*", + "args": "comptags" + }, + "comptry": { + "shortDescription": "Complete try", + "description": ": See the section `The zsh/computil Module` in *zshmodules(1).*", + "args": "comptry" + }, + "compvalues": { + "shortDescription": "Complete values", + "description": ": See the section `The zsh/computil Module` in *zshmodules(1).*", + "args": "compvalues" + }, + "continue": { + "shortDescription": "Resume the next loop iteration", + "description": ": Resume the next iteration of the enclosing **for***,* **while***,* **until***,* **select** *or* **repeat** *loop. If an arithmetic expression n is specified, break out of* *n-1 loops and resume at the nth enclosing loop.*", + "args": "continue [ n ]" + }, + "declare": { + "shortDescription": "Set or display parameter attributes/values", + "description": ": Same as **typeset***.*", + "args": "declare" + }, + "dirs": { + "shortDescription": "Interact with directory stack", + "description": "**dirs** *\\[* **-lpv** *\\]* With no arguments, print the contents of the directory stack. Directories are added to this stack with the **pushd** *command,* and removed with the **cd** *or* **popd** *commands.* If arguments are specified, load them onto the directory stack, replacing anything that was there, and push the current directory onto the stack.\n\n> **-c** clear the directory stack.\n>\n> **-l**\n>\n> : print directory names in full instead of using of using **\\~** *expressions (see Dynamic and Static named directories in zshexpn(1)).*\n>\n> **-p**\n>\n> : print directory entries one per line.\n>\n> **-v**\n>\n> : number the directories in the stack when printing.", + "args": "dirs [ -c ] [ arg ... ]" + }, + "disable": { + "shortDescription": "Disable shell features", + "description": ": Temporarily disable the *named hash table elements or patterns. The default* is to disable builtin commands. This allows you to use an external command with the same name as a builtin command. The **-a** *option* causes **disable** *to act on regular or global aliases. The* **-s** option causes **disable** *to act on suffix aliases. The* **-f** *option causes* **disable** *to act on shell functions. The* **-r** *options causes* **disable** *to act on reserved words. Without arguments all disabled* hash table elements from the corresponding hash table are printed. With the **-m** *flag the arguments are taken as patterns (which should be* quoted to prevent them from undergoing filename expansion), and all hash table elements from the corresponding hash table matching these patterns are disabled. Disabled objects can be enabled with the **enable** command.\n\n With the option **-p***, name \\... refer to elements of the* shell\\'s pattern syntax as described in the section `Filename Generation`. Certain elements can be disabled separately, as given below.\n\n Note that patterns not allowed by the current settings for the options **EXTENDED_GLOB***,* **KSH_GLOB** *and* **SH_GLOB** *are never enabled, regardless of the* setting here. For example, if **EXTENDED_GLOB** *is not active,* the pattern **\\^** *is ineffective even if `***disable -p \\\"\\^\\\"***` has* not been issued. The list below indicates any option settings that restrict the use of the pattern. It should be noted that setting **SH_GLOB** *has a wider effect than merely disabling patterns* as certain expressions, in particular those involving parentheses, are parsed differently.\n\n The following patterns may be disabled; all the strings need quoting on the command line to prevent them from being interpreted immediately as patterns and the patterns are shown below in single quotes as a reminder.\n\n **\\'?\\'** The pattern character **?** *wherever it occurs, including when preceding* a parenthesis with **KSH_GLOB***.*\n\n **\\'\\*\\'**\n\n : The pattern character **\\*** *wherever it occurs, including recursive* globbing and when preceding a parenthesis with **KSH_GLOB***.*\n\n **\\'\\[\\'**\n\n : Character classes.\n\n **\\'\\<\\'** *(***NO_SH_GLOB***)*\n\n : Numeric ranges.\n\n **\\'\\|\\'** *(***NO_SH_GLOB***)*\n\n : Alternation in grouped patterns, case statements, or KSH_GLOB parenthesised expressions.\n\n **\\'(\\'** *(***NO_SH_GLOB***)*\n\n : Grouping using single parentheses. Disabling this does not disable the use of parentheses for **KSH_GLOB** *where they are introduced by a* special character, nor for glob qualifiers (use `**setopt** NO_BARE_GLOB_QUAL*` to disable glob qualifiers that use parentheses* only).\n\n **\\'\\~\\'** *(***EXTENDED_GLOB***)*\n\n : Exclusion in the form *A***\\~***B.*\n\n **\\'\\^\\'** *(***EXTENDED_GLOB***)*\n\n : Exclusion in the form *A***\\^***B.*\n\n **\\'#\\'** *(***EXTENDED_GLOB***)*\n\n : The pattern character **\\#** *wherever it occurs, both for* repetition of a previous pattern and for indicating globbing flags.\n\n **\\'?(\\'** *(***KSH_GLOB***)*\n\n : The grouping form **?(***\\...***)***. Note this is also* disabled if **\\'?\\'** *is disabled.*\n\n **\\'\\*(\\'** *(***KSH_GLOB***)*\n\n : The grouping form **\\*(***\\...***)***. Note this is also* disabled if **\\'\\*\\'** *is disabled.*\n\n **\\'+(\\'** *(***KSH_GLOB***)*\n\n : The grouping form **+(***\\...***)***.*\n\n **\\'!(\\'** *(***KSH_GLOB***)*\n\n : The grouping form **!(***\\...***)***.*\n\n **\\'@(\\'** *(***KSH_GLOB***)*\n\n : The grouping form **@(***\\...***)***.*", + "args": "disable [ -afmprs ] name ..." + }, + "disown": { + "shortDescription": "Remove job from job table", + "description": "*job \\...* **&\\|**\n\n*job \\...* **&!** Remove the specified *jobs from the job table; the shell will* no longer report their status, and will not complain if you try to exit an interactive shell with them running or stopped. If no *job is specified, disown the current job.*\n\n> If the *jobs are currently stopped and the* **AUTO_CONTINUE** *option* is not set, a warning is printed containing information about how to make them running after they have been disowned. If one of the latter two forms is used, the *jobs will automatically be made running,* independent of the setting of the **AUTO_CONTINUE** *option.*", + "args": "disown [ job ... ]" + }, + "echo": { + "shortDescription": "Write on standard output", + "description": ": Write each *arg on the standard output, with a space separating* each one. If the **-n** *flag is not present, print a newline at the end.* **echo** *recognizes the following escape sequences:*\n\n **\\\\a**\n\n : bell character\n\n **\\\\b**\n\n : backspace\n\n **\\\\c**\n\n : suppress subsequent characters and final newline\n\n **\\\\e**\n\n : escape\n\n **\\\\f**\n\n : form feed\n\n **\\\\n**\n\n : linefeed (newline)\n\n **\\\\r**\n\n : carriage return\n\n **\\\\t**\n\n : horizontal tab\n\n **\\\\v**\n\n : vertical tab\n\n **\\\\\\\\**\n\n : backslash\n\n **\\\\0***NNN*\n\n : character code in octal\n\n **\\\\x***NN*\n\n : character code in hexadecimal\n\n **\\\\u***NNNN*\n\n : unicode character code in hexadecimal\n\n **\\\\U***NNNNNNNN*\n\n : unicode character code in hexadecimal\n\n The **-E** *flag, or the* **BSD_ECHO** *option, can be used to disable* these escape sequences. In the latter case, **-e** *flag can be used to* enable them.\n\n Note that for standards compliance a double dash does not terminate option processing; instead, it is printed directly. However, a single dash does terminate option processing, so the first dash, possibly following options, is not printed, but everything following it is printed as an argument. The single dash behaviour is different from other shells. For a more portable way of printing text, see **printf***, and for a more controllable way of printing text within zsh,* see **print***.*", + "args": "echo [ -neE ] [ arg ... ]" + }, + "echotc": { + "shortDescription": "Echo terminal capabilities", + "description": ": See the section `The zsh/termcap Module` in *zshmodules(1).*", + "args": "echotc" + }, + "echoti": { + "shortDescription": "Echo terminal info", + "description": ": See the section `The zsh/terminfo Module` in *zshmodules(1).*", + "args": "echoti" + }, + "emulate": { + "shortDescription": "Emulate a shell", + "description": ": Without any argument print current emulation mode.\n\n With single argument set up zsh options to emulate the specified shell as much as possible. **csh** *will never be fully emulated.* If the argument is not one of the shells listed above, **zsh** will be used as a default; more precisely, the tests performed on the argument are the same as those used to determine the emulation at startup based on the shell name, see the section COMPATIBILITY in *zsh(1)* . In addition to setting shell options, the command also restores the pristine state of pattern enables, as if all patterns had been enabled using **enable -p***.*\n\n If the **emulate** *command occurs inside a function that has been* marked for execution tracing with **functions -t** *then the* **xtrace** option will be turned on regardless of emulation mode or other options. Note that code executed inside the function by the **.***,* **source***, or* **eval** *commands is not considered to be running directly from the* function, hence does not provoke this behaviour.\n\n If the **-R** *switch is given, all settable options* are reset to their default value corresponding to the specified emulation mode, except for certain options describing the interactive environment; otherwise, only those options likely to cause portability problems in scripts and functions are altered. If the **-L** *switch is given,* the options **LOCAL_OPTIONS***,* **LOCAL_PATTERNS** *and* **LOCAL_TRAPS** will be set as well, causing the effects of the **emulate** *command and any* **setopt***,* **disable -p** *or* **enable -p***, and* **trap** *commands to be local to* the immediately surrounding shell function, if any; normally these options are turned off in all emulation modes except **ksh***. The* **-L** *switch is mutually exclusive with the* use of **-c** *in flags.*\n\n If there is a single argument and the **-l** *switch is given, the* options that would be set or unset (the latter indicated with the prefix `**no***`) are listed.* **-l** *can be combined with* **-L** *or* **-R** *and* the list will be modified in the appropriate way. Note the list does not depend on the current setting of options, i.e. it includes all options that may in principle change, not just those that would actually change.\n\n The *flags may be any of the invocation-time flags described in* the section INVOCATION in *zsh(1),* except that `**-o EMACS***` and `***-o VI***` may not be used. Flags such* as `**+r***`/`***+o RESTRICTED***` may be prohibited in some circumstances.*\n\n If **-c** *arg appears in flags, arg is evaluated while the* requested emulation is temporarily in effect. In this case the emulation mode and all options are restored to their previous values before **emulate** *returns. The* **-R** *switch may precede the name of the shell* to emulate; note this has a meaning distinct from including **-R** *in* *flags.*\n\n Use of **-c** *enables `sticky` emulation mode for functions defined* within the evaluated expression: the emulation mode is associated thereafter with the function so that whenever the function is executed the emulation (respecting the **-R** *switch, if present) and all* options are set (and pattern disables cleared) before entry to the function, and the state is restored after exit. If the function is called when the sticky emulation is already in effect, either within an `**emulate** *shell* **-c***` expression or* within another function with the same sticky emulation, entry and exit from the function do not cause options to be altered (except due to standard processing such as the **LOCAL_OPTIONS** *option). This also* applies to functions marked for autoload within the sticky emulation; the appropriate set of options will be applied at the point the function is loaded as well as when it is run.\n\n For example:\n\n > emulate sh -c 'fni() { setopt cshnullglob; }\n > fno() { fni; }'\n > fno\n\n The two functions **fni** *and* **fno** *are defined with sticky* **sh** emulation. **fno** *is then executed, causing options associated* with emulations to be set to their values in **sh***.* **fno** *then* calls **fni***; because* **fni** *is also marked for sticky* **sh** emulation, no option changes take place on entry to or exit from it. Hence the option **cshnullglob***, turned off by* **sh** *emulation, will* be turned on within **fni** *and remain on return to* **fno***. On exit* from **fno***, the emulation mode and all options will be restored to the* state they were in before entry to the temporary emulation.\n\n The documentation above is typically sufficient for the intended purpose of executing code designed for other shells in a suitable environment. More detailed rules follow.\n\n 1\\.\n\n : The sticky emulation environment provided by `**emulate** *shell* **-c***` is identical to that provided by entry to* a function marked for sticky emulation as a consequence of being defined in such an environment. Hence, for example, the sticky emulation is inherited by subfunctions defined within functions with sticky emulation.\n\n 2\\.\n\n : No change of options takes place on entry to or exit from functions that are not marked for sticky emulation, other than those that would normally take place, even if those functions are called within sticky emulation.\n\n 3\\.\n\n : No special handling is provided for functions marked for **autoload** *nor for functions present in wordcode created by* the **zcompile** *command.*\n\n 4\\.\n\n : The presence or absence of the **-R** *switch to* **emulate** corresponds to different sticky emulation modes, so for example `**emulate sh -c***`, `***emulate -R sh -c***` and `***emulate csh -c***`* are treated as three distinct sticky emulations.\n\n 5\\.\n\n : Difference in shell options supplied in addition to the basic emulation also mean the sticky emulations are different, so for example `**emulate zsh -c***` and `***emulate zsh -o cbases -c***` are* treated as distinct sticky emulations.", + "args": "emulate [ -lLR ] [ {zsh|sh|ksh|csh} [ flags ... ] ]" + }, + "enable": { + "shortDescription": "Enable shell features", + "description": ": Enable the *named hash table elements, presumably disabled* earlier with **disable***. The default is to enable builtin commands.* The **-a** *option causes* **enable** *to act on regular or global aliases.* The **-s** *option causes* **enable** *to act on suffix aliases.* The **-f** *option causes* **enable** *to act on shell functions. The* **-r** option causes **enable** *to act on reserved words. Without arguments* all enabled hash table elements from the corresponding hash table are printed. With the **-m** *flag the arguments are taken as patterns* (should be quoted) and all hash table elements from the corresponding hash table matching these patterns are enabled. Enabled objects can be disabled with the **disable** *builtin command.*\n\n **enable -p** *reenables patterns disabled with* **disable -p***. Note* that it does not override globbing options; for example, `**enable -p** \\\"\\~\\\"*` does not cause the pattern character* **\\~** *to be active unless* the **EXTENDED_GLOB** *option is also set. To enable all possible* patterns (so that they may be individually disabled with **disable -p***),* use `**setopt EXTENDED_GLOB KSH_GLOB NO_SH_GLOB***`.*", + "args": "enable [ -afmprs ] name ..." + }, + "eval": { + "shortDescription": "Execute arguments in shell", + "description": ": Read the arguments as input to the shell and execute the resulting command(s) in the current shell process. The return status is the same as if the commands had been executed directly by the shell; if there are no *args or they contain no commands (i.e. are* an empty string or whitespace) the return status is zero.", + "args": "eval [ arg ... ]" + }, + "exec": { + "shortDescription": "Replace shell with command", + "description": ": Replace the current shell with *command rather than forking.* If *command is a shell builtin command or a shell function,* the shell executes it, and exits when the command is complete.\n\n With **-c** *clear the environment; with* **-l** *prepend* **-** *to the* **argv\\[0\\]** *string of the command executed (to simulate a login shell);* with **-a** *argv0 set the* **argv\\[0\\]** *string of the command* executed. See the section `Precommand Modifiers` in *zshmisc(1).*\n\n If the option **POSIX_BUILTINS** *is set, command is never* interpreted as a shell builtin command or shell function. This means further precommand modifiers such as **builtin** *and* **noglob** *are also not interpreted within the shell. Hence* *command is always found by searching the command path.*\n\n If *command is omitted but any redirections are specified,* then the redirections will take effect in the current shell.", + "args": "exec [ -cl ] [ -a argv0 ] [ command [ arg ... ] ]" + }, + "exit": { + "shortDescription": "Exit the shell", + "description": ": Exit the shell with the exit status specified by an arithmetic expression *n; if none* is specified, use the exit status from the last command executed. An EOF condition will also cause the shell to exit, unless the **IGNORE_EOF** *option is set.*\n\n See notes at the end of the section JOBS in *zshmisc(1) for some possibly unexpected interactions* of the **exit** *command with jobs.*", + "args": "exit [ n ]" + }, + "export": { + "shortDescription": "Export to environment", + "description": ": The specified *names are marked for automatic export* to the environment of subsequently executed commands. Equivalent to **typeset -gx***.* If a parameter specified does not already exist, it is created in the global scope.", + "args": "export [ name[=value] ... ]" + }, + "false": { + "shortDescription": "Return exit status of 1", + "description": ": Do nothing and return an exit status of 1.", + "args": "false [ arg ... ]" + }, + "fc": { + "shortDescription": "Fix command", + "description": "**fc** **-ARWI** *\\[ filename \\]* The **fc** *command controls the interactive history mechanism. Note* that reading and writing of history options is only performed if the shell is interactive. Usually this is detected automatically, but it can be forced by setting the **interactive** *option when starting the* shell.\n\n> The first two forms of this command select a range of events from *first to last from the history list. The arguments first* and *last may be specified as a number or as a string. A negative* number is used as an offset to the current history event number. A string specifies the most recent event beginning with the given string. All substitutions *old***=***new, if any, are then performed on the* text of the events.\n>\n> The range of events selected by numbers can be narrowed further by the following flags.\n>\n> **-I**\n>\n> : restricts to only internal events (not from **\\$HISTFILE***)*\n>\n> **-L**\n>\n> : restricts to only local events (not from other shells, see **SHARE_HISTORY** *in zshoptions(1) \\-- note that* **\\$HISTFILE** *is* considered local when read at startup)\n>\n> **-m**\n>\n> : takes the first argument as a pattern (which should be quoted) and only the history events matching this pattern are considered\n>\n> If *first is not specified, it will be set to -1 (the most recent* event), or to -16 if the **-l** *flag is given.* If *last is not specified, it will be set to first,* or to -1 if the **-l** *flag is given.* However, if the current event has added entries to the history with `**print -s***` or `***fc -R***`, then the default last for* **-l** includes all new history entries since the current event began.\n>\n> When the **-l** *flag is given, the resulting events are listed on* standard output. Otherwise the editor program specified by **-e** *ename* is invoked on a file containing these history events. If **-e** *is not given, the* value of the parameter **FCEDIT** *is used; if that is not set the value of* the parameter **EDITOR** *is used; if that is not set a builtin default,* usually `**vi***` is used. If ename is `***-***`, no editor is invoked.* When editing is complete, the edited command is executed.\n>\n> The flag `**-s***` is equivalent to `***-e -***`.* The flag **-r** *reverses the order of the events and the* flag **-n** *suppresses event numbers when listing.*\n>\n> Also when listing,\n>\n> **-d**\n>\n> : prints timestamps for each event\n>\n> **-f**\n>\n> : prints full time-date stamps in the US `*MM***/***DD***/***YY hh***:***mm` format*\n>\n> **-E**\n>\n> : prints full time-date stamps in the European `*dd***.***mm***.***yyyy hh***:***mm` format*\n>\n> **-i**\n>\n> : prints full time-date stamps in ISO8601 `*yyyy***-***mm***-***dd hh***:***mm` format*\n>\n> **-t** *fmt*\n>\n> : prints time and date stamps in the given format; *fmt is formatted with the strftime function with the zsh extensions* described for the **%D{***string***}** *prompt format in* the section EXPANSION OF PROMPT SEQUENCES in *zshmisc(1). The resulting formatted string must be* no more than 256 characters or will not be printed\n>\n> \n>\n> **-D**\n>\n> : prints elapsed times; may be combined with one of the options above\n>\n> `**fc -p***` pushes the current history list onto a stack and switches to a* new history list. If the **-a** *option is also specified, this history list* will be automatically popped when the current function scope is exited, which is a much better solution than creating a trap function to call `**fc -P***`* manually. If no arguments are specified, the history list is left empty, **\\$HISTFILE** *is unset, and* **\\$HISTSIZE** *&* **\\$SAVEHIST** *are set to their* default values. If one argument is given, **\\$HISTFILE** *is set to that* filename, **\\$HISTSIZE** *&* **\\$SAVEHIST** *are left unchanged, and the history* file is read in (if it exists) to initialize the new list. If a second argument is specified, **\\$HISTSIZE** *&* **\\$SAVEHIST** *are instead set to the* single specified numeric value. Finally, if a third argument is specified, **\\$SAVEHIST** *is set to a separate value from* **\\$HISTSIZE***. You are free to* change these environment values for the new history list however you desire in order to manipulate the new history list.\n>\n> `**fc -P***` pops the history list back to an older list saved by `***fc -p***`.* The current list is saved to its **\\$HISTFILE** *before it is destroyed* (assuming that **\\$HISTFILE** *and* **\\$SAVEHIST** *are set appropriately, of* course). The values of **\\$HISTFILE***,* **\\$HISTSIZE***, and* **\\$SAVEHIST** *are* restored to the values they had when `**fc -p***` was called. Note that this* restoration can conflict with making these variables \\\"local\\\", so your best bet is to avoid local declarations for these variables in functions that use `**fc -p***`. The one other guaranteed-safe combination is declaring these* variables to be local at the top of your function and using the automatic option (**-a***) with `***fc -p***`. Finally, note that it is legal to manually* pop a push marked for automatic popping if you need to do so before the function exits.\n>\n> `**fc -R***` reads the history from the given file,* `**fc -W***` writes the history out to the given file,* and `**fc -A***` appends the history out to the given file.* If no filename is specified, the **\\$HISTFILE** *is assumed.* If the **-I** *option is added to* **-R***, only those events that are* not already contained within the internal history list are added. If the **-I** *option is added to* **-A** *or* **-W***, only those* events that are new since last incremental append/write to the history file are appended/written. In any case, the created file will have no more than **\\$SAVEHIST** entries.", + "args": "fc -P" + }, + "fg": { + "shortDescription": "Put a job in the foreground", + "description": "*job \\...* Bring each specified *job in turn to the foreground.* If no *job is specified, resume the current job.*", + "args": "fg [ job ... ]" + }, + "float": { + "shortDescription": "Floating point arithmetic", + "description": ": Equivalent to **typeset -E***, except that options irrelevant to floating* point numbers are not permitted.", + "args": "float [ {+|-}Hghlprtux ] [ {+|-}EFLRZ [ n ] ] [ name[=value] ... ]" + }, + "functions": { + "shortDescription": "List functions", + "description": "**functions -c** *oldfn newfn*\n\n**functions -M** *\\[***-s***\\] mathfn \\[ min \\[ max \\[ shellfn \\] \\] \\]*\n\n**functions -M** *\\[* **-m** *pattern \\... \\]*\n\n**functions +M** *\\[* **-m** *\\] mathfn \\...* Equivalent to **typeset -f***, with the exception of the* **-c***,* **-x***,* **-M** *and* **-W** *options. For* **functions -u** *and* **functions -U***,* see **autoload***, which provides additional options. For* **functions -t** and **functions -T***, see* **typeset -f***.*\n\n> The **-x** *option indicates that any functions output will have* each leading tab for indentation, added by the shell to show syntactic structure, expanded to the given number *num of spaces. num* can also be 0 to suppress all indentation.\n>\n> The **-W** *option turns on the option* **WARN_NESTED_VAR** *for the named* function or functions only. The option is turned off at the start of nested functions (apart from anonoymous functions) unless the called function also has the **-W** *attribute.*\n>\n> The **-c** *option causes oldfn to be copied to newfn. The* copy is efficiently handled internally by reference counting. If *oldfn was marked for autoload it is first loaded and if this* fails the copy fails. Either function may subsequently be redefined without affecting the other. A typical idiom is that *oldfn is the* name of a library shell function which is then redefined to call **newfn***, thereby installing a modified version of the function.*\n>\n> *The* **-M** *and* **+M** *flags*\n>\n> Use of the **-M** *option may not be combined with any of the options* handled by **typeset -f***.*\n>\n> **functions -M** *mathfn defines mathfn as the name of* a mathematical function recognised in all forms of arithmetical expressions; see the section `Arithmetic Evaluation` in *zshmisc(1). By default mathfn may take* any number of comma-separated arguments. If *min is given,* it must have exactly *min args; if min and max are* both given, it must have at least *min and at most max* args. *max may be -1 to indicate that there is no upper limit.*\n>\n> By default the function is implemented by a shell function of the same name; if *shellfn is specified it gives the name of the corresponding* shell function while *mathfn remains the name used in arithmetical* expressions. The name of the function in **\\$0** *is mathfn (not* *shellfn as would usually be the case), provided the option* **FUNCTION_ARGZERO** *is in effect. The positional parameters in the shell* function correspond to the arguments of the mathematical function call.\n>\n> The result of the last arithmetical expression evaluated inside the shell function gives the result of the mathematical function. This is not limited to arithmetic substitutions of the form **\\$((***\\...***))***,* but also includes arithmetical expressions evaluated in any other way, including by the **let** *builtin,* by **((***\\...***))** *statements,* and even by the **return** *builtin* and by array subscripts. Therefore, care must be taken not to use syntactical constructs that perform arithmetic evaluation after evaluating what is to be the result of the function. For example:\n>\n> > # WRONG\n> > zmath_cube() {\n> > (( $1 * $1 * $1 ))\n> > return 0\n> > }\n> > functions -M cube 1 1 zmath_cube\n> > print $(( cube(3) ))\n>\n> This will print `**0***` because of the* **return***.*\n>\n> Commenting the **return** *out would lead to a different problem: the* **((***\\...***))** *statement would become* the last statement in the function, so the *return status (***\\$?***) of the* function would be non-zero (indicating failure) whenever the *arithmetic* result *of the function would happen to be zero (numerically):*\n>\n> > # WRONG\n> > zmath_cube() {\n> > (( $1 * $1 * $1 ))\n> > }\n> > functions -M cube 1 1 zmath_cube\n> > print $(( cube(0) ))\n>\n> Instead, the **true** *builtin can be used:*\n>\n> > # RIGHT\n> > zmath_cube() {\n> > (( $1 * $1 * $1 ))\n> > true\n> > }\n> > functions -M cube 1 1 zmath_cube\n> > print $(( cube(3) ))\n>\n> If the additional option **-s** *is given to* **functions -M***, the* argument to the function is a single string: anything between the opening and matching closing parenthesis is passed to the function as a single argument, even if it includes commas or white space. The minimum and maximum argument specifiers must therefore be 1 if given. An empty argument list is passed as a zero-length string. Thus, the following string function takes a single argument, including the commas, and prints 11:\n>\n> > stringfn() { (( $#1 )); true }\n> > functions -Ms stringfn\n> > print $(( stringfn(foo,bar,rod) ))\n>\n> **functions -M** *with no arguments lists all such user-defined functions in* the same form as a definition. With the additional option **-m** *and* a list of arguments, all functions whose *mathfn matches one of* the pattern arguments are listed.\n>\n> **function +M** *removes the list of mathematical functions; with the* additional option **-m** *the arguments are treated as patterns and* all functions whose *mathfn matches the pattern are removed. Note* that the shell function implementing the behaviour is not removed (regardless of whether its name coincides with *mathfn).*", + "args": "functions [ {+|-}UkmtTuWz ] [ -x num ] [ name ... ]" + }, + "getcap": { + "shortDescription": "Get capabilities", + "description": ": See the section `The zsh/cap Module` in *zshmodules(1).*", + "args": "getcap" + }, + "getln": { + "shortDescription": "Get line from buffer", + "description": ": Read the top value from the buffer stack and put it in the shell parameter *name. Equivalent to* **read -zr***.*", + "args": "getln [ -AclneE ] name ..." + }, + "getopts": { + "shortDescription": "Parse positional parameters", + "description": ": Checks the *args for legal options. If the args are omitted,* use the positional parameters. A valid option argument begins with a `**+***` or a `***-***`. An argument not beginning with* a `**+***` or a `***-***`, or the argument `***--***`, ends the options.* Note that a single `**-***` is not considered a valid option argument.* *optstring contains the letters that* **getopts** recognizes. If a letter is followed by a `**:***`, that option* requires an argument. The options can be separated from the argument by blanks.\n\n Each time it is invoked, **getopts** *places the option letter it finds* in the shell parameter *name, prepended with a `***+***` when* *arg begins with a `***+***`. The index of the next arg* is stored in **OPTIND***. The option argument, if any,* is stored in **OPTARG***.*\n\n The first option to be examined may be changed by explicitly assigning to **OPTIND***.* **OPTIND** *has an initial value of* **1***, and is* normally set to **1** *upon entry to a shell function and restored* upon exit. (The **POSIX_BUILTINS** *option disables this, and also changes* the way the value is calculated to match other shells.) **OPTARG** is not reset and retains its value from the most recent call to **getopts***. If either of* **OPTIND** *or* **OPTARG** *is explicitly* unset, it remains unset, and the index or option argument is not stored. The option itself is still stored in *name in this case.*\n\n A leading `**:***` in optstring causes* **getopts** *to store the* letter of any invalid option in **OPTARG***, and to set name to* `**?***` for an unknown option and to `***:***` when a required argument is* missing. Otherwise, **getopts** *sets name to `***?***` and prints* an error message when an option is invalid. The exit status is nonzero when there are no more options.", + "args": "getopts optstring name [ arg ... ]" + }, + "hash": { + "shortDescription": "Remember command locations", + "description": ": **hash** *can be used to directly modify the contents of the command* hash table, and the named directory hash table. Normally one would modify these tables by modifying one\\'s **PATH** (for the command hash table) or by creating appropriate shell parameters (for the named directory hash table). The choice of hash table to work on is determined by the **-d** *option;* without the option the command hash table is used, and with the option the named directory hash table is used.\n\n A command *name starting with a* **/** *is never hashed, whether by* explicit use of the **hash** *command or otherwise. Such a command* is always found by direct look up in the file system.\n\n Given no arguments, and neither the **-r** *or* **-f** *options,* the selected hash table will be listed in full.\n\n The **-r** *option causes the selected hash table to be emptied.* It will be subsequently rebuilt in the normal fashion. The **-f** *option causes the selected hash table to be fully* rebuilt immediately. For the command hash table this hashes all the absolute directories in the **PATH***,* and for the named directory hash table this adds all users\\' home directories. These two options cannot be used with any arguments.\n\n The **-m** *option causes the arguments to be taken as patterns* (which should be quoted) and the elements of the hash table matching those patterns are printed. This is the only way to display a limited selection of hash table elements.\n\n For each *name with a corresponding value, put `name` in* the selected hash table, associating it with the pathname `*value`.* In the command hash table, this means that whenever `*name` is used as a command argument, the shell will try* to execute the file given by `*value`.* In the named directory hash table, this means that `*value` may be referred to as `***\\~***name`.*\n\n For each *name with no* corresponding *value, attempt to add name to the hash table,* checking what the appropriate **value** *is in the normal manner for* that hash table. If an appropriate **value** *can\\'t be found, then* the hash table will be unchanged.\n\n The **-v** *option causes hash table entries to be listed as they are* added by explicit specification. If has no effect if used with **-f***.*\n\n If the **-L** *flag is present, then each hash table entry is printed in* the form of a call to hash.", + "args": "hash [ -Ldfmrv ] [ name[=value] ] ..." + }, + "history": { + "shortDescription": "Command history", + "description": ": Same as **fc -l***.*", + "args": "history" + }, + "integer": { + "shortDescription": "Integer arithmetic", + "description": ": Equivalent to **typeset -i***, except that options irrelevant to* integers are not permitted.", + "args": "integer [ {+|-}Hghlprtux ] [ {+|-}LRZi [ n ] ] [ name[=value] ... ]" + }, + "jobs": { + "shortDescription": "List active jobs", + "description": "**jobs -Z** *string* Lists information about each given job, or all jobs if *job is omitted. The* **-l** *flag lists process* IDs, and the **-p** *flag lists process groups.* If the **-r** *flag is specified only running jobs will be listed* and if the **-s** *flag is given only stopped jobs are shown.* If the **-d** *flag is given, the directory from which the job was* started (which may not be the current directory of the job) will also be shown.\n\n> The **-Z** *option replaces the shell\\'s argument and environment space with* the given string, truncated if necessary to fit. This will normally be visible in **ps** *(ps(1)) listings. This feature is typically* used by daemons, to indicate their state.\n>\n> Full job control is only available in the top-level interactive shell, not in commands run in the left hand side of pipelines or within the **(***\\...***)** *construct. However, a snapshot* of the job state at that point is taken, so it is still possible to use the **jobs** *builtin, or any parameter providing job information.* This gives information about the state of jobs at the point the subshell was created. If background processes are created within the subshell, then instead information about those processes is provided.\n>\n> For example,\n>\n> > sleep 10 & # Job in background\n> > ( # Shell forks\n> > jobs # Shows information about \"sleep 10 &\"\n> > sleep 5 & # Process in background (no job control)\n> > jobs # Shows information about \"sleep 5 &\"\n> > )", + "args": "jobs [ -dlprs ] [ job ... ]" + }, + "kill": { + "shortDescription": "Send a signal to a process", + "description": "**kill** **-l** *\\[ sig \\... \\]* Sends either **SIGTERM** *or the specified signal to the given* jobs or processes. Signals are given by number or by names, with or without the `**SIG***`* prefix. If the signal being sent is not `**KILL***` or `***CONT***`, then the job* will be sent a `**CONT***` signal if it is stopped.* The argument *job can be the process ID of a job* not in the job list. In the second form, **kill -l***, if sig is not* specified the signal names are listed. Otherwise, for each *sig that is a name, the corresponding signal number is* listed. For each *sig that is a signal number or a number* representing the exit status of a process which was terminated or stopped by a signal the name of the signal is printed.\n\n> On some systems, alternative signal names are allowed for a few signals. Typical examples are **SIGCHLD** *and* **SIGCLD** *or* **SIGPOLL** *and* **SIGIO***, assuming they correspond to the same signal number.* **kill** -l *will only list the preferred form, however* **kill -l** *alt will* show if the alternative form corresponds to a signal number. For example, under Linux **kill -l IO** *and* **kill -l POLL** *both output 29, hence* **kill -IO** *and* **kill -POLL** *have the same effect.*\n>\n> Many systems will allow process IDs to be negative to kill a process group or zero to kill the current process group.", + "args": "kill [ -s signal_name | -n signal_number | -sig ] job ..." + }, + "let": { + "shortDescription": "Evaluate arithmetic expression", + "description": ": Evaluate each *arg as an arithmetic expression.* See the section `Arithmetic Evaluation` in *zshmisc(1)* for a description of arithmetic expressions. The exit status is 0 if the value of the last expression is nonzero, 1 if it is zero, and 2 if an error occurred.", + "args": "let arg ..." + }, + "limit": { + "shortDescription": "Set or display resource limits", + "description": ": Set or display resource limits. Unless the **-s** *flag is given,* the limit applies only the children of the shell. If **-s** *is* given without other arguments, the resource limits of the current shell is set to the previously set resource limits of the children.\n\n If *limit is not specified, print the current limit placed* on *resource, otherwise* set the limit to the specified value. If the **-h** *flag* is given, use hard limits instead of soft limits. If no *resource is given, print all limits.*\n\n When looping over multiple resources, the shell will abort immediately if it detects a badly formed argument. However, if it fails to set a limit for some other reason it will continue trying to set the remaining limits.\n\n *resource can be one of:*\n\n **addressspace**\n\n : Maximum amount of address space used.\n\n **aiomemorylocked**\n\n : Maximum amount of memory locked in RAM for AIO operations.\n\n **aiooperations**\n\n : Maximum number of AIO operations.\n\n **cachedthreads**\n\n : Maximum number of cached threads.\n\n **coredumpsize**\n\n : Maximum size of a core dump.\n\n **cputime**\n\n : Maximum CPU seconds per process.\n\n **datasize**\n\n : Maximum data size (including stack) for each process.\n\n **descriptors**\n\n : Maximum value for a file descriptor.\n\n **filesize**\n\n : Largest single file allowed.\n\n **kqueues**\n\n : Maximum number of kqueues allocated.\n\n **maxproc**\n\n : Maximum number of processes.\n\n **maxpthreads**\n\n : Maximum number of threads per process.\n\n **memorylocked**\n\n : Maximum amount of memory locked in RAM.\n\n **memoryuse**\n\n : Maximum resident set size.\n\n **msgqueue**\n\n : Maximum number of bytes in POSIX message queues.\n\n **posixlocks**\n\n : Maximum number of POSIX locks per user.\n\n **pseudoterminals**\n\n : Maximum number of pseudo-terminals.\n\n **resident**\n\n : Maximum resident set size.\n\n **sigpending**\n\n : Maximum number of pending signals.\n\n **sockbufsize**\n\n : Maximum size of all socket buffers.\n\n **stacksize**\n\n : Maximum stack size for each process.\n\n **swapsize**\n\n : Maximum amount of swap used.\n\n **vmemorysize**\n\n : Maximum amount of virtual memory.\n\n Which of these resource limits are available depends on the system. *resource can be abbreviated to any unambiguous prefix. It* can also be an integer, which corresponds to the integer defined for the resource by the operating system.\n\n If argument corresponds to a number which is out of the range of the resources configured into the shell, the shell will try to read or write the limit anyway, and will report an error if this fails. As the shell does not store such resources internally, an attempt to set the limit will fail unless the **-s** *option is present.*\n\n *limit is a number, with an optional scaling factor, as follows:*\n\n *n***h**\n\n : hours\n\n *n***k**\n\n : kilobytes (default)\n\n *n***m**\n\n : megabytes or minutes\n\n *n***g**\n\n : gigabytes\n\n \\[*mm***:***\\]ss*\n\n : minutes and seconds\n\n The **limit** *command is not made available by default when the* shell starts in a mode emulating another shell. It can be made available with the command `**zmodload -F zsh/rlimits b:limit***`.*", + "args": "limit [ -hs ] [ resource [ limit ] ] ..." + }, + "local": { + "shortDescription": "Create a local variable", + "description": ": Same as **typeset***, except that the options* **-g***, and* **-f** *are not permitted. In this case the* **-x** *option does not force* the use of **-g***, i.e. exported variables will be local to functions.*", + "args": "local [ {+|-}AHUahlprtux ] [ {+|-}EFLRZi [ n ] ] [ name[=value] ... ]" + }, + "logout": { + "shortDescription": "Exit the shell", + "description": ": Same as **exit***, except that it only works in a login shell.*", + "args": "logout [ n ]" + }, + "noglob": { + "shortDescription": "Disable filename expansion", + "description": ": See the section `Precommand Modifiers` in *zshmisc(1).*", + "args": "noglob simple command" + }, + "popd": { + "shortDescription": "Remove directory from stack", + "description": ": Remove an entry from the directory stack, and perform a **cd** *to* the new top directory. With no argument, the current top entry is removed. An argument of the form `**+***n` identifies a stack* entry by counting from the left of the list shown by the **dirs** *command,* starting with zero. An argument of the form **-***n counts from the right.* If the **PUSHD_MINUS** *option is set, the meanings of `***+***` and* `**-***` in this context are swapped.*\n\n If the **-q** *(quiet) option is specified, the hook function* **chpwd** and the functions in the array **\\$chpwd_functions** *are not called,* and the new directory stack is not printed. This is useful for calls to **popd** *that do not change the environment seen by an interactive user.*", + "args": "popd [ -q ] [ {+|-}n ]" + }, + "print": { + "shortDescription": "Print arguments", + "description": "*\\[* **-v** *name \\] \\[* **-xX** *tabstop \\] \\[* **-R** *\\[* **-en** *\\]\\] \\[ arg \\... \\]* With the `**-f***` option the arguments are printed as described by* **printf***.* With no flags or with the flag `**-***`, the arguments are printed on* the standard output as described by **echo***, with the following differences:* the escape sequence `**\\\\M-***x` (or `***\\\\M***x`) metafies the character* *x (sets the highest bit),* `**\\\\C-***x` (or `***\\\\C***x`) produces a control character* (`**\\\\C-@***` and `***\\\\C-?***` give the characters NULL and delete),* a character code in octal is represented by `**\\\\***NNN`* (instead of `**\\\\0***NNN`),* and `**\\\\E***` is a synonym for `***\\\\e***`.* Finally, if not in an escape sequence, `**\\\\***` escapes the following character and is not printed.*\n\n> **-a** Print arguments with the column incrementing first. Only useful with the **-c** *and* **-C** *options.*\n>\n> **-b**\n>\n> : Recognize all the escape sequences defined for the **bindkey** *command,* see the section `Zle Builtins` in *zshzle(1).*\n>\n> **-c**\n>\n> : Print the arguments in columns. Unless **-a** *is also given, arguments are* printed with the row incrementing first.\n>\n> **-C** *cols*\n>\n> : Print the arguments in *cols columns. Unless* **-a** *is also given,* arguments are printed with the row incrementing first.\n>\n> **-D**\n>\n> : Treat the arguments as paths, replacing directory prefixes with **\\~** expressions corresponding to directory names, as appropriate.\n>\n> **-i**\n>\n> : If given together with **-o** *or* **-O***, sorting is performed* case-independently.\n>\n> **-l**\n>\n> : Print the arguments separated by newlines instead of spaces. Note: if the list of arguments is empty, **print -l** *will still output one empty* line. To print a possibly-empty list of arguments one per line, use **print -C1***, as in `***print -rC1 \\-- \\\"\\$list\\[@\\]\\\"***`.*\n>\n> **-m**\n>\n> : Take the first argument as a pattern (should be quoted), and remove it from the argument list together with subsequent arguments that do not match this pattern.\n>\n> **-n**\n>\n> : Do not add a newline to the output.\n>\n> **-N**\n>\n> : Print the arguments separated and terminated by nulls. Again, **print -rNC1 \\-- \\\"\\$list\\[@\\]\\\"** *is a canonical way to print an* arbitrary list as null-delimited records.\n>\n> **-o**\n>\n> : Print the arguments sorted in ascending order.\n>\n> **-O**\n>\n> : Print the arguments sorted in descending order.\n>\n> **-p**\n>\n> : Print the arguments to the input of the coprocess.\n>\n> **-P**\n>\n> : Perform prompt expansion (see EXPANSION OF PROMPT SEQUENCES in *zshmisc(1)).* In combination with `**-f***`,* prompt escape sequences are parsed only within interpolated arguments, not within the format string.\n>\n> **-r**\n>\n> : Ignore the escape conventions of **echo***.*\n>\n> **-R**\n>\n> : Emulate the BSD **echo** *command, which does not process escape sequences* unless the **-e** *flag is given. The* **-n** *flag suppresses the trailing* newline. Only the **-e** *and* **-n** *flags are recognized after* **-R***; all other arguments and options are printed.*\n>\n> **-s**\n>\n> : Place the results in the history list instead of on the standard output. Each argument to the **print** *command is treated as a single word in the* history, regardless of its content.\n>\n> **-S**\n>\n> : Place the results in the history list instead of on the standard output. In this case only a single argument is allowed; it will be split into words as if it were a full shell command line. The effect is similar to reading the line from a history file with the **HIST_LEX_WORDS** *option active.*\n>\n> **-u** *n*\n>\n> : Print the arguments to file descriptor *n.*\n>\n> **-v** *name*\n>\n> : Store the printed arguments as the value of the parameter *name.*\n>\n> **-x** *tab-stop*\n>\n> : Expand leading tabs on each line of output in the printed string assuming a tab stop every *tab-stop characters. This is appropriate* for formatting code that may be indented with tabs. Note that leading tabs of any argument to print, not just the first, are expanded, even if **print** *is using spaces to separate arguments (the column count* is maintained across arguments but may be incorrect on output owing to previous unexpanded tabs).\n>\n> The start of the output of each print command is assumed to be aligned with a tab stop. Widths of multibyte characters are handled if the option **MULTIBYTE** *is in effect. This option is ignored if other* formatting options are in effect, namely column alignment or **printf** *style, or if output is to a special location such as shell* history or the command line editor.\n>\n> **-X** *tab-stop*\n>\n> : This is similar to **-x***, except that all tabs in the printed string* are expanded. This is appropriate if tabs in the arguments are being used to produce a table format.\n>\n> **-z**\n>\n> : Push the arguments onto the editing buffer stack, separated by spaces.\n>\n> If any of `**-m***`, `***-o***` or `***-O***` are used in combination with* `**-f***` and there are no arguments (after the removal process in the* case of `**-m***`) then nothing is printed.*", + "args": "print [ -abcDilmnNoOpPrsSz ] [ -u n ] [ -f format ] [ -C cols ]" + }, + "printf": { + "shortDescription": "Format and print data", + "description": ": Print the arguments according to the format specification. Formatting rules are the same as used in C. The same escape sequences as for **echo** are recognised in the format. All C conversion specifications ending in one of **csdiouxXeEfgGn** *are handled. In addition to this, `***%b***` can be* used instead of `**%s***` to cause escape sequences in the argument to be* recognised and `**%q***` can be used to quote the argument in such a way* that allows it to be reused as shell input. With the numeric format specifiers, if the corresponding argument starts with a quote character, the numeric value of the following character is used as the number to print; otherwise the argument is evaluated as an arithmetic expression. See the section `Arithmetic Evaluation` in *zshmisc(1)* for a description of arithmetic expressions. With `**%n***`, the corresponding argument is taken as an* identifier which is created as an integer parameter.\n\n Normally, conversion specifications are applied to each argument in order but they can explicitly specify the *nth argument is to be used by* replacing `**%***` by `***%***n***\\$***` and `***\\****` by `***\\****n***\\$***`.* It is recommended that you do not mix references of this explicit style with the normal style and the handling of such mixed styles may be subject to future change.\n\n If arguments remain unused after formatting, the format string is reused until all arguments have been consumed. With the **print** *builtin, this* can be suppressed by using the **-r** *option. If more arguments are* required by the format than have been specified, the behaviour is as if zero or an empty string had been specified as the argument.\n\n The **-v** *option causes the output to be stored as the value of the* parameter *name, instead of printed. If name is an array and* the format string is reused when consuming arguments then one array element will be used for each use of the format string.", + "args": "printf [ -v name ] format [ arg ... ]" + }, + "pushd": { + "shortDescription": "Add directory to stack", + "description": "> The third form of **pushd** *changes directory by rotating the* directory list. An argument of the form `**+***n` identifies a stack* entry by counting from the left of the list shown by the **dirs** command, starting with zero. An argument of the form `**-***n` counts* from the right. If the **PUSHD_MINUS** *option is set, the meanings* of `**+***` and `***-***` in this context are swapped.*\n>\n> If the **-q** *(quiet) option is specified, the hook function* **chpwd** and the functions in the array **\\$chpwd_functions** *are not called,* and the new directory stack is not printed. This is useful for calls to **pushd** *that do not change the environment seen by an interactive user.*\n>\n> If the option **-q** *is not specified and the shell option* **PUSHD_SILENT** is not set, the directory stack will be printed after a **pushd** *is* performed.\n>\n> The options **-s***,* **-L** *and* **-P** *have the same meanings as for the* **cd** *builtin.*", + "args": "pushd [ -qsLP ] [ arg ]\n\npushd [ -qsLP ] old new\n\npushd [ -qsLP ] {+|-}n Change the current directory, and push the old\ncurrent directory onto the directory stack. In the first form, change\nthe current directory to arg. If arg is not specified, change to the\nsecond directory on the stack (that is, exchange the top two entries),\nor change to $HOME if the PUSHD_TO_HOME option is set or if there is\nonly one entry on the stack. Otherwise, arg is interpreted as it would\nbe by cd. The meaning of old and new in the second form is also the same\nas for cd." + }, + "pushln": { + "shortDescription": "Push arguments onto the buffer", + "description": ": Equivalent to **print -nz***.*", + "args": "pushln [ arg ... ]" + }, + "pwd": { + "shortDescription": "Print working directory", + "description": ": Print the absolute pathname of the current working directory. If the **-r** *or the* **-P** *flag is specified, or the* **CHASE_LINKS** option is set and the **-L** *flag is not given, the printed path will not* contain symbolic links.", + "args": "pwd [ -rLP ]" + }, + "r": { + "shortDescription": "Re-execute command", + "description": ": Same as **fc -e -***.*", + "args": "r" + }, + "read": { + "shortDescription": "Read a line from input", + "description": "*\\[* **-u** *n \\] \\[ \\[name\\]\\[***?***prompt\\] \\] \\[ name \\... \\]* Read one line and break it into fields using the characters in **\\$IFS** *as separators, except as noted below.* The first field is assigned to the first *name, the second field* to the second *name, etc., with leftover* fields assigned to the last *name.* If *name is omitted then* **REPLY** *is used for scalars and* **reply** *for arrays.*\n\n> **-r** Raw mode: a `**\\\\***` at the end of a line does not signify line* continuation and backslashes in the line don\\'t quote the following character and are not removed.\n>\n> **-s**\n>\n> : Don\\'t echo back characters if reading from the terminal.\n>\n> **-q**\n>\n> : Read only one character from the terminal and set *name to* `**y***` if this character was `***y***` or `***Y***` and to `***n***` otherwise.* With this flag set the return status is zero only if the character was `**y***` or `***Y***`. This option may be used with a timeout (see* **-t***); if* the read times out, or encounters end of file, status 2 is returned. Input is read from the terminal unless one of **-u** *or* **-p** *is present. This option* may also be used within zle widgets.\n>\n> **-k** *\\[ num \\]*\n>\n> : Read only one (or *num) characters. All are assigned to the first* *name, without word splitting. This flag is ignored when* **-q** *is* present. Input is read from the terminal unless one of **-u** *or* **-p** is present. This option may also be used within zle widgets.\n>\n> Note that despite the mnemonic `key` this option does read full characters, which may consist of multiple bytes if the option **MULTIBYTE** *is set.*\n>\n> **-z**\n>\n> : Read one entry from the editor buffer stack and assign it to the first *name, without word splitting. Text is pushed onto the stack with* `**print -z***` or with* **push-line** *from the line editor (see* *zshzle(1)). This flag is ignored when the* **-k** *or* **-q** *flags are present.*\n>\n> **-e**\n>\n> **-E** The input read is printed (echoed) to the standard output. If the **-e** flag is used, no input is assigned to the parameters.\n>\n> **-A**\n>\n> : The first *name is taken as the name of an array and all words are* assigned to it.\n>\n> **-c**\n>\n> **-l** These flags are allowed only if called inside a function used for completion (specified with the **-K** *flag to* **compctl***). If the* **-c** *flag is given, the words of the* current command are read. If the **-l** *flag is given, the whole* line is assigned as a scalar. If both flags are present, **-l** is used and **-c** *is ignored.*\n>\n> **-n**\n>\n> : Together with **-c***, the number of the word the cursor is on is* read. With **-l***, the index of the character the cursor is on is* read. Note that the command name is word number 1, not word 0, and that when the cursor is at the end of the line, its character index is the length of the line plus one.\n>\n> **-u** *n*\n>\n> : Input is read from file descriptor *n.*\n>\n> **-p**\n>\n> : Input is read from the coprocess.\n>\n> **-d** *delim*\n>\n> : Input is terminated by the first character of *delim instead of* by newline.\n>\n> **-t** *\\[ num \\]*\n>\n> : Test if input is available before attempting to read. If *num* is present, it must begin with a digit and will be evaluated to give a number of seconds, which may be a floating point number; in this case the read times out if input is not available within this time. If *num is not present, it is taken to be zero, so that* **read** *returns immediately if no input is available.* If no input is available, return status 1 and do not set any variables. This option is not available when reading from the editor buffer with **-z***, when called from within completion with* **-c** *or* **-l***, with* **-q** *which clears the input queue before reading, or within zle where* other mechanisms should be used to test for input. Note that read does not attempt to alter the input processing mode. The default mode is canonical input, in which an entire line is read at a time, so usually `**read -t***` will not read anything until an entire line has* been typed. However, when reading from the terminal with **-k** input is processed one key at a time; in this case, only availability of the first character is tested, so that e.g. `**read -t -k 2***` can still* block on the second character. Use two instances of `**read -t -k***` if* this is not what is wanted.\n>\n> If the first argument contains a `**?***`, the remainder of this* word is used as a *prompt on standard error when the shell* is interactive.\n>\n> The value (exit status) of **read** *is 1 when an end-of-file is* encountered, or when **-c** *or* **-l** *is present and the command is* not called from a **compctl** *function, or as described for* **-q***.* Otherwise the value is 0.\n>\n> The behavior of some combinations of the **-k***,* **-p***,* **-q***,* **-u** and **-z** *flags is undefined. Presently* **-q** *cancels all the others,* **-p** *cancels* **-u***,* **-k** *cancels* **-z***, and otherwise* **-z** cancels both **-p** *and* **-u***.*\n>\n> The **-c** *or* **-l** *flags cancel any and all of* **-kpquz***.*", + "args": "read [ -rszpqAclneE ] [ -t [ num ] ] [ -k [ num ] ] [ -d delim ]" + }, + "readonly": { + "shortDescription": "Mark variables as read-only", + "description": ": Same as **typeset -r***. With the* **POSIX_BUILTINS** *option set, same* as **typeset -gr***.*", + "args": "readonly" + }, + "rehash": { + "shortDescription": "Recompute command hash table", + "description": ": Same as **hash -r***.*", + "args": "rehash" + }, + "return": { + "shortDescription": "Return from a function", + "description": ": Causes a shell function or `**.***` script to return to* the invoking script with the return status specified by an arithmetic expression *n.* For example, the following prints `**42***`:*\n\n > () { integer foo=40; return \"foo + 2\" }\n > echo $?\n\n If *n* is omitted, the return status is that of the last command executed.\n\n If **return** *was executed from a trap in a* **TRAP***NAL function,* the effect is different for zero and non-zero return status. With zero status (or after an implicit return at the end of the trap), the shell will return to whatever it was previously processing; with a non-zero status, the shell will behave as interrupted except that the return status of the trap is retained. Note that the numeric value of the signal which caused the trap is passed as the first argument, so the statement `**return \\\"128+\\$1\\\"***` will return the same status as if the signal* had not been trapped.", + "args": "return [ n ]" + }, + "sched": { + "shortDescription": "Schedule commands", + "description": ": See the section `The zsh/sched Module` in *zshmodules(1).*", + "args": "sched" + }, + "set": { + "shortDescription": "Set shell options", + "description": "*\\[ arg \\... \\]* Set the options for the shell and/or set the positional parameters, or declare and set an array. If the **-s** *option is given, it causes the* specified arguments to be sorted before assigning them to the positional parameters (or to the array *name if* **-A** *is used). With* **+s** sort arguments in descending order. For the meaning of the other flags, see *zshoptions(1). Flags may be specified by name using the* **-o** *option. If no option* name is supplied with **-o***, the current option states are printed: see* the description of **setopt** *below for more information on the format.* With **+o** *they are printed in a form that can be used as input* to the shell.\n\n> If the **-A** *flag is specified, name is set to an array containing* the given *args; if no name is specified, all arrays are printed* together with their values.\n>\n> If **+A** *is used and name is an array, the* given arguments will replace the initial elements of that array; if no *name is specified, all arrays are printed without their values.*\n>\n> The behaviour of arguments after **-A** *name or* **+A** *name* depends on whether the option **KSH_ARRAYS** *is set. If it is not set, all* arguments following *name are treated as values for the array,* regardless of their form. If the option is set, normal option processing continues at that point; only regular arguments are treated as values for the array. This means that\n>\n> > set -A array -x -- foo\n>\n> sets **array** *to `***-x -- foo***` if* **KSH_ARRAYS** *is not set, but sets* the array to **foo** *and turns on the option `***-x***` if it is set.*\n>\n> If the **-A** *flag is not present, but there are arguments beyond the* options, the positional parameters are set. If the option list (if any) is terminated by `**--***`, and there are no further arguments, the* positional parameters will be unset.\n>\n> If no arguments and no `**--***` are given, then the names and values of* all parameters are printed on the standard output. If the only argument is `**+***`, the names of all parameters are printed.*\n>\n> For historical reasons, `**set -***` is treated as `***set +xv***`* and `**set -** *args` as `***set +xv \\--** *args` when in* any other emulation mode than zsh\\'s native mode.", + "args": "set [ {+|-}options | {+|-}o [ option_name ] ] ... [ {+|-}A [ name ] ]" + }, + "setcap": { + "shortDescription": "Set capabilities", + "description": ": See the section `The zsh/cap Module` in *zshmodules(1).*", + "args": "setcap" + }, + "setopt": { + "shortDescription": "Set shell options", + "description": ": Set the options for the shell. All options specified either with flags or by name are set.\n\n If no arguments are supplied, the names of all options currently set are printed. The form is chosen so as to minimize the differences from the default options for the current emulation (the default emulation being native **zsh***, shown as* **\\** *in* *zshoptions(1)).* Options that are on by default for the emulation are shown with the prefix **no** *only if they are off, while other options are* shown without the prefix **no** *and only if they are on. In addition to* options changed from the default state by the user, any options activated automatically by the shell (for example, **SHIN_STDIN** *or* **INTERACTIVE***)* will be shown in the list. The format is further modified by the option **KSH_OPTION_PRINT***, however the rationale for choosing options with* or without the **no** *prefix remains the same in this case.*\n\n If the **-m** *flag is given the arguments are taken as patterns* (which should be quoted to protect them from filename expansion), and all options with names matching these patterns are set.\n\n Note that a bad option name does not cause execution of subsequent shell code to be aborted; this is behaviour is different from that of `**set** -o*`. This is because* **set** *is regarded as a special builtin by the* POSIX standard, but **setopt** *is not.*", + "args": "setopt [ {+|-}options | {+|-}o option_name ] [ -m ] [ name ... ]" + }, + "shift": { + "shortDescription": "Shift positional parameters", + "description": ": The positional parameters **\\${***n+1***}** *\\... are renamed* to **\\$1** *\\..., where n is an arithmetic expression that* defaults to 1. If any *names are given then the arrays with these names are* shifted instead of the positional parameters.\n\n If the option **-p** *is given arguments are instead removed (popped)* from the end rather than the start of the array.", + "args": "shift [ -p ] [ n ] [ name ... ]" + }, + "source": { + "shortDescription": "Source a file", + "description": ": Same as `**.***`, except that the current directory is always searched and* is always searched first, before directories in **\\$path***.*", + "args": "source file [ arg ... ]" + }, + "stat": { + "shortDescription": "Display file status", + "description": ": See the section `The zsh/stat Module` in *zshmodules(1).*", + "args": "stat" + }, + "suspend": { + "shortDescription": "Suspend the shell", + "description": ": Suspend the execution of the shell (send it a **SIGTSTP***)* until it receives a **SIGCONT***.* Unless the **-f** *option is given, this will refuse to suspend a login shell.*", + "args": "suspend [ -f ]" + }, + "test": { + "shortDescription": "Evaluate a conditional expression", + "description": "**\\[** *\\[ arg \\... \\]* **\\]** Like the system version of **test***. Added for compatibility;* use conditional expressions instead (see the section `Conditional Expressions`). The main differences between the conditional expression syntax and the **test** *and* **\\[** *builtins are: these commands are not handled* syntactically, so for example an empty variable expansion may cause an argument to be omitted; syntax errors cause status 2 to be returned instead of a shell error; and arithmetic operators expect integer arguments rather than arithmetic expressions.\n\n> The command attempts to implement POSIX and its extensions where these are specified. Unfortunately there are intrinsic ambiguities in the syntax; in particular there is no distinction between test operators and strings that resemble them. The standard attempts to resolve these for small numbers of arguments (up to four); for five or more arguments compatibility cannot be relied on. Users are urged wherever possible to use the `**\\[\\[***` test syntax which does not have these ambiguities.*", + "args": "test [ arg ... ]" + }, + "times": { + "shortDescription": "Display shell times", + "description": ": Print the accumulated user and system times for the shell and for processes run from the shell.", + "args": "times" + }, + "trap": { + "shortDescription": "Set signal handlers", + "description": ": *arg is a series of commands (usually quoted to protect it from* immediate evaluation by the shell) to be read and executed when the shell receives any of the signals specified by one or more *sig args.* Each *sig can be given as a number,* or as the name of a signal either with or without the string **SIG** in front (e.g. 1, HUP, and SIGHUP are all the same signal).\n\n If *arg is `***-***`, then the specified signals are reset to their* defaults, or, if no *sig args are present, all traps are reset.*\n\n If *arg is an empty string, then the specified signals* are ignored by the shell (and by the commands it invokes).\n\n If *arg is omitted but one or more sig args are provided (i.e.* the first argument is a valid signal number or name), the effect is the same as if *arg had been specified as `***-***`.*\n\n The **trap** *command with no arguments prints a list of commands* associated with each signal.\n\n If *sig is* **ZERR** *then arg will be executed* after each command with a nonzero exit status. **ERR** *is an alias* for **ZERR** *on systems that have no* **SIGERR** *signal (this is the* usual case).\n\n If *sig is* **DEBUG** *then arg will be executed* before each command if the option **DEBUG_BEFORE_CMD** *is set* (as it is by default), else after each command. Here, a `command` is what is described as a `sublist` in the shell grammar, see the section SIMPLE COMMANDS & PIPELINES in *zshmisc(1).* If **DEBUG_BEFORE_CMD** *is set various additional features are available.* First, it is possible to skip the next command by setting the option **ERR_EXIT***; see the description of the* **ERR_EXIT** *option in* *zshoptions(1). Also, the shell parameter* **ZSH_DEBUG_CMD** *is set to the string corresponding to the command* to be executed following the trap. Note that this string is reconstructed from the internal format and may not be formatted the same way as the original text. The parameter is unset after the trap is executed.\n\n If *sig is* **0** *or* **EXIT** and the **trap** *statement is executed inside the body of a function,* then the command *arg is executed after the function completes.* The value of **\\$?** *at the start of execution is the exit status of the* shell or the return status of the function exiting. If *sig is* **0** *or* **EXIT** and the **trap** *statement is not executed inside the body of a function,* then the command *arg is executed when the shell terminates; the* trap runs before any **zshexit** *hook functions.*\n\n **ZERR***,* **DEBUG***, and* **EXIT** *traps are not executed inside other* traps. **ZERR** *and* **DEBUG** *traps are kept within subshells, while* other traps are reset.\n\n Note that traps defined with the **trap** *builtin are slightly different* from those defined as `**TRAP***NAL () { \\... }`, as the latter have* their own function environment (line numbers, local variables, etc.) while the former use the environment of the command in which they were called. For example,\n\n > trap 'print $LINENO' DEBUG\n\n will print the line number of a command executed after it has run, while\n\n > TRAPDEBUG() { print $LINENO; }\n\n will always print the number zero.\n\n Alternative signal names are allowed as described under **kill** *above.* Defining a trap under either name causes any trap under an alternative name to be removed. However, it is recommended that for consistency users stick exclusively to one name or another.", + "args": "trap [ arg ] [ sig ... ]" + }, + "true": { + "shortDescription": "Return exit status of 0", + "description": ": Do nothing and return an exit status of 0.", + "args": "true [ arg ... ]" + }, + "ttyctl": { + "shortDescription": "Control terminal attributes", + "description": ": The **-f** *option freezes the tty (i.e. terminal or terminal emulator), and* **-u** *unfreezes it.* When the tty is frozen, no changes made to the tty settings by external programs will be honored by the shell, except for changes in the size of the screen; the shell will simply reset the settings to their previous values as soon as each command exits or is suspended. Thus, **stty** *and similar programs have* no effect when the tty is frozen. Freezing the tty does not cause the current state to be remembered: instead, it causes future changes to the state to be blocked.\n\n Without options it reports whether the terminal is frozen or not.\n\n Note that, regardless of whether the tty is frozen or not, the shell needs to change the settings when the line editor starts, so unfreezing the tty does not guarantee settings made on the command line are preserved. Strings of commands run between editing the command line will see a consistent tty state. See also the shell variable **STTY** *for a means of initialising* the tty before running external commands and/or freezing the tty around a single command.", + "args": "ttyctl [ -fu ]" + }, + "type": { + "shortDescription": "Describe a command", + "description": ": Equivalent to **whence -v***.*", + "args": "type [ -wfpamsS ] name ..." + }, + "typeset": { + "shortDescription": "Set or display parameter attributes/values", + "description": "*\\[* **+** *\\| SCALAR\\[***=***value\\] array\\[***=(***value \\...***)***\\] \\[ sep \\] \\]*\n\n**typeset** **-f** *\\[ {***+***\\|***-***}***TUkmtuz** *\\] \\[* **+** *\\] \\[ name \\... \\]* Set or display attributes and values for shell parameters.\n\n> Except as noted below for control flags that change the behavior, a parameter is created for each *name that does not already refer* to one. When inside a function, a new parameter is created for every *name (even those that already exist), and is unset again when the* function completes. See `Local Parameters` in *zshparam(1). The same rules apply to special shell parameters, which* retain their special attributes when made local.\n>\n> For each *name***=***value assignment, the parameter* *name is set to value. If the assignment is omitted and name* does *not refer to an existing parameter, a new parameter is intialized* to empty string, zero, or empty array (as appropriate), *unless the* shell option **TYPESET_TO_UNSET** *is set. When that option is set,* the parameter attributes are recorded but the parameter remains unset.\n>\n> If the shell option **TYPESET_SILENT** *is not set, for each remaining* *name that refers to a parameter that is already set, the name and* value of the parameter are printed in the form of an assignment. Nothing is printed for newly-created parameters, or when any attribute flags listed below are given along with the *name. Using* `**+***` instead of minus to introduce an attribute turns it off.*\n>\n> If no *name is present, the names and values of all parameters are* printed. In this case the attribute flags restrict the display to only those parameters that have the specified attributes, and using `**+***`* rather than `**-***` to introduce the flag suppresses printing of the values* of parameters when there is no parameter name.\n>\n> All forms of the command handle scalar assignment. Array assignment is possible if any of the reserved words **declare***,* **export***,* **float***,* **integer***,* **local***,* **readonly** *or* **typeset** *is matched when the* line is parsed (N.B. not when it is executed). In this case the arguments are parsed as assignments, except that the `**+=***` syntax and the* **GLOB_ASSIGN** *option are not supported, and scalar values after* **=** are *not split further into words, even if expanded (regardless of the* setting of the **KSH_TYPESET** *option; this option is obsolete).*\n>\n> Examples of the differences between command and reserved word parsing:\n>\n> > # Reserved word parsing\n> > typeset svar=$(echo one word) avar=(several words)\n>\n> The above creates a scalar parameter **svar** *and an array* parameter **avar** *as if the assignments had been*\n>\n> > svar=\"one word\"\n> > avar=(several words)\n>\n> On the other hand:\n>\n> > # Normal builtin interface\n> > builtin typeset svar=$(echo two words)\n>\n> The **builtin** *keyword causes the above to use the standard builtin* interface to **typeset** *in which argument parsing is performed in the same* way as for other commands. This example creates a scalar **svar** containing the value **two** *and another scalar parameter* **words** *with* no value. An array value in this case would either cause an error or be treated as an obscure set of glob qualifiers.\n>\n> Arbitrary arguments are allowed if they take the form of assignments after command line expansion; however, these only perform scalar assignment:\n>\n> > var='svar=val'\n> > typeset $var\n>\n> The above sets the scalar parameter **svar** *to the value* **val***.* Parentheses around the value within **var** *would not cause array* assignment as they will be treated as ordinary characters when **\\$var** is substituted. Any non-trivial expansion in the name part of the assignment causes the argument to be treated in this fashion:\n>\n> > typeset {var1,var2,var3}=name\n>\n> The above syntax is valid, and has the expected effect of setting the three parameters to the same value, but the command line is parsed as a set of three normal command line arguments to **typeset** *after* expansion. Hence it is not possible to assign to multiple arrays by this means.\n>\n> Note that each interface to any of the commands may be disabled separately. For example, `**disable -r typeset***` disables the reserved* word interface to **typeset***, exposing the builtin interface, while* `**disable typeset***` disables the builtin. Note that disabling the* reserved word interface for **typeset** *may cause problems with the* output of `**typeset -p***`, which assumes the reserved word interface is* available in order to restore array and associative array values.\n>\n> Unlike parameter assignment statements, **typeset***\\'s exit status on an* assignment that involves a command substitution does not reflect the exit status of the command substitution. Therefore, to test for an error in a command substitution, separate the declaration of the parameter from its initialization:\n>\n> > # WRONG\n> > typeset var1=$(exit 1) || echo \"Trouble with var1\"\n> >\n> > # RIGHT\n> > typeset var1 && var1=$(exit 1) || echo \"Trouble with var1\"\n>\n> To initialize a parameter *param to a command output and mark it readonly,* use **typeset -r** *param or* **readonly** *param after the parameter* assignment statement.\n>\n> If no attribute flags are given, and either no *name arguments are* present or the flag **+m** *is used, then each parameter name printed is* preceded by a list of the attributes of that parameter (**array***,* **association***,* **exported***,* **float***,* **integer***,* **readonly***,* or **undefined** *for autoloaded parameters not yet loaded). If* **+m** *is* used with attribute flags, and all those flags are introduced with **+***, the matching parameter names are printed but their values* are not.\n>\n> The following control flags change the behavior of **typeset***:*\n>\n> **+** If `**+***` appears by itself in a separate word as the last option,* then the names of all parameters (functions with **-f***) are printed, but* the values (function bodies) are not. No *name arguments may appear,* and it is an error for any other options to follow `**+***`. The* effect of `**+***` is as if all attribute flags which precede it were* given with a `**+***` prefix. For example, `***typeset -U +***` is* equivalent to `**typeset +U***` and displays the names of all arrays having* the uniqueness attribute, whereas `**typeset -f -U +***` displays the* names of all autoloadable functions. If **+** *is the only option,* then type information (array, readonly, etc.) is also printed for each parameter, in the same manner as `**typeset +m \\\"\\*\\\"***`.*\n>\n> **-g**\n>\n> : The **-g** *(global) means that any resulting parameter will not be* restricted to local scope. Note that this does not necessarily mean that the parameter will be global, as the flag will apply to any existing parameter (even if unset) from an enclosing function. This flag does not affect the parameter after creation, hence it has no effect when listing existing parameters, nor does the flag **+g** *have any effect except in* combination with **-m** *(see below).*\n>\n> **-m**\n>\n> : If the **-m** *flag is given the name arguments are taken as patterns* (use quoting to prevent these from being interpreted as file patterns). With no attribute flags, all parameters (or functions with the **-f** flag) with matching names are printed (the shell option **TYPESET_SILENT** is not used in this case).\n>\n> If the **+g** *flag is combined with* **-m***, a new local parameter is* created for every matching parameter that is not already local. Otherwise **-m** *applies all other flags or assignments to the existing parameters.*\n>\n> Except when assignments are made with *name***=***value, using* **+m** *forces the matching parameters and their attributes to be printed,* even inside a function. Note that **-m** *is ignored if no patterns are* given, so `**typeset -m***` displays attributes but `***typeset -a +m***`* does not.\n>\n> **-p** *\\[ n \\]*\n>\n> : If the **-p** *option is given, parameters and values are printed in the* form of a typeset command with an assignment, regardless of other flags and options. Note that the **-H** *flag on parameters is respected; no* value will be shown for these parameters.\n>\n> **-p** *may be followed by an optional integer argument. Currently* only the value **1** *is supported. In this case arrays and associative* arrays are printed with newlines between indented elements for readability.\n>\n> **-T** *\\[ scalar\\[***=***value\\] array\\[***=(***value \\...***)***\\] \\[ sep \\] \\]*\n>\n> : This flag has a different meaning when used with **-f***; see below.* Otherwise the **-T** *option requires zero, two, or three arguments to be* present. With no arguments, the list of parameters created in this fashion is shown. With two or three arguments, the first two are the name of a scalar and of an array parameter (in that order) that will be tied together in the manner of **\\$PATH** *and* **\\$path***. The optional third* argument is a single-character separator which will be used to join the elements of the array to form the scalar; if absent, a colon is used, as with **\\$PATH***. Only the first character of the separator is significant;* any remaining characters are ignored. Multibyte characters are not yet supported.\n>\n> Only one of the scalar and array parameters may be assigned an initial value (the restrictions on assignment forms described above also apply).\n>\n> Both the scalar and the array may be manipulated as normal. If one is unset, the other will automatically be unset too. There is no way of untying the variables without unsetting them, nor of converting the type of one of them with another **typeset** *command;* **+T** *does not work,* assigning an array to *scalar is an error, and assigning a scalar to* *array sets it to be a single-element array.*\n>\n> Note that both `**typeset -xT \\...***` and `***export -T \\...***` work, but* only the scalar will be marked for export. Setting the value using the scalar version causes a split on all separators (which cannot be quoted). It is possible to apply **-T** *to two previously tied variables but with a* different separator character, in which case the variables remain joined as before but the separator is changed.\n>\n> When an existing scalar is tied to a new array, the value of the scalar is preserved but no attribute other than export will be preserved.\n>\n> Attribute flags that transform the final value (**-L***,* **-R***,* **-Z***,* **-l***,* **-u***) are only applied to the expanded value at the point* of a parameter expansion expression using `**\\$***`. They are not applied* when a parameter is retrieved internally by the shell for any purpose.\n>\n> The following attribute flags may be specified:\n>\n> **-A** The names refer to associative array parameters; see `Array Parameters` in *zshparam(1).*\n>\n> **-L** *\\[ n \\]*\n>\n> : Left justify and remove leading blanks from the value when the parameter is expanded. If *n is nonzero, it defines the width of the field.* If *n is zero, the width is determined by the width of the value of* the first assignment. In the case of numeric parameters, the length of the complete value assigned to the parameter is used to determine the width, not the value that would be output.\n>\n> The width is the count of characters, which may be multibyte characters if the **MULTIBYTE** *option is in effect. Note that the screen* width of the character is not taken into account; if this is required, use padding with parameter expansion flags **\\${(ml***\\...***)***\\...***}** *as described in* `Parameter Expansion Flags` in *zshexpn(1).*\n>\n> When the parameter is expanded, it is filled on the right with blanks or truncated if necessary to fit the field. Note truncation can lead to unexpected results with numeric parameters. Leading zeros are removed if the **-Z** *flag is also set.*\n>\n> **-R** *\\[ n \\]*\n>\n> : Similar to **-L***, except that right justification is used;* when the parameter is expanded, the field is left filled with blanks or truncated from the end. May not be combined with the **-Z** flag.\n>\n> **-U**\n>\n> : For arrays (but not for associative arrays), keep only the first occurrence of each duplicated value. This may also be set for tied parameters (see **-T***) or colon-separated special parameters like* **PATH** *or* **FIGNORE***, etc. Note the flag takes effect on assignment,* and the type of the variable being assigned to is determinative; for variables with shared values it is therefore recommended to set the flag for all interfaces, e.g. `**typeset -U PATH path***`.*\n>\n> This flag has a different meaning when used with **-f***; see below.*\n>\n> **-Z** *\\[ n \\]*\n>\n> : Specially handled if set along with the **-L** *flag.* Otherwise, similar to **-R***, except that leading zeros are used for* padding instead of blanks if the first non-blank character is a digit. Numeric parameters are specially handled: they are always eligible for padding with zeroes, and the zeroes are inserted at an appropriate place in the output.\n>\n> **-a**\n>\n> : The names refer to array parameters. An array parameter may be created this way, but it may be assigned to in the **typeset** statement only if the reserved word form of **typeset** *is enabled* (as it is by default). When displaying, both normal and associative arrays are shown.\n>\n> **-f**\n>\n> : The names refer to functions rather than parameters. No assignments can be made, and the only other valid flags are **-t***,* **-T***,* **-k***,* **-u***,* **-U** *and* **-z***. The flag* **-t** *turns on execution tracing* for this function; the flag **-T** *does the same, but turns off tracing* for any named (not anonymous) function called from the present one, unless that function also has the **-t** *or* **-T** *flag. The* **-u** *and* **-U** *flags cause the* function to be marked for autoloading; **-U** *also causes alias* expansion to be suppressed when the function is loaded. See the description of the `**autoload***` builtin for details.*\n>\n> Note that the builtin **functions** *provides the same basic capabilities* as **typeset -f** *but gives access to a few extra options;* **autoload** gives further additional options for the case **typeset -fu** *and* **typeset -fU***.*\n>\n> **-h**\n>\n> : Hide: only useful for special parameters (those marked `\\` in the table in *zshparam(1)), and for local parameters with the same name as a special parameter,* though harmless for others. A special parameter with this attribute will not retain its special effect when made local. Thus after `**typeset -h** PATH*`, a function containing `***typeset PATH***` will create an ordinary* local parameter without the usual behaviour of **PATH***. Alternatively,* the local parameter may itself be given this attribute; hence inside a function `**typeset -h PATH***` creates an ordinary local parameter and the* special **PATH** *parameter is not altered in any way. It is also possible* to create a local parameter using `**typeset +h** *special`, where the* local copy of *special will retain its special properties regardless of* having the **-h** *attribute. Global special parameters loaded from shell* modules (currently those in **zsh/mapfile** *and* **zsh/parameter***) are* automatically given the **-h** *attribute to avoid name clashes.*\n>\n> **-H**\n>\n> : Hide value: specifies that **typeset** *will not display the value of the* parameter when listing parameters; the display for such parameters is always as if the `**+***` flag had been given. Use of the parameter is* in other respects normal, and the option does not apply if the parameter is specified by name, or by pattern with the **-m** *option. This is on by* default for the parameters in the **zsh/parameter** *and* **zsh/mapfile** modules. Note, however, that unlike the **-h** *flag this is also useful* for non-special parameters.\n>\n> **-i** *\\[ n \\]*\n>\n> : Use an internal integer representation. If *n is nonzero it* defines the output arithmetic base, otherwise it is determined by the first assignment. Bases from 2 to 36 inclusive are allowed.\n>\n> **-E** *\\[ n \\]*\n>\n> : Use an internal double-precision floating point representation. On output the variable will be converted to scientific notation. If *n is* nonzero it defines the number of significant figures to display; the default is ten.\n>\n> **-F** *\\[ n \\]*\n>\n> : Use an internal double-precision floating point representation. On output the variable will be converted to fixed-point decimal notation. If *n* is nonzero it defines the number of digits to display after the decimal point; the default is ten.\n>\n> **-l**\n>\n> : Convert the result to lower case whenever the parameter is expanded. The value is *not converted when assigned.*\n>\n> **-r**\n>\n> : The given *names are marked readonly. Note that if name is a* special parameter, the readonly attribute can be turned on, but cannot then be turned off.\n>\n> If the **POSIX_BUILTINS** *option is set, the readonly attribute is* more restrictive: unset variables can be marked readonly and cannot then be set; furthermore, the readonly attribute cannot be removed from any variable.\n>\n> It is still possible to change other attributes of the variable though, some of which like **-U** *or* **-Z** *would affect the value. More generally,* the readonly attribute should not be relied on as a security mechanism.\n>\n> Note that in zsh (like in pdksh but unlike most other shells) it is still possible to create a local variable of the same name as this is considered a different variable (though this variable, too, can be marked readonly). Special variables that have been made readonly retain their value and readonly attribute when made local.\n>\n> **-t**\n>\n> : Tags the named parameters. Tags have no special meaning to the shell. This flag has a different meaning when used with **-f***; see above.*\n>\n> **-u**\n>\n> : Convert the result to upper case whenever the parameter is expanded. The value is *not converted when assigned.* This flag has a different meaning when used with **-f***; see above.*\n>\n> **-x**\n>\n> : Mark for automatic export to the environment of subsequently executed commands. If the option **GLOBAL_EXPORT** *is set, this implies* the option **-g***, unless* **+g** *is also explicitly given; in other words* the parameter is not made local to the enclosing function. This is for compatibility with previous versions of zsh.", + "args": "typeset -T [ {+|-}Uglrux ] [ {+|-}LRZp [ n ] ]" + }, + "ulimit": { + "shortDescription": "Set or display resource limits", + "description": ": Set or display resource limits of the shell and the processes started by the shell. The value of *limit can be a number in the unit specified* below or one of the values `**unlimited***`, which removes the limit on the* resource, or `**hard***`, which uses the current value of the hard limit on* the resource.\n\n By default, only soft limits are manipulated. If the **-H** *flag* is given use hard limits instead of soft limits. If the **-S** *flag is given* together with the **-H** *flag set both hard and soft limits.*\n\n If no options are used, the file size limit (**-f***) is assumed.*\n\n If *limit is omitted the current value of the specified resources are* printed. When more than one resource value is printed, the limit name and unit is printed before each value.\n\n When looping over multiple resources, the shell will abort immediately if it detects a badly formed argument. However, if it fails to set a limit for some other reason it will continue trying to set the remaining limits.\n\n Not all the following resources are supported on all systems. Running **ulimit -a** *will show which are supported.*\n\n **-a**\n\n : Lists all of the current resource limits.\n\n **-b**\n\n : Socket buffer size in bytes (N.B. not kilobytes)\n\n **-c**\n\n : 512-byte blocks on the size of core dumps.\n\n **-d**\n\n : Kilobytes on the size of the data segment.\n\n **-f**\n\n : 512-byte blocks on the size of files written.\n\n **-i**\n\n : The number of pending signals.\n\n **-k**\n\n : The number of kqueues allocated.\n\n **-l**\n\n : Kilobytes on the size of locked-in memory.\n\n **-m**\n\n : Kilobytes on the size of physical memory.\n\n **-n**\n\n : open file descriptors.\n\n **-p**\n\n : The number of pseudo-terminals.\n\n **-q**\n\n : Bytes in POSIX message queues.\n\n **-r**\n\n : Maximum real time priority. On some systems where this is not available, such as NetBSD, this has the same effect as **-T** for compatibility with **sh***.*\n\n **-s**\n\n : Kilobytes on the size of the stack.\n\n **-T**\n\n : The number of simultaneous threads available to the user.\n\n **-t**\n\n : CPU seconds to be used.\n\n **-u**\n\n : The number of processes available to the user.\n\n **-v**\n\n : Kilobytes on the size of virtual memory. On some systems this refers to the limit called `address space`.\n\n **-w**\n\n : Kilobytes on the size of swapped out memory.\n\n **-x**\n\n : The number of locks on files.\n\n A resource may also be specified by integer in the form `**-N** *resource`, where resource corresponds to the integer defined for* the resource by the operating system. This may be used to set the limits for resources known to the shell which do not correspond to option letters. Such limits will be shown by number in the output of `**ulimit -a***`.*\n\n The number may alternatively be out of the range of limits compiled into the shell. The shell will try to read or write the limit anyway, and will report an error if this fails.", + "args": "ulimit [ -HSa ] [ { -bcdfiklmnpqrsTtvwx | -N resource } [ limit ] ... ]" + }, + "umask": { + "shortDescription": "Set file creation mask", + "description": ": The umask is set to *mask. mask can be either* an octal number or a symbolic value as described in the *chmod(1) man page.* If *mask is omitted, the current value is printed. The* **-S** option causes the mask to be printed as a symbolic value. Otherwise, the mask is printed as an octal number. Note that in the symbolic form the permissions you specify are those which are to be allowed (not denied) to the users specified.", + "args": "umask [ -S ] [ mask ]" + }, + "unalias": { + "shortDescription": "Removes aliases", + "description": ": Removes aliases. This command works the same as **unhash -a***, except that* the **-a** *option removes all regular or global aliases, or with* **-s** all suffix aliases: in this case no *name arguments may appear. The* options **-m** *(remove by pattern) and* **-s** *without* **-a** *(remove* listed suffix aliases) behave as for **unhash -a***. Note that* the meaning of **-a** *is different between* **unalias** *and* **unhash***.*", + "args": "unalias [ -ams ] name ..." + }, + "unfunction": { + "shortDescription": "Remove function definition", + "description": ": Same as **unhash -f***.*", + "args": "unfunction" + }, + "unhash": { + "shortDescription": "Remove command from hash table", + "description": ": Remove the element named *name from an internal hash table. The* default is remove elements from the command hash table. The **-a** option causes **unhash** *to remove regular or global aliases; note* when removing a global aliases that the argument must be quoted to prevent it from being expanded before being passed to the command. The **-s** *option causes* **unhash** *to remove suffix aliases.* The **-f** *option causes* **unhash** *to remove shell functions. The* **-d** *options causes* **unhash** *to remove named directories. If the* **-m** *flag is given* the arguments are taken as patterns (should be quoted) and all elements of the corresponding hash table with matching names will be removed.", + "args": "unhash [ -adfms ] name ..." + }, + "unlimit": { + "shortDescription": "Remove resource limits", + "description": ": The resource limit for each *resource is set to the hard limit.* If the **-h** *flag is given and the shell has appropriate privileges,* the hard resource limit for each *resource is removed.* The resources of the shell process are only changed if the **-s** flag is given.\n\n The **unlimit** *command is not made available by default when the* shell starts in a mode emulating another shell. It can be made available with the command `**zmodload -F zsh/rlimits b:unlimit***`.*", + "args": "unlimit [ -hs ] resource ..." + }, + "unset": { + "shortDescription": "Unset values and attributes of variables", + "description": ": Each named parameter is unset. Local parameters remain local even if unset; they appear unset within scope, but the previous value will still reappear when the scope ends.\n\n Individual elements of associative array parameters may be unset by using subscript syntax on *name, which should be quoted (or the entire command* prefixed with **noglob***) to protect the subscript from filename generation.*\n\n If the **-m** *flag is specified the arguments are taken as patterns (should* be quoted) and all parameters with matching names are unset. Note that this cannot be used when unsetting associative array elements, as the subscript will be treated as part of the pattern.\n\n The **-v** *flag specifies that name refers to parameters. This is the* default behaviour.\n\n **unset -f** *is equivalent to* **unfunction***.*", + "args": "unset [ -fmv ] name ..." + }, + "unsetopt": { + "shortDescription": "Unset shell options", + "description": ": Unset the options for the shell. All options specified either with flags or by name are unset. If no arguments are supplied, the names of all options currently unset are printed. If the **-m** *flag is given the arguments are taken as patterns* (which should be quoted to preserve them from being interpreted as glob patterns), and all options with names matching these patterns are unset.", + "args": "unsetopt [ {+|-}options | {+|-}o option_name ] [ name ... ]" + }, + "vared": { + "shortDescription": "Edit shell variables", + "description": ": See the section `Zle Builtins` in *zshzle(1).*", + "args": "vared" + }, + "wait": { + "shortDescription": "Wait for a process", + "description": ": Wait for the specified jobs or processes. If *job is not given* then all currently active child processes are waited for. Each *job can be either a job specification or the process ID* of a job in the job table. The exit status from this command is that of the job waited for. If *job represents an unknown job or process ID, a warning is printed* (unless the **POSIX_BUILTINS** *option is set) and the exit status is 127.*\n\n It is possible to wait for recent processes (specified by process ID, not by job) that were running in the background even if the process has exited. Typically the process ID will be recorded by capturing the value of the variable **\\$!** *immediately after the process has been* started. There is a limit on the number of process IDs remembered by the shell; this is given by the value of the system configuration parameter **CHILD_MAX***. When this limit is reached, older process IDs* are discarded, least recently started processes first.\n\n Note there is no protection against the process ID wrapping, i.e. if the wait is not executed soon enough there is a chance the process waited for is the wrong one. A conflict implies both process IDs have been generated by the shell, as other processes are not recorded, and that the user is potentially interested in both, so this problem is intrinsic to process IDs.", + "args": "wait [ job ... ]" + }, + "whence": { + "shortDescription": "Locate a command", + "description": ": For each *name, indicate how it would be interpreted if used as a* command name.\n\n If *name is not an alias, built-in command, external command, shell* function, hashed command, or a reserved word, the exit status shall be non-zero, and \\-- if **-v***,* **-c***, or* **-w** *was passed \\-- a message* will be written to standard output. (This is different from other shells that write that message to standard error.)\n\n **whence** *is most useful when name is only the last path component* of a command, i.e. does not include a `**/***`; in particular, pattern* matching only succeeds if just the non-directory component of the command is passed.\n\n **-v** Produce a more verbose report.\n\n **-c**\n\n : Print the results in a **csh***-like format.* This takes precedence over **-v***.*\n\n **-w**\n\n : For each *name, print `name***:** *word` where word* is one of **alias***,* **builtin***,* **command***,* **function***,* **hashed***,* **reserved** *or* **none***, according as name* corresponds to an alias, a built-in command, an external command, a shell function, a command defined with the **hash** *builtin, a* reserved word, or is not recognised. This takes precedence over **-v** *and* **-c***.*\n\n **-f**\n\n : Causes the contents of a shell function to be displayed, which would otherwise not happen unless the **-c** flag were used.\n\n **-p**\n\n : Do a path search for *name* even if it is an alias, reserved word, shell function or builtin.\n\n **-a**\n\n : Do a search for all occurrences of *name* throughout the command path. Normally only the first occurrence is printed.\n\n **-m**\n\n : The arguments are taken as patterns (pattern characters should be quoted), and the information is displayed for each command matching one of these patterns.\n\n **-s**\n\n : If a pathname contains symlinks, print the symlink-free pathname as well.\n\n **-S**\n\n : As **-s***, but if the pathname had to be resolved by following* multiple symlinks, the intermediate steps are printed, too. The symlink resolved at each step might be anywhere in the path.\n\n **-x** *num*\n\n : Expand tabs when outputting shell functions using the **-c** *option.* This has the same effect as the **-x** *option to the* **functions** builtin.", + "args": "whence [ -vcwfpamsS ] [ -x num ] name ..." + }, + "where": { + "shortDescription": "Locate a command", + "description": ": Equivalent to **whence -ca***.*", + "args": "where [ -wpmsS ] [ -x num ] name ..." + }, + "which": { + "shortDescription": "Locate a command", + "description": ": Equivalent to **whence -c***.*", + "args": "which [ -wpamsS ] [ -x num ] name ..." + }, + "zcompile": { + "shortDescription": "Compile functions", + "description": "**zcompile -t** *file \\[ name \\... \\]* This builtin command can be used to compile functions or scripts, storing the compiled form in a file, and to examine files containing the compiled form. This allows faster autoloading of functions and sourcing of scripts by avoiding parsing of the text when the files are read.\n\n> The first form (without the **-c***,* **-a** *or* **-t** *options) creates a* compiled file. If only the *file argument is given, the* output file has the name `*file***.zwc***` and will be placed in* the same directory as the *file. The shell will load the compiled* file instead of the normal function file when the function is autoloaded; see the section `Autoloading Functions` in *zshmisc(1)* for a description of how autoloaded functions are searched. The extension **.zwc** *stands for `zsh word code`.*\n>\n> If there is at least one *name argument, all the named files* are compiled into the output *file given as the first argument. If* *file does not end in* **.zwc***, this extension is automatically* appended. Files containing multiple compiled functions are called `digest` files, and are intended to be used as elements of the **FPATH***/***fpath** special array.\n>\n> The second form, with the **-c** *or* **-a** *options, writes the compiled* definitions for all the named functions into *file. For* **-c***, the* names must be functions currently defined in the shell, not those marked for autoloading. Undefined functions that are marked for autoloading may be written by using the **-a** *option, in which case the* **fpath** is searched and the contents of the definition files for those functions, if found, are compiled into *file. If both* **-c** *and* **-a** *are given, names of both defined functions and functions marked* for autoloading may be given. In either case, the functions in files written with the **-c** *or* **-a** *option will be autoloaded as if the* **KSH_AUTOLOAD** *option were unset.*\n>\n> The reason for handling loaded and not-yet-loaded functions with different options is that some definition files for autoloading define multiple functions, including the function with the same name as the file, and, at the end, call that function. In such cases the output of `**zcompile -c***` does not include the additional functions defined in* the file, and any other initialization code in the file is lost. Using `**zcompile -a***` captures all this extra information.*\n>\n> If the **-m** *option is combined with* **-c** *or* **-a***,* the *names are used as patterns and all functions whose names* match one of these patterns will be written. If no *name is given,* the definitions of all functions currently defined or marked as autoloaded will be written.\n>\n> Note the second form cannot be used for compiling functions that include redirections as part of the definition rather than within the body of the function; for example\n>\n> > fn1() { { ... } >~/logfile }\n>\n> can be compiled but\n>\n> > fn1() { ... } >~/logfile\n>\n> cannot. It is possible to use the first form of **zcompile** *to compile* autoloadable functions that include the full function definition instead of just the body of the function.\n>\n> The third form, with the **-t** *option, examines an existing* compiled file. Without further arguments, the names of the original files compiled into it are listed. The first line of output shows the version of the shell which compiled the file and how the file will be used (i.e. by reading it directly or by mapping it into memory). With arguments, nothing is output and the return status is set to zero if definitions for *all names were found in the compiled* file, and non-zero if the definition for at least one *name was not* found.\n>\n> Other options:\n>\n> **-U** Aliases are not expanded when compiling the *named files.*\n>\n> **-R**\n>\n> : When the compiled file is read, its contents are copied into the shell\\'s memory, rather than memory-mapped (see **-M***). This* happens automatically on systems that do not support memory mapping.\n>\n> When compiling scripts instead of autoloadable functions, it is often desirable to use this option; otherwise the whole file, including the code to define functions which have already been defined, will remain mapped, consequently wasting memory.\n>\n> **-M**\n>\n> : The compiled file is mapped into the shell\\'s memory when read. This is done in such a way that multiple instances of the shell running on the same host will share this mapped file. If neither **-R** *nor* **-M** *is given, the* **zcompile** *builtin decides what to do based* on the size of the compiled file.\n>\n> **-k**\n>\n> **-z** These options are used when the compiled file contains functions which are to be autoloaded. If **-z** *is given, the* function will be autoloaded as if the **KSH_AUTOLOAD** *option is* *not set, even if it is set at the time the compiled file is* read, while if the **-k** *is given, the function will be loaded as if* **KSH_AUTOLOAD** *is set. These options also take precedence over* any **-k** *or* **-z** *options specified to the* **autoload** *builtin. If* neither of these options is given, the function will be loaded as determined by the setting of the **KSH_AUTOLOAD** *option at the time* the compiled file is read. These options may also appear as many times as necessary between the listed *names to specify the loading style of all following functions, up to* the next **-k** *or* **-z***.*\n>\n> The created file always contains two versions of the compiled format, one for big-endian machines and one for small-endian machines. The upshot of this is that the compiled file is machine independent and if it is read or mapped, only one half of the file is actually used (and mapped).", + "args": "zcompile [ -U ] [ -z | -k ] [ -R | -M ] file [ name ... ]\n\nzcompile -ca [ -m ] [ -R | -M ] file [ name ... ]" + }, + "zformat": { + "shortDescription": "Format strings", + "description": ": See the section `The zsh/zutil Module` in *zshmodules(1).*", + "args": "zformat" + }, + "zftp": { + "shortDescription": "Zsh FTP client", + "description": ": See the section `The zsh/zftp Module` in *zshmodules(1).*", + "args": "zftp" + }, + "zle": { + "shortDescription": "Zsh line editor", + "description": ": See the section `Zle Builtins` in *zshzle(1).*", + "args": "zle" + }, + "zmodload": { + "shortDescription": "Load a module", + "description": "**zmodload** **-R** *modalias \\...* Performs operations relating to zsh\\'s loadable modules. Loading of modules while the shell is running (`dynamical loading`) is not available on all operating systems, or on all installations on a particular operating system, although the **zmodload** *command itself is always* available and can be used to manipulate modules built into versions of the shell executable without dynamical loading.\n\n> Without arguments the names of all currently loaded binary modules are printed. The **-L** *option causes this list to be in the form of a* series of **zmodload** *commands. Forms with arguments are:*\n>\n> **zmodload** *\\[* **-is** *\\] name \\...*\n>\n> **zmodload** **-u** *\\[* **-i** *\\] name \\...* In the simplest case, **zmodload** *loads a binary module. The module must* be in a file with a name consisting of the specified *name followed by* a standard suffix, usually `**.so***` (`***.sl***` on HPUX).* If the module to be loaded is already loaded the duplicate module is ignored. If **zmodload** *detects an inconsistency, such as an* invalid module name or circular dependency list, the current code block is aborted. If it is available, the module is loaded if necessary, while if it is not available, non-zero status is silently returned. The option **-i** *is accepted for compatibility but has no effect.*\n>\n> > The *named module is searched for in the same way a command is, using* **\\$module_path** *instead of* **\\$path***. However, the path search is* performed even when the module name contains a `**/***`, which it usually does.* There is no way to prevent the path search.\n> >\n> > If the module supports features (see below), **zmodload** *tries to* enable all features when loading a module. If the module was successfully loaded but not all features could be enabled, **zmodload** *returns status 2.*\n> >\n> > If the option **-s** *is given, no error is printed if the module was not* available (though other errors indicating a problem with the module are printed). The return status indicates if the module was loaded. This is appropriate if the caller considers the module optional.\n> >\n> > With **-u***,* **zmodload** *unloads modules. The same name* must be given that was given when the module was loaded, but it is not necessary for the module to exist in the file system. The **-i** *option suppresses the error if the module is already* unloaded (or was never loaded).\n> >\n> > Each module has a boot and a cleanup function. The module will not be loaded if its boot function fails. Similarly a module can only be unloaded if its cleanup function runs successfully.\n>\n> **zmodload -F** *\\[* **-almLe** **-P** *param \\] module \\[ \\[***+-***\\]feature \\... \\]*\n>\n> : **zmodload -F** *allows more selective control over the features provided* by modules. With no options apart from **-F***, the module named* *module is loaded, if it was not already loaded, and the list of* *features is set to the required state. If no* *features are specified, the module is loaded, if it was not already* loaded, but the state of features is unchanged. Each feature may be preceded by a **+** *to turn the feature on, or* **-** *to turn it* off; the **+** *is assumed if neither character is present.* Any feature not explicitly mentioned is left in its current state; if the module was not previously loaded this means any such features will remain disabled. The return status is zero if all features were set, 1 if the module failed to load, and 2 if some features could not be set (for example, a parameter couldn\\'t be added because there was a different parameter of the same name) but the module was loaded.\n>\n> The standard features are builtins, conditions, parameters and math functions; these are indicated by the prefix `**b:***`, `***c:***`* (`**C:***` for an infix condition), `***p:***` and `***f:***`, respectively,* followed by the name that the corresponding feature would have in the shell. For example, `**b:strftime***` indicates a builtin named* **strftime** *and* **p:EPOCHSECONDS** *indicates a parameter named* **EPOCHSECONDS***. The module may provide other (`abstract`) features of* its own as indicated by its documentation; these have no prefix.\n>\n> With **-l** *or* **-L***, features provided by the module are listed. With* **-l** *alone, a list of features together with their states is shown, one* feature per line. With **-L** *alone, a* **zmodload -F** *command that would* cause enabled features of the module to be turned on is shown. With **-lL***, a* **zmodload -F** *command that would cause all the features to be* set to their current state is shown. If one of these combinations is given with the option **-P** *param then the parameter param is set to an* array of features, either features together with their state or (if **-L** *alone is given) enabled features.*\n>\n> With the option **-L** *the module name may be omitted; then a list* of all enabled features for all modules providing features is printed in the form of **zmodload -F** *commands. If* **-l** *is also given,* the state of both enabled and disabled features is output in that form.\n>\n> A set of features may be provided together with **-l** *or* **-L** *and a* module name; in that case only the state of those features is considered. Each feature may be preceded by **+** *or* **-** *but the* character has no effect. If no set of features is provided, all features are considered.\n>\n> With **-e***, the command first tests that the module is loaded;* if it is not, status 1 is returned. If the module is loaded, the list of features given as an argument is examined. Any feature given with no prefix is simply tested to see if the module provides it; any feature given with a prefix **+** *or* **-** *is tested to* see if is provided and in the given state. If the tests on all features in the list succeed, status 0 is returned, else status 1.\n>\n> With **-m***, each entry in the given list of features is taken* as a pattern to be matched against the list of features provided by the module. An initial **+** *or* **-** *must be given explicitly.* This may not be combined with the **-a** *option as autoloads must* be specified explicitly.\n>\n> With **-a***, the given list of features is marked for autoload from* the specified module, which may not yet be loaded. An optional **+** may appear before the feature name. If the feature is prefixed with **-***, any existing autoload is removed. The options* **-l** *and* **-L** may be used to list autoloads. Autoloading is specific to individual features; when the module is loaded only the requested feature is enabled. Autoload requests are preserved if the module is subsequently unloaded until an explicit `**zmodload -Fa** *module* **-***feature` is issued. It is not an error to request an autoload* for a feature of a module that is already loaded.\n>\n> When the module is loaded each autoload is checked against the features actually provided by the module; if the feature is not provided the autoload request is deleted. A warning message is output; if the module is being loaded to provide a different feature, and that autoload is successful, there is no effect on the status of the current command. If the module is already loaded at the time when **zmodload -Fa** *is* run, an error message is printed and status 1 returned.\n>\n> **zmodload -Fa** *can be used with the* **-l***,* **-L***,* **-e** *and* **-P** *options for listing and testing the existence of autoloadable* features. In this case **-l** *is ignored if* **-L** *is specified.* **zmodload -FaL** *with no module name lists autoloads for all modules.*\n>\n> Note that only standard features as described above can be autoloaded; other features require the module to be loaded before enabling.\n>\n> **zmodload** **-d** *\\[* **-L** *\\] \\[ name \\]*\n>\n> **zmodload** **-d** *name dep \\...*\n>\n> **zmodload** **-ud** *name \\[ dep \\... \\]* The **-d** *option can be used to specify module dependencies. The modules* named in the second and subsequent arguments will be loaded before the module named in the first argument.\n>\n> > With **-d** *and one argument, all dependencies for that module are listed.* With **-d** *and no arguments, all module dependencies are listed. This* listing is by default in a Makefile-like format. The **-L** *option* changes this format to a list of **zmodload -d** *commands.*\n> >\n> > If **-d** *and* **-u** *are both used, dependencies are removed. If only one* argument is given, all dependencies for that module are removed.\n>\n> **zmodload** **-ab** *\\[* **-L** *\\]*\n>\n> **zmodload** **-ab** *\\[* **-i** *\\] name \\[ builtin \\... \\]*\n>\n> **zmodload** **-ub** *\\[* **-i** *\\] builtin \\...* The **-ab** *option defines autoloaded builtins. It defines the specified* *builtins. When any of those builtins is called, the module specified* in the first argument is loaded and all its features are enabled (for selective control of features use `**zmodload -F -a***` as described* above). If only the *name is given, one builtin is defined, with* the same name as the module. **-i** *suppresses the error if the builtin* is already defined or autoloaded, but not if another builtin of the same name is already defined.\n>\n> > With **-ab** *and no arguments, all autoloaded builtins are listed, with the* module name (if different) shown in parentheses after the builtin name. The **-L** *option changes this format to a list of* **zmodload -a** commands.\n> >\n> > If **-b** *is used together with the* **-u** *option, it removes builtins* previously defined with **-ab***. This is only possible if the builtin is* not yet loaded. **-i** *suppresses the error if the builtin is already* removed (or never existed).\n> >\n> > Autoload requests are retained if the module is subsequently unloaded until an explicit `**zmodload -ub** *builtin` is issued.*\n>\n> **zmodload** **-ac** *\\[* **-IL** *\\]*\n>\n> **zmodload** **-ac** *\\[* **-iI** *\\] name \\[ cond \\... \\]*\n>\n> **zmodload** **-uc** *\\[* **-iI** *\\] cond \\...* The **-ac** *option is used to define autoloaded condition codes. The* *cond strings give the names of the conditions defined by the* module. The optional **-I** *option is used to define infix condition* names. Without this option prefix condition names are defined.\n>\n> > If given no condition names, all defined names are listed (as a series of **zmodload** *commands if the* **-L** *option is given).*\n> >\n> > The **-uc** *option removes definitions for autoloaded conditions.*\n>\n> **zmodload** **-ap** *\\[* **-L** *\\]*\n>\n> **zmodload** **-ap** *\\[* **-i** *\\] name \\[ parameter \\... \\]*\n>\n> **zmodload** **-up** *\\[* **-i** *\\] parameter \\...* The **-p** *option is like the* **-b** *and* **-c** *options, but makes* **zmodload** *work on autoloaded parameters instead.*\n>\n> **zmodload** **-af** *\\[* **-L** *\\]*\n>\n> **zmodload** **-af** *\\[* **-i** *\\] name \\[ function \\... \\]*\n>\n> **zmodload** **-uf** *\\[* **-i** *\\] function \\...* The **-f** *option is like the* **-b***,* **-p***, and* **-c** *options, but* makes **zmodload** *work on autoloaded math functions instead.*\n>\n> **zmodload** **-a** *\\[* **-L** *\\]*\n>\n> **zmodload** **-a** *\\[* **-i** *\\] name \\[ builtin \\... \\]*\n>\n> **zmodload** **-ua** *\\[* **-i** *\\] builtin \\...* Equivalent to **-ab** *and* **-ub***.*\n>\n> **zmodload -e** *\\[* **-A** *\\] \\[ string \\... \\]*\n>\n> : The **-e** *option without arguments lists all loaded modules; if the* **-A** option is also given, module aliases corresponding to loaded modules are also shown. If arguments are provided, nothing is printed; the return status is set to zero if all *strings given as arguments* are names of loaded modules and to one if at least on *string is not* the name of a loaded module. This can be used to test for the availability of things implemented by modules. In this case, any aliases are automatically resolved and the **-A** *flag is not used.*\n>\n> **zmodload** **-A** *\\[* **-L** *\\] \\[ modalias\\[***=***module\\] \\... \\]*\n>\n> : For each argument, if both *modalias and module are given,* define *modalias to be an alias for the module module.* If the module *modalias is ever subsequently requested, either via a* call to **zmodload** *or implicitly, the shell will attempt to load* *module instead. If module is not given, show the definition of* *modalias. If no arguments are given, list all defined module aliases.* When listing, if the **-L** *flag was also given, list the definition as a* **zmodload** *command to recreate the alias.*\n>\n> The existence of aliases for modules is completely independent of whether the name resolved is actually loaded as a module: while the alias exists, loading and unloading the module under any alias has exactly the same effect as using the resolved name, and does not affect the connection between the alias and the resolved name which can be removed either by **zmodload -R** *or by redefining the alias. Chains of aliases (i.e. where* the first resolved name is itself an alias) are valid so long as these are not circular. As the aliases take the same format as module names, they may include path separators: in this case, there is no requirement for any part of the path named to exist as the alias will be resolved first. For example, `**any/old/alias***` is always a valid alias.*\n>\n> Dependencies added to aliased modules are actually added to the resolved module; these remain if the alias is removed. It is valid to create an alias whose name is one of the standard shell modules and which resolves to a different module. However, if a module has dependencies, it will not be possible to use the module name as an alias as the module will already be marked as a loadable module in its own right.\n>\n> Apart from the above, aliases can be used in the **zmodload** *command* anywhere module names are required. However, aliases will not be shown in lists of loaded modules with a bare `**zmodload***`.*\n>\n> **zmodload** **-R** *modalias \\...* \n>\n> : For each *modalias argument that was previously defined as a module* alias via **zmodload -A***, delete the alias. If any was not defined, an* error is caused and the remainder of the line is ignored.\n>\n> Note that **zsh** *makes no distinction between modules that were linked* into the shell and modules that are loaded dynamically. In both cases this builtin command has to be used to make available the builtins and other things defined by modules (unless the module is autoloaded on these definitions). This is true even for systems that don\\'t support dynamic loading of modules.", + "args": "zmodload [ -a [ -bcpf [ -I ] ] ] [ -iL ] ...\n\nzmodload -u [ -abcdpf [ -I ] ] [ -iL ] ...\n\nzmodload -A [ -L ] [ modalias[=module] ... ]" + }, + "zparseopts": { + "shortDescription": "Parse options", + "description": ": See the section `The zsh/zutil Module` in *zshmodules(1).*", + "args": "zparseopts" + }, + "zprof": { + "shortDescription": "Zsh profiler", + "description": ": See the section `The zsh/zprof Module` in *zshmodules(1).*", + "args": "zprof" + }, + "zpty": { + "shortDescription": "Zsh pseudo terminal", + "description": ": See the section `The zsh/zpty Module` in *zshmodules(1).*", + "args": "zpty" + }, + "zregexparse": { + "shortDescription": "Parse regex", + "description": ": See the section `The zsh/zutil Module` in *zshmodules(1).*", + "args": "zregexparse" + }, + "zsocket": { + "shortDescription": "Zsh socket interface", + "description": ": See the section `The zsh/net/socket Module` in *zshmodules(1).*", + "args": "zsocket" + }, + "zstyle": { + "shortDescription": "Define styles", + "description": ": See the section `The zsh/zutil Module` in *zshmodules(1).*", + "args": "zstyle" + }, + "ztcp": { + "shortDescription": "Manipulate TCP sockets", + "description": ": See the section `The zsh/net/tcp Module` in *zshmodules(1).*", + "args": "ztcp" + } +} as const; \ No newline at end of file diff --git a/extensions/terminal-suggest/src/terminalSuggestMain.ts b/extensions/terminal-suggest/src/terminalSuggestMain.ts index 08965a0e58da..ba26dc44035f 100644 --- a/extensions/terminal-suggest/src/terminalSuggestMain.ts +++ b/extensions/terminal-suggest/src/terminalSuggestMain.ts @@ -10,6 +10,8 @@ import * as vscode from 'vscode'; import cdSpec from './completions/cd'; import codeCompletionSpec from './completions/code'; import codeInsidersCompletionSpec from './completions/code-insiders'; +import npxCompletionSpec from './completions/npx'; +import setLocationSpec from './completions/set-location'; import { upstreamSpecs } from './constants'; import { PathExecutableCache } from './env/pathExecutableCache'; import { osIsWindows } from './helpers/os'; @@ -22,22 +24,15 @@ import { getTokenType, TokenType } from './tokens'; import type { ICompletionResource } from './types'; import { createCompletionItem } from './helpers/completionItem'; import { getFigSuggestions } from './fig/figInterface'; +import { executeCommand, executeCommandTimeout, IFigExecuteExternals } from './fig/execute'; +import { createTimeoutPromise } from './helpers/promise'; -// TODO: remove once API is finalized export const enum TerminalShellType { - Sh = 1, - Bash = 2, - Fish = 3, - Csh = 4, - Ksh = 5, - Zsh = 6, - CommandPrompt = 7, - GitBash = 8, - PowerShell = 9, - Python = 10, - Julia = 11, - NuShell = 12, - Node = 13 + Bash = 'bash', + Fish = 'fish', + Zsh = 'zsh', + PowerShell = 'pwsh', + Python = 'python' } const isWindows = osIsWindows(); @@ -48,6 +43,8 @@ export const availableSpecs: Fig.Spec[] = [ cdSpec, codeInsidersCompletionSpec, codeCompletionSpec, + npxCompletionSpec, + setLocationSpec, ]; for (const spec of upstreamSpecs) { availableSpecs.push(require(`./completions/upstream/${spec}`).default); @@ -67,11 +64,10 @@ async function getShellGlobals(shellType: TerminalShellType, existingCommands?: if (cachedCommands) { return cachedCommands; } - const shell = getShell(shellType); - if (!shell) { + if (!shellType) { return; } - const options: ExecOptionsWithStringEncoding = { encoding: 'utf-8', shell }; + const options: ExecOptionsWithStringEncoding = { encoding: 'utf-8', shell: shellType }; const mixedCommands: (string | ICompletionResource)[] | undefined = await getShellSpecificGlobals.get(shellType)?.(options, existingCommands); const normalizedCommands = mixedCommands?.map(command => typeof command === 'string' ? ({ label: command }) : command); cachedGlobals.set(shellType, normalizedCommands); @@ -91,35 +87,56 @@ export async function activate(context: vscode.ExtensionContext) { id: 'terminal-suggest', async provideTerminalCompletions(terminal: vscode.Terminal, terminalContext: vscode.TerminalCompletionContext, token: vscode.CancellationToken): Promise { if (token.isCancellationRequested) { + console.debug('#terminalCompletions token cancellation requested'); return; } - const shellType: TerminalShellType | undefined = 'shellType' in terminal.state ? terminal.state.shellType as TerminalShellType : undefined; - if (!shellType) { + const shellType: string | undefined = 'shell' in terminal.state ? terminal.state.shell as string : undefined; + const terminalShellType = getTerminalShellType(shellType); + if (!terminalShellType) { + console.debug('#terminalCompletions No shell type found for terminal'); return; } - const commandsInPath = await pathExecutableCache.getExecutablesInPath(terminal.shellIntegration?.env); - const shellGlobals = await getShellGlobals(shellType, commandsInPath?.labels) ?? []; + const commandsInPath = await pathExecutableCache.getExecutablesInPath(terminal.shellIntegration?.env?.value); + const shellGlobals = await getShellGlobals(terminalShellType, commandsInPath?.labels) ?? []; if (!commandsInPath?.completionResources) { + console.debug('#terminalCompletions No commands found in path'); return; } // Order is important here, add shell globals first so they are prioritized over path commands const commands = [...shellGlobals, ...commandsInPath.completionResources]; const prefix = getPrefix(terminalContext.commandLine, terminalContext.cursorPosition); const pathSeparator = isWindows ? '\\' : '/'; - const tokenType = getTokenType(terminalContext, shellType); - const result = await getCompletionItemsFromSpecs(availableSpecs, terminalContext, commands, prefix, tokenType, terminal.shellIntegration?.cwd, getEnvAsRecord(terminal.shellIntegration?.env), terminal.name, token); + const tokenType = getTokenType(terminalContext, terminalShellType); + const result = await Promise.race([ + getCompletionItemsFromSpecs( + availableSpecs, + terminalContext, + commands, + prefix, + tokenType, + terminal.shellIntegration?.cwd, + getEnvAsRecord(terminal.shellIntegration?.env?.value), + terminal.name, + token + ), + createTimeoutPromise(300, undefined) + ]); + if (!result) { + return; + } + if (terminal.shellIntegration?.env) { const homeDirCompletion = result.items.find(i => i.label === '~'); - if (homeDirCompletion && terminal.shellIntegration.env.HOME) { - homeDirCompletion.documentation = getFriendlyResourcePath(vscode.Uri.file(terminal.shellIntegration.env.HOME), pathSeparator, vscode.TerminalCompletionItemKind.Folder); + if (homeDirCompletion && terminal.shellIntegration.env?.value?.HOME) { + homeDirCompletion.documentation = getFriendlyResourcePath(vscode.Uri.file(terminal.shellIntegration.env.value.HOME), pathSeparator, vscode.TerminalCompletionItemKind.Folder); homeDirCompletion.kind = vscode.TerminalCompletionItemKind.Folder; } } if (result.cwd && (result.filesRequested || result.foldersRequested)) { - return new vscode.TerminalCompletionList(result.items, { filesRequested: result.filesRequested, foldersRequested: result.foldersRequested, cwd: result.cwd, env: terminal.shellIntegration?.env }); + return new vscode.TerminalCompletionList(result.items, { filesRequested: result.filesRequested, foldersRequested: result.foldersRequested, fileExtensions: result.fileExtensions,cwd: result.cwd, env: terminal.shellIntegration?.env.value }); } return result.items; } @@ -166,8 +183,8 @@ export async function resolveCwdFromPrefix(prefix: string, currentCwd?: vscode.U // Ignore errors } - // If the prefix is not a folder, return the current cwd - return currentCwd; + // No valid path found + return undefined; } function getPrefix(commandLine: string, cursorPosition: number): string { @@ -206,12 +223,14 @@ export async function getCompletionItemsFromSpecs( shellIntegrationCwd: vscode.Uri | undefined, env: Record, name: string, - token?: vscode.CancellationToken -): Promise<{ items: vscode.TerminalCompletionItem[]; filesRequested: boolean; foldersRequested: boolean; cwd?: vscode.Uri }> { + token?: vscode.CancellationToken, + executeExternals?: IFigExecuteExternals, +): Promise<{ items: vscode.TerminalCompletionItem[]; filesRequested: boolean; foldersRequested: boolean; fileExtensions?: string[]; cwd?: vscode.Uri }> { const items: vscode.TerminalCompletionItem[] = []; let filesRequested = false; let foldersRequested = false; let hasCurrentArg = false; + let fileExtensions: string[] | undefined; let precedingText = terminalContext.commandLine.slice(0, terminalContext.cursorPosition + 1); if (isWindows) { @@ -223,11 +242,12 @@ export async function getCompletionItemsFromSpecs( } } - const result = await getFigSuggestions(specs, terminalContext, availableCommands, prefix, tokenType, shellIntegrationCwd, env, name, precedingText, token); + const result = await getFigSuggestions(specs, terminalContext, availableCommands, prefix, tokenType, shellIntegrationCwd, env, name, precedingText, executeExternals ?? { executeCommand, executeCommandTimeout }, token); if (result) { hasCurrentArg ||= result.hasCurrentArg; filesRequested ||= result.filesRequested; foldersRequested ||= result.foldersRequested; + fileExtensions ??= result.fileExtensions; if (result.items) { items.push(...result.items); } @@ -271,10 +291,10 @@ export async function getCompletionItemsFromSpecs( let cwd: vscode.Uri | undefined; if (shellIntegrationCwd && (filesRequested || foldersRequested)) { - cwd = await resolveCwdFromPrefix(prefix, shellIntegrationCwd) ?? shellIntegrationCwd; + cwd = await resolveCwdFromPrefix(prefix, shellIntegrationCwd); } - return { items, filesRequested, foldersRequested, cwd }; + return { items, filesRequested, foldersRequested, fileExtensions, cwd }; } function compareItems(existingItem: vscode.TerminalCompletionItem, command: ICompletionResource): vscode.TerminalCompletionItem | undefined { @@ -291,30 +311,56 @@ function compareItems(existingItem: vscode.TerminalCompletionItem, command: ICom } } -function getShell(shellType: TerminalShellType): string | undefined { +function getEnvAsRecord(shellIntegrationEnv: { [key: string]: string | undefined } | undefined): Record { + const env: Record = {}; + for (const [key, value] of Object.entries(shellIntegrationEnv ?? process.env)) { + if (typeof value === 'string') { + env[key] = value; + } + } + if (!shellIntegrationEnv) { + sanitizeProcessEnvironment(env); + } + return env; +} + +function getTerminalShellType(shellType: string | undefined): TerminalShellType | undefined { switch (shellType) { - case TerminalShellType.Bash: - return 'bash'; - case TerminalShellType.Fish: - return 'fish'; - case TerminalShellType.Zsh: - return 'zsh'; - case TerminalShellType.PowerShell: - return 'pwsh'; - default: { + case 'bash': + return TerminalShellType.Bash; + case 'zsh': + return TerminalShellType.Zsh; + case 'pwsh': + return TerminalShellType.PowerShell; + case 'fish': + return TerminalShellType.Fish; + case 'python': + return TerminalShellType.Python; + default: return undefined; - } } } -function getEnvAsRecord(shellIntegrationEnv: { [key: string]: string | undefined } | undefined): Record { - const env: Record = {}; - if (shellIntegrationEnv) { - for (const [key, value] of Object.entries(shellIntegrationEnv)) { - if (typeof value === 'string') { - env[key] = value; +export function sanitizeProcessEnvironment(env: Record, ...preserve: string[]): void { + const set = preserve.reduce>((set, key) => { + set[key] = true; + return set; + }, {}); + const keysToRemove = [ + /^ELECTRON_.$/, + /^VSCODE_(?!(PORTABLE|SHELL_LOGIN|ENV_REPLACE|ENV_APPEND|ENV_PREPEND)).$/, + /^SNAP(|_.*)$/, + /^GDK_PIXBUF_.$/, + ]; + const envKeys = Object.keys(env); + envKeys + .filter(key => !set[key]) + .forEach(envKey => { + for (let i = 0; i < keysToRemove.length; i++) { + if (envKey.search(keysToRemove[i]) !== -1) { + delete env[envKey]; + break; + } } - } - } - return env; + }); } diff --git a/extensions/terminal-suggest/src/test/completions/code.test.ts b/extensions/terminal-suggest/src/test/completions/code.test.ts index e90d3120ff4e..a7b6885f83d8 100644 --- a/extensions/terminal-suggest/src/test/completions/code.test.ts +++ b/extensions/terminal-suggest/src/test/completions/code.test.ts @@ -8,8 +8,7 @@ import codeCompletionSpec from '../../completions/code'; import { testPaths, type ISuiteSpec, type ITestSpec } from '../helpers'; import codeInsidersCompletionSpec from '../../completions/code-insiders'; -export const codeSpecOptions = ['-', '--add', '--category', '--diff', '--disable-extension', '--disable-extensions', '--disable-gpu', '--enable-proposed-api', '--extensions-dir', '--goto', '--help', '--inspect-brk-extensions', '--inspect-extensions', '--install-extension', '--list-extensions', '--locale', '--log', '--max-memory', '--merge', '--new-window', '--pre-release', '--prof-startup', '--profile', '--reuse-window', '--show-versions', '--status', '--sync', '--telemetry', '--uninstall-extension', '--user-data-dir', '--verbose', '--version', '--wait', '-a', '-d', '-g', '-h', '-m', '-n', '-r', '-s', '-v', '-w']; - +export const codeSpecOptions = ['-', '--add', '--category', '--diff', '--disable-extension', '--disable-extensions', '--disable-gpu', '--enable-proposed-api', '--extensions-dir', '--goto', '--help', '--inspect-brk-extensions', '--inspect-extensions', '--install-extension', '--list-extensions', '--locale', '--locate-shell-integration-path', '--log', '--max-memory', '--merge', '--new-window', '--pre-release', '--prof-startup', '--profile', '--reuse-window', '--show-versions', '--status', '--sync', '--telemetry', '--uninstall-extension', '--user-data-dir', '--verbose', '--version', '--wait', '-a', '-d', '-g', '-h', '-m', '-n', '-r', '-s', '-v', '-w']; export function createCodeTestSpecs(executable: string): ITestSpec[] { const localeOptions = ['bg', 'de', 'en', 'es', 'fr', 'hu', 'it', 'ja', 'ko', 'pt-br', 'ru', 'tr', 'zh-CN', 'zh-TW']; @@ -39,8 +38,9 @@ export function createCodeTestSpecs(executable: string): ITestSpec[] { { input: `${executable} --goto |`, expectedResourceRequests: { type: 'files', cwd: testPaths.cwd } }, { input: `${executable} --user-data-dir |`, expectedResourceRequests: { type: 'folders', cwd: testPaths.cwd } }, { input: `${executable} --profile |` }, - { input: `${executable} --install-extension |` }, - { input: `${executable} --uninstall-extension |` }, + { input: `${executable} --install-extension |`, expectedCompletions: [executable], expectedResourceRequests: { type: 'both', cwd: testPaths.cwd } }, + { input: `${executable} --uninstall-extension |`, expectedCompletions: [executable] }, + { input: `${executable} --disable-extension |`, expectedCompletions: [executable] }, { input: `${executable} --log |`, expectedCompletions: logOptions }, { input: `${executable} --sync |`, expectedCompletions: syncOptions }, { input: `${executable} --extensions-dir |`, expectedResourceRequests: { type: 'folders', cwd: testPaths.cwd } }, diff --git a/extensions/terminal-suggest/src/test/fig.test.ts b/extensions/terminal-suggest/src/test/fig.test.ts index 5df50219e86a..915d32d97ea2 100644 --- a/extensions/terminal-suggest/src/test/fig.test.ts +++ b/extensions/terminal-suggest/src/test/fig.test.ts @@ -127,5 +127,62 @@ export const figGenericTestSuites: ISuiteSpec[] = [ { input: 'foo --bar b|', expectedCompletions: ['a', 'b', 'c'] }, { input: 'foo --bar c|', expectedCompletions: ['a', 'b', 'c'] }, ] + }, + { + name: 'Fig script generator', + completionSpecs: [ + { + name: 'foo', + description: 'Foo', + args: { + name: 'bar', + generators: [ + { + script: () => ['echo abcd'], + postProcess: (out) => out.split('').map(item => { + return { name: item }; + }).filter(i => !!i) + } + ] + } + } + ], + availableCommands: 'foo', + testSpecs: [ + { input: 'foo |', expectedCompletions: ['e', 'c', 'h', 'o', ' ', 'a', 'b', 'c', 'd'] }, + { input: 'foo a|', expectedCompletions: ['e', 'c', 'h', 'o', ' ', 'a', 'b', 'c', 'd'] }, + { input: 'foo b|', expectedCompletions: ['e', 'c', 'h', 'o', ' ', 'a', 'b', 'c', 'd'] }, + { input: 'foo c|', expectedCompletions: ['e', 'c', 'h', 'o', ' ', 'a', 'b', 'c', 'd'] }, + ] + }, + { + name: 'Fig custom generator', + completionSpecs: [ + { + name: 'foo', + description: 'Foo', + args: { + name: 'bar', + generators: [ + { + custom: async (tokens: string[], executeCommand: Fig.ExecuteCommandFunction, generatorContext: Fig.GeneratorContext) => { + if (tokens.length) { + return tokens.map(token => ({ name: token })); + } + executeCommand({ command: 'echo', args: ['a\tb\nc\td'] }); + } + } + ] + } + } + ], + availableCommands: 'foo', + testSpecs: [ + { input: 'foo |', expectedCompletions: ['foo'] }, + { input: 'foo a|', expectedCompletions: ['a', 'foo'] }, + { input: 'foo b|', expectedCompletions: ['b', 'foo'] }, + { input: 'foo c|', expectedCompletions: ['c', 'foo'] }, + ] } ]; + diff --git a/extensions/terminal-suggest/src/test/terminalSuggestMain.test.ts b/extensions/terminal-suggest/src/test/terminalSuggestMain.test.ts index d13f45a28c60..b14b6a2c4d37 100644 --- a/extensions/terminal-suggest/src/test/terminalSuggestMain.test.ts +++ b/extensions/terminal-suggest/src/test/terminalSuggestMain.test.ts @@ -22,6 +22,7 @@ import { gitTestSuiteSpec } from './completions/upstream/git.test'; import { osIsWindows } from '../helpers/os'; import codeCompletionSpec from '../completions/code'; import { figGenericTestSuites } from './fig.test'; +import { IFigExecuteExternals } from '../fig/execute'; const testSpecs2: ISuiteSpec[] = [ { @@ -101,7 +102,9 @@ suite('Terminal Suggest', () => { getTokenType(terminalContext, undefined), testPaths.cwd, {}, - 'testName' + 'testName', + undefined, + new MockFigExecuteExternals() ); deepStrictEqual(result.items.map(i => i.label).sort(), (testSpec.expectedCompletions ?? []).sort()); strictEqual(result.filesRequested, filesRequested, 'Files requested different than expected, got: ' + result.filesRequested); @@ -114,3 +117,24 @@ suite('Terminal Suggest', () => { }); } }); + + +class MockFigExecuteExternals implements IFigExecuteExternals { + public async executeCommand(input: Fig.ExecuteCommandInput): Promise { + return this.executeCommandTimeout(input); + } + async executeCommandTimeout(input: Fig.ExecuteCommandInput): Promise { + const command = [input.command, ...input.args].join(' '); + try { + return { + status: 0, + stdout: input.command, + stderr: '', + }; + } catch (err) { + console.error(`Error running shell command '${command}'`, { err }); + throw err; + } + } +} + diff --git a/extensions/terminal-suggest/src/tokens.ts b/extensions/terminal-suggest/src/tokens.ts index a967aeb20545..0520a2315a44 100644 --- a/extensions/terminal-suggest/src/tokens.ts +++ b/extensions/terminal-suggest/src/tokens.ts @@ -5,18 +5,19 @@ import { TerminalShellType } from './terminalSuggestMain'; + export const enum TokenType { Command, Argument, } -const shellTypeResetChars: { [key: number]: string[] | undefined } = { - [TerminalShellType.Bash]: ['>', '>>', '<', '2>', '2>>', '&>', '&>>', '|', '|&', '&&', '||', '&', ';', '(', '{', '<<'], - [TerminalShellType.Zsh]: ['>', '>>', '<', '2>', '2>>', '&>', '&>>', '<>', '|', '|&', '&&', '||', '&', ';', '(', '{', '<<', '<<<', '<('], - [TerminalShellType.PowerShell]: ['>', '>>', '<', '2>', '2>>', '*>', '*>>', '|', '-and', '-or', '-not', '!', '&', '-eq', '-ne', '-gt', '-lt', '-ge', '-le', '-like', '-notlike', '-match', '-notmatch', '-contains', '-notcontains', '-in', '-notin'] -}; +const shellTypeResetChars = new Map([ + [TerminalShellType.Bash, ['>', '>>', '<', '2>', '2>>', '&>', '&>>', '|', '|&', '&&', '||', '&', ';', '(', '{', '<<']], + [TerminalShellType.Zsh, ['>', '>>', '<', '2>', '2>>', '&>', '&>>', '<>', '|', '|&', '&&', '||', '&', ';', '(', '{', '<<', '<<<', '<(']], + [TerminalShellType.PowerShell, ['>', '>>', '<', '2>', '2>>', '*>', '*>>', '|', '-and', '-or', '-not', '!', '&', '-eq', '-ne', '-gt', '-lt', '-ge', '-le', '-like', '-notlike', '-match', '-notmatch', '-contains', '-notcontains', '-in', '-notin']] +]); -const defaultShellTypeResetChars = shellTypeResetChars[TerminalShellType.Bash]!; +const defaultShellTypeResetChars = shellTypeResetChars.get(TerminalShellType.Bash)!; export function getTokenType(ctx: { commandLine: string; cursorPosition: number }, shellType: TerminalShellType | undefined): TokenType { const spaceIndex = ctx.commandLine.substring(0, ctx.cursorPosition).lastIndexOf(' '); @@ -24,7 +25,7 @@ export function getTokenType(ctx: { commandLine: string; cursorPosition: number return TokenType.Command; } const previousTokens = ctx.commandLine.substring(0, spaceIndex + 1).trim(); - const commandResetChars = shellType === undefined ? defaultShellTypeResetChars : shellTypeResetChars[shellType] ?? defaultShellTypeResetChars; + const commandResetChars = shellType === undefined ? defaultShellTypeResetChars : shellTypeResetChars.get(shellType) ?? defaultShellTypeResetChars; if (commandResetChars.some(e => previousTokens.endsWith(e))) { return TokenType.Command; } diff --git a/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json b/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json index a79ad3853262..8e879df0e9c8 100644 --- a/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json +++ b/extensions/theme-monokai-dimmed/themes/dimmed-monokai-color-theme.json @@ -19,8 +19,8 @@ "editor.wordHighlightStrongBackground": "#6767ce80", "editorCursor.foreground": "#c07020", "editorWhitespace.foreground": "#505037", - "editorIndentGuide.background": "#505037", - "editorIndentGuide.activeBackground": "#707057", + "editorIndentGuide.background1": "#505037", + "editorIndentGuide.activeBackground1": "#707057", "editorGroupHeader.tabsBackground": "#282828", "tab.inactiveBackground": "#404040", "tab.border": "#303030", @@ -126,14 +126,6 @@ "foreground": "#8080FF", } }, - { - "name": "Keyword", - "scope": "keyword", - "settings": { - "fontStyle": "", - "foreground": "#6089B4" - } - }, { "name": "Support", "scope": "support", @@ -185,22 +177,6 @@ "foreground": "#6089B4" } }, - { - "name": "Tag name", - "scope": "entity.name.tag", - "settings": { - "fontStyle": "", - "foreground": "#9872A2" - } - }, - { - "name": "Tag attribute", - "scope": "entity.other.attribute-name", - "settings": { - "fontStyle": "", - "foreground": "#9872A2" - } - }, { "name": "Library function", "scope": "support.function", @@ -225,14 +201,6 @@ "foreground": "#6089B4" } }, - { - "name": "Meta Brace", - "scope": "punctuation.section.embedded -(source string source punctuation.section.embedded), meta.brace.erb.html", - "settings": { - "fontStyle": "", - "foreground": "#008200" - } - }, { "name": "Invalid", "scope": "invalid", diff --git a/extensions/tunnel-forwarding/package-lock.json b/extensions/tunnel-forwarding/package-lock.json index 57accdc3b8d8..81113fc22efb 100644 --- a/extensions/tunnel-forwarding/package-lock.json +++ b/extensions/tunnel-forwarding/package-lock.json @@ -9,26 +9,28 @@ "version": "1.0.0", "license": "MIT", "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "engines": { "vscode": "^1.82.0" } }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" } } } diff --git a/extensions/tunnel-forwarding/package.json b/extensions/tunnel-forwarding/package.json index 315baa03598e..4dc127134c7c 100644 --- a/extensions/tunnel-forwarding/package.json +++ b/extensions/tunnel-forwarding/package.json @@ -44,7 +44,7 @@ "watch": "gulp watch-extension:tunnel-forwarding" }, "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "prettier": { "printWidth": 100, diff --git a/extensions/typescript-basics/language-configuration.json b/extensions/typescript-basics/language-configuration.json index e7fadc46b723..844c8189d9f5 100644 --- a/extensions/typescript-basics/language-configuration.json +++ b/extensions/typescript-basics/language-configuration.json @@ -249,7 +249,7 @@ // Add // when pressing enter from inside line comment { "beforeText": { - "pattern": "(?= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" } }, "node_modules/@microsoft/1ds-post-js": { - "version": "4.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.4.tgz", - "integrity": "sha512-nlKjWricDj0Tn68Dt0P8lX9a+X7LYrqJ6/iSfQwMfDhRIGLqW+wxx8gxS+iGWC/oc8zMQAeiZaemUpCwQcwpRQ==", + "version": "4.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.10.tgz", + "integrity": "sha512-VSLjc9cT+Y+eTiSfYltJHJCejn8oYr0E6Pq2BMhOEO7F6IyLGYIxzKKvo78ze9x+iHX7KPTATcZ+PFgjGXuNqg==", "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "4.3.4", + "@microsoft/1ds-core-js": "4.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" } }, "node_modules/@microsoft/applicationinsights-channel-js": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.4.tgz", - "integrity": "sha512-Z4nrxYwGKP9iyrYtm7iPQXVOFy4FsEsX0nDKkAi96Qpgw+vEh6NH4ORxMMuES0EollBQ3faJyvYCwckuCVIj0g==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-channel-js/-/applicationinsights-channel-js-3.3.10.tgz", + "integrity": "sha512-iolFLz1ocWAzIQqHIEjjov3gNTPkgFQ4ArHnBcJEYoffOGWlJt6copaevS5YPI5rHzmbySsengZ8cLJJBBrXzQ==", "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-common": "3.3.4", - "@microsoft/applicationinsights-core-js": "3.3.4", + "@microsoft/applicationinsights-common": "3.3.10", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-common": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.4.tgz", - "integrity": "sha512-4ms16MlIvcP4WiUPqopifNxcWCcrXQJ2ADAK/75uok2mNQe6ZNRsqb/P+pvhUxc8A5HRlvoXPP1ptDSN5Girgw==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-common/-/applicationinsights-common-3.3.10.tgz", + "integrity": "sha512-RVIenPIvNgZCbjJdALvLM4rNHgAFuHI7faFzHCgnI6S2WCUNGHeXlQTs9EUUrL+n2TPp9/cd0KKMILU5VVyYiA==", "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "3.3.4", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-core-js": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.4.tgz", - "integrity": "sha512-MummANF0mgKIkdvVvfmHQTBliK114IZLRhTL0X0Ep+zjDwWMHqYZgew0nlFKAl6ggu42abPZFK5afpE7qjtYJA==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.10.tgz", + "integrity": "sha512-5yKeyassZTq2l+SAO4npu6LPnbS++UD+M+Ghjm9uRzoBwD8tumFx0/F8AkSVqbniSREd+ztH/2q2foewa2RZyg==", "license": "MIT", "dependencies": { "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { "tslib": ">= 1.0.0" @@ -110,18 +110,18 @@ } }, "node_modules/@microsoft/applicationinsights-web-basic": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.4.tgz", - "integrity": "sha512-OpEPXr8vU/t/M8T9jvWJzJx/pCyygIiR1nGM/2PTde0wn7anl71Gxl5fWol7K/WwFEORNjkL3CEyWOyDc+28AA==", + "version": "3.3.10", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-web-basic/-/applicationinsights-web-basic-3.3.10.tgz", + "integrity": "sha512-AZib5DAT3NU0VT0nLWEwXrnoMDDgZ/5S4dso01CNU5ELNxLdg+1fvchstlVdMy4FrAnxzs8Wf/GIQNFYOVgpAw==", "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-channel-js": "3.3.4", - "@microsoft/applicationinsights-common": "3.3.4", - "@microsoft/applicationinsights-core-js": "3.3.4", + "@microsoft/applicationinsights-channel-js": "3.3.10", + "@microsoft/applicationinsights-common": "3.3.10", + "@microsoft/applicationinsights-core-js": "3.3.10", "@microsoft/applicationinsights-shims": "3.0.1", "@microsoft/dynamicproto-js": "^2.0.3", - "@nevware21/ts-async": ">= 0.5.2 < 2.x", - "@nevware21/ts-utils": ">= 0.11.3 < 2.x" + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { "tslib": ">= 1.0.0" @@ -146,71 +146,77 @@ } }, "node_modules/@nevware21/ts-utils": { - "version": "0.11.6", - "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.11.6.tgz", - "integrity": "sha512-OUUJTh3fnaUSzg9DEHgv3d7jC+DnPL65mIO7RaR+jWve7+MmcgIvF79gY97DPQ4frH+IpNR78YAYd/dW4gK3kg==", + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.12.5.tgz", + "integrity": "sha512-JPQZWPKQJjj7kAftdEZL0XDFfbMgXCGiUAZe0d7EhLC3QlXTlZdSckGqqRIQ2QNl0VTEZyZUvRBw6Ednw089Fw==", "license": "MIT" }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/@types/semver": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-5.5.0.tgz", - "integrity": "sha512-41qEJgBH/TWgo5NFSvBCJ1qkoi3Q6ONSF2avrHq1LVEZfYpdHmj0y9SuTK+u9ZhG1sYQKBL1AWXKyLWP4RaUoQ==", - "dev": true + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==", + "dev": true, + "license": "MIT" }, "node_modules/@vscode/extension-telemetry": { - "version": "0.9.8", - "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-0.9.8.tgz", - "integrity": "sha512-7YcKoUvmHlIB8QYCE4FNzt3ErHi9gQPhdCM3ZWtpw1bxPT0I+lMdx52KHlzTNoJzQ2NvMX7HyzyDwBEiMgTrWQ==", + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@vscode/extension-telemetry/-/extension-telemetry-1.4.0.tgz", + "integrity": "sha512-mqypuqIDn+vQnaQ8tMdvCRn9BprZTH2j6nlFFv9hE6hB25n32F8rOZinos8OgblRi87X1qdBpjpb0Em1LGSG9w==", "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "^4.3.4", - "@microsoft/1ds-post-js": "^4.3.4", - "@microsoft/applicationinsights-web-basic": "^3.3.4" + "@microsoft/1ds-core-js": "^4.3.10", + "@microsoft/1ds-post-js": "^4.3.10", + "@microsoft/applicationinsights-web-basic": "^3.3.10" }, "engines": { "vscode": "^1.75.0" } }, "node_modules/@vscode/sync-api-client": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@vscode/sync-api-client/-/sync-api-client-0.7.2.tgz", - "integrity": "sha512-HQHz57RVKmR8sTEen1Y/T3r6mzDX7IaUJz/O2RJkn0Qu9ThvCsakLP0N+1iiwPnPfUfmNSwQXbSw8bEQFPcpYQ==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@vscode/sync-api-client/-/sync-api-client-0.8.1.tgz", + "integrity": "sha512-82NRZpZlvRtlFoqi/J8xGw964F8ZfJJeJu2D9QMAAzq8BMDCXqkhO2qHApnyEV4gG2mQSaHtfw0f30GOPExgUg==", + "license": "MIT", "dependencies": { - "@vscode/sync-api-common": "0.7.2", - "vscode-uri": "3.0.3" + "@vscode/sync-api-common": "0.8.1", + "vscode-uri": "^3.0.6" }, "engines": { - "node": ">=16.15.1" + "node": ">=16.14.2" } }, "node_modules/@vscode/sync-api-common": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/@vscode/sync-api-common/-/sync-api-common-0.7.2.tgz", - "integrity": "sha512-ne1XEeDIYA3mp4oo1QoF1fqFedd0Vf4ybMmLb9HixbTyXy/qwMNL2p6OjXjOsmx6w2q9eqzGA5W/OPRSJxTTIQ==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@vscode/sync-api-common/-/sync-api-common-0.8.1.tgz", + "integrity": "sha512-XfanQNxkUlCpAsQ2+1SzNEdoY7jtcu48jYBR0n864ZWAWrH7m2QBK32YoNlrDN2V0eh8tdF0WH1NuHOm3Y021A==", + "license": "MIT", "engines": { - "node": ">=16.15.1" + "node": ">=16.14.2" } }, "node_modules/@vscode/sync-api-service": { - "version": "0.7.3", - "resolved": "https://registry.npmjs.org/@vscode/sync-api-service/-/sync-api-service-0.7.3.tgz", - "integrity": "sha512-m2AmmfG4uzfjLMgWRHQ3xnBkdwCiUTO68vdw1XuzMsOb39Jwm9xr5bVVxwOFR9lPC0FfO1H6FUxBhZQvg7itPA==", + "version": "0.8.1", + "resolved": "https://registry.npmjs.org/@vscode/sync-api-service/-/sync-api-service-0.8.1.tgz", + "integrity": "sha512-W9mWVJcnxqqmGIgYKwddMuBsCFff+nxkA67nGlnRGUjpu6Mb+CoYaYLwFriq2c74zpRyMsON7Nfhh5zlFvTAeg==", + "license": "MIT", "dependencies": { - "@vscode/sync-api-common": "0.7.2", - "vscode-uri": "3.0.3" + "@vscode/sync-api-common": "0.8.1", + "uuid": "^9.0.0", + "vscode-uri": "^3.0.6" }, "engines": { - "node": ">=16.15.1", - "vscode": "^1.67.0" + "node": ">=16.14.2", + "vscode": "^1.71.0" } }, "node_modules/@vscode/ts-package-manager": { @@ -222,28 +228,16 @@ } }, "node_modules/jsonc-parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", - "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==" - }, - "node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "license": "MIT" }, "node_modules/semver": { - "version": "7.5.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.2.tgz", - "integrity": "sha512-SoftuTROv/cRjCze/scjGyiDtcUyxw1rgYQSZY7XTmtR5hX+dm76iDbTH8TkLPHCQmlbQVSSbNZCPM2hb0knnQ==", - "dependencies": { - "lru-cache": "^6.0.0" - }, + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -257,10 +251,24 @@ "integrity": "sha512-V+uqV66BOQnWxvI6HjDnE4VkInmYZUQ4dgB7gzaDyFyFSK1i1nF/j7DpS9UbQAgV9NaF1XpcyuavnM1qOeiEIg==" }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" + }, + "node_modules/uuid": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-9.0.1.tgz", + "integrity": "sha512-b+1eJOlsR9K8HJpow9Ok3fiWOWSIcIzXodvv0rQjVoOVNpWMpxf1wZNpt4y9h10odCNrqnYp1OBzRktckBe3sA==", + "funding": [ + "https://github.com/sponsors/broofa", + "https://github.com/sponsors/ctavan" + ], + "license": "MIT", + "bin": { + "uuid": "dist/bin/uuid" + } }, "node_modules/vscode-tas-client": { "version": "0.1.84", @@ -274,14 +282,10 @@ } }, "node_modules/vscode-uri": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.3.tgz", - "integrity": "sha512-EcswR2S8bpR7fD0YPeS7r2xXExrScVMxg4MedACaWHEtx9ftCF/qHG1xGkolzTPcEmjTavCQgbVzHUIdTMzFGA==" - }, - "node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "license": "MIT" } } } diff --git a/extensions/typescript-language-features/package.json b/extensions/typescript-language-features/package.json index 5a1f52a9b257..11ac6cd7c013 100644 --- a/extensions/typescript-language-features/package.json +++ b/extensions/typescript-language-features/package.json @@ -9,10 +9,10 @@ "aiKey": "0c6ae279ed8443289764825290e4f9e2-1a736e7c-1324-4338-be46-fc2a58ae4d14-7255", "enabledApiProposals": [ "workspaceTrust", + "createFileSystemWatcher", "multiDocumentHighlightProvider", "codeActionAI", - "codeActionRanges", - "editorHoverVerbosityLevel" + "codeActionRanges" ], "capabilities": { "virtualWorkspaces": { @@ -38,19 +38,19 @@ "Programming Languages" ], "dependencies": { - "@vscode/extension-telemetry": "^0.9.8", - "@vscode/sync-api-client": "^0.7.2", - "@vscode/sync-api-common": "^0.7.2", - "@vscode/sync-api-service": "^0.7.3", + "@vscode/extension-telemetry": "^1.4.0", + "@vscode/sync-api-client": "^0.8.1", + "@vscode/sync-api-common": "^0.8.1", + "@vscode/sync-api-service": "^0.8.1", "@vscode/ts-package-manager": "^0.0.2", - "jsonc-parser": "^3.2.0", - "semver": "7.5.2", + "jsonc-parser": "^3.3.1", + "semver": "7.7.3", "vscode-tas-client": "^0.1.84", - "vscode-uri": "^3.0.3" + "vscode-uri": "^3.1.0" }, "devDependencies": { - "@types/node": "20.x", - "@types/semver": "^5.5.0" + "@types/node": "25.x", + "@types/semver": "^7.7.1" }, "scripts": { "vscode:prepublish": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:typescript-language-features", @@ -1349,8 +1349,10 @@ "typescript.tsserver.experimental.useVsCodeWatcher": { "type": "boolean", "description": "%configuration.tsserver.useVsCodeWatcher%", - "deprecationMessage": "%configuration.tsserver.useVsCodeWatcher.deprecation%", - "default": true + "default": true, + "tags": [ + "experimental" + ] }, "typescript.tsserver.watchOptions": { "description": "%configuration.tsserver.watchOptions%", @@ -1515,15 +1517,6 @@ "type": "boolean", "default": true, "markdownDescription": "%configuration.updateImportsOnPaste%" - }, - "typescript.experimental.expandableHover": { - "type": "boolean", - "default": false, - "description": "%configuration.expandableHover%", - "scope": "window", - "tags": [ - "experimental" - ] } } }, diff --git a/extensions/typescript-language-features/package.nls.json b/extensions/typescript-language-features/package.nls.json index 6a7dc26a24d6..3ca0f590005c 100644 --- a/extensions/typescript-language-features/package.nls.json +++ b/extensions/typescript-language-features/package.nls.json @@ -167,7 +167,6 @@ "configuration.surveys.enabled": "Enabled/disable occasional surveys that help us improve VS Code's JavaScript and TypeScript support.", "configuration.suggest.completeJSDocs": "Enable/disable suggestion to complete JSDoc comments.", "configuration.tsserver.useVsCodeWatcher": "Use VS Code's file watchers instead of TypeScript's. Requires using TypeScript 5.4+ in the workspace.", - "configuration.tsserver.useVsCodeWatcher.deprecation": "Please use the `#typescript.tsserver.watchOptions#` setting instead.", "configuration.tsserver.watchOptions": "Configure which watching strategies should be used to keep track of files and directories.", "configuration.tsserver.watchOptions.vscode": "Use VS Code's file watchers instead of TypeScript's. Requires using TypeScript 5.4+ in the workspace.", "configuration.tsserver.watchOptions.watchFile": "Strategy for how individual files are watched.", @@ -224,8 +223,7 @@ "configuration.tsserver.web.projectWideIntellisense.suppressSemanticErrors": "Suppresses semantic errors on web even when project wide IntelliSense is enabled. This is always on when project wide IntelliSense is not enabled or available. See `#typescript.tsserver.web.projectWideIntellisense.enabled#`", "configuration.tsserver.web.typeAcquisition.enabled": "Enable/disable package acquisition on the web. This enables IntelliSense for imported packages. Requires `#typescript.tsserver.web.projectWideIntellisense.enabled#`. Currently not supported for Safari.", "configuration.tsserver.nodePath": "Run TS Server on a custom Node installation. This can be a path to a Node executable, or 'node' if you want VS Code to detect a Node installation.", - "configuration.updateImportsOnPaste": "Enable updating imports when pasting code. Requires TypeScript 5.7+.\n\nBy default this shows a option to update imports after pasting. You can use the `#editor.pasteAs.preferences#` setting to update imports automatically when pasting: `\"editor.pasteAs.preferences\": [ \"text.updateImports.jsts\" ]`.", - "configuration.expandableHover": "Enable expanding/contracting the hover to reveal more/less information from the TS server.", + "configuration.updateImportsOnPaste": "Automatically update imports when pasting code. Requires TypeScript 5.6+.", "walkthroughs.nodejsWelcome.title": "Get started with JavaScript and Node.js", "walkthroughs.nodejsWelcome.description": "Make the most of Visual Studio Code's first-class JavaScript experience.", "walkthroughs.nodejsWelcome.downloadNode.forMacOrWindows.title": "Install Node.js", diff --git a/extensions/typescript-language-features/src/configuration/configuration.ts b/extensions/typescript-language-features/src/configuration/configuration.ts index 693e7ad17e47..adf7c4f9ca22 100644 --- a/extensions/typescript-language-features/src/configuration/configuration.ts +++ b/extensions/typescript-language-features/src/configuration/configuration.ts @@ -227,36 +227,11 @@ export abstract class BaseServiceConfigurationProvider implements ServiceConfigu } private readUseVsCodeWatcher(configuration: vscode.WorkspaceConfiguration): boolean { - const watcherExcludes = configuration.get>('files.watcherExclude') ?? {}; - if ( - watcherExcludes['**/node_modules/*/**'] === true || // VS Code default prior to 1.94.x - watcherExcludes['**/node_modules/**'] === true || - watcherExcludes['**/node_modules'] === true || - watcherExcludes['**'] === true // VS Code Watching is entirely disabled - ) { - return false; - } - - const experimentalConfig = configuration.inspect('typescript.tsserver.experimental.useVsCodeWatcher'); - if (typeof experimentalConfig?.globalValue === 'boolean') { - return experimentalConfig.globalValue; - } - if (typeof experimentalConfig?.workspaceValue === 'boolean') { - return experimentalConfig.workspaceValue; - } - if (typeof experimentalConfig?.workspaceFolderValue === 'boolean') { - return experimentalConfig.workspaceFolderValue; - } - - return configuration.get('typescript.tsserver.watchOptions', vscodeWatcherName) === vscodeWatcherName; + return configuration.get('typescript.tsserver.experimental.useVsCodeWatcher', true); } private readWatchOptions(configuration: vscode.WorkspaceConfiguration): Proto.WatchOptions | undefined { - const watchOptions = configuration.get('typescript.tsserver.watchOptions'); - if (watchOptions === vscodeWatcherName) { - return undefined; - } - + const watchOptions = configuration.get('typescript.tsserver.watchOptions'); // Returned value may be a proxy. Clone it into a normal object return { ...(watchOptions ?? {}) }; } diff --git a/extensions/typescript-language-features/src/filesystems/autoInstallerFs.ts b/extensions/typescript-language-features/src/filesystems/autoInstallerFs.ts index d639b7fe9990..1265b07450d7 100644 --- a/extensions/typescript-language-features/src/filesystems/autoInstallerFs.ts +++ b/extensions/typescript-language-features/src/filesystems/autoInstallerFs.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { PackageManager } from '@vscode/ts-package-manager'; +import { PackageManager, ResolvedProject } from '@vscode/ts-package-manager'; import { basename, join } from 'path'; import * as vscode from 'vscode'; import { URI } from 'vscode-uri'; @@ -161,12 +161,18 @@ export class AutoInstallerFs extends Disposable implements vscode.FileSystemProv } const installing = (async () => { - const proj = await this.packageManager.resolveProject(root, await this.getInstallOpts(incomingUri.original, root)); + let proj: ResolvedProject; + try { + proj = await this.packageManager.resolveProject(root, await this.getInstallOpts(incomingUri.original, root)); + } catch (e) { + console.error(`failed to resolve project at ${incomingUri.path}: `, e); + return; + } + try { await proj.restore(); } catch (e) { console.error(`failed to restore package at ${incomingUri.path}: `, e); - throw e; } })(); this._projectCache.set(root, installing); diff --git a/extensions/typescript-language-features/src/languageFeatures/copyPaste.ts b/extensions/typescript-language-features/src/languageFeatures/copyPaste.ts index a0d59256f108..98b14148d95a 100644 --- a/extensions/typescript-language-features/src/languageFeatures/copyPaste.ts +++ b/extensions/typescript-language-features/src/languageFeatures/copyPaste.ts @@ -15,6 +15,20 @@ import FileConfigurationManager from './fileConfigurationManager'; import { conditionalRegistration, requireGlobalConfiguration, requireMinVersion, requireSomeCapability } from './util/dependentRegistration'; class CopyMetadata { + + static parse(data: string): CopyMetadata | undefined { + try { + + const parsedData = JSON.parse(data); + const resource = vscode.Uri.parse(parsedData.resource); + const ranges = parsedData.ranges.map((range: any) => new vscode.Range(range.start, range.end)); + const copyOperation = parsedData.copyOperation ? Promise.resolve(parsedData.copyOperation) : undefined; + return new CopyMetadata(resource, ranges, copyOperation); + } catch (error) { + return undefined; + } + } + constructor( public readonly resource: vscode.Uri, public readonly ranges: readonly vscode.Range[], @@ -213,7 +227,15 @@ class DocumentPasteProvider implements vscode.DocumentPasteEditProvider { + token: vscode.CancellationToken + ): Promise { const filepath = this.client.toOpenTsFilePath(document); if (!filepath) { return undefined; } - const enableExpandableHover = vscode.workspace.getConfiguration('typescript').get('experimental.expandableHover'); - let verbosityLevel: number | undefined; - if (enableExpandableHover && this.client.apiVersion.gte(API.v570)) { - verbosityLevel = Math.max(0, this.getPreviousLevel(context?.previousHover) + (context?.verbosityDelta ?? 0)); - } - const args = { ...typeConverters.Position.toFileLocationRequestArgs(filepath, position), verbosityLevel }; - const response = await this.client.interruptGetErr(async () => { await this.fileConfigurationManager.ensureConfigurationForDocument(document, token); + const args = typeConverters.Position.toFileLocationRequestArgs(filepath, position); return this.client.execute('quickinfo', args, token); }); @@ -50,24 +42,9 @@ class TypeScriptHoverProvider implements vscode.HoverProvider { return undefined; } - const contents = this.getContents(document.uri, response.body, response._serverType); - const range = typeConverters.Range.fromTextSpan(response.body); - const hover = verbosityLevel !== undefined ? - new vscode.VerboseHover( - contents, - range, - // @ts-expect-error - /*canIncreaseVerbosity*/ response.body.canIncreaseVerbosityLevel, - /*canDecreaseVerbosity*/ verbosityLevel !== 0 - ) : new vscode.Hover( - contents, - range - ); - - if (verbosityLevel !== undefined) { - this.lastHoverAndLevel = [hover, verbosityLevel]; - } - return hover; + return new vscode.Hover( + this.getContents(document.uri, response.body, response._serverType), + typeConverters.Range.fromTextSpan(response.body)); } private getContents( @@ -95,13 +72,6 @@ class TypeScriptHoverProvider implements vscode.HoverProvider { parts.push(md); return parts; } - - private getPreviousLevel(previousHover: vscode.Hover | undefined): number { - if (previousHover && this.lastHoverAndLevel && this.lastHoverAndLevel[0] === previousHover) { - return this.lastHoverAndLevel[1]; - } - return 0; - } } export function register( diff --git a/extensions/typescript-language-features/src/languageFeatures/jsDocCompletions.ts b/extensions/typescript-language-features/src/languageFeatures/jsDocCompletions.ts index f2c1b49c67f2..7bf3eb47da71 100644 --- a/extensions/typescript-language-features/src/languageFeatures/jsDocCompletions.ts +++ b/extensions/typescript-language-features/src/languageFeatures/jsDocCompletions.ts @@ -103,7 +103,7 @@ class JsDocCompletionProvider implements vscode.CompletionItemProvider { export function templateToSnippet(template: string): vscode.SnippetString { // TODO: use append placeholder let snippetIndex = 1; - template = template.replace(/\$/g, '\\$'); // CodeQL [SM02383] This is only used for text which is put into the editor. It is not for rendered html + template = template.replace(/\\/g, '\\\\').replace(/\$/g, '\\$'); // Escape backslash and dollar for VSCode snippets template = template.replace(/^[ \t]*(?=(\/|[ ]\*))/gm, ''); template = template.replace(/^(\/\*\*\s*\*[ ]*)$/m, (x) => x + `\$0`); template = template.replace(/\* @param([ ]\{\S+\})?\s+(\S+)[ \t]*$/gm, (_param, type, post) => { diff --git a/extensions/typescript-language-features/src/languageFeatures/util/textRendering.ts b/extensions/typescript-language-features/src/languageFeatures/util/textRendering.ts index f44ac0c4f407..b0acc74db201 100644 --- a/extensions/typescript-language-features/src/languageFeatures/util/textRendering.ts +++ b/extensions/typescript-language-features/src/languageFeatures/util/textRendering.ts @@ -210,7 +210,8 @@ function convertLinkTags( } function escapeMarkdownSyntaxTokensForCode(text: string): string { - return text.replace(/`/g, '\\$&'); // CodeQL [SM02383] This is only meant to escape backticks. The Markdown is fully sanitized after being rendered. + // First escape backslashes, then backticks + return text.replace(/\\/g, '\\\\').replace(/`/g, '\\$&'); // CodeQL [SM02383] This now escapes backticks and backslashes. } export function tagsToMarkdown( diff --git a/extensions/typescript-language-features/src/tsServer/bufferSyncSupport.ts b/extensions/typescript-language-features/src/tsServer/bufferSyncSupport.ts index 56d2896fa339..7b47be8d32f4 100644 --- a/extensions/typescript-language-features/src/tsServer/bufferSyncSupport.ts +++ b/extensions/typescript-language-features/src/tsServer/bufferSyncSupport.ts @@ -191,7 +191,20 @@ class SyncedBuffer { } private getProjectRootPath(resource: vscode.Uri): string | undefined { - const workspaceRoot = this.client.getWorkspaceRootForResource(resource); + let workspaceRoot = this.client.getWorkspaceRootForResource(resource); + + // If we didn't find a real workspace, we still want to try sending along a workspace folder + // to prevent TS from loading projects from outside of any workspace. + // Just pick the highest level one on the same FS even though the file is outside of it + if (!workspaceRoot && vscode.workspace.workspaceFolders) { + for (const root of Array.from(vscode.workspace.workspaceFolders).sort((a, b) => a.uri.path.length - b.uri.path.length)) { + if (root.uri.scheme === resource.scheme && root.uri.authority === resource.authority) { + workspaceRoot = root.uri; + break; + } + } + } + if (workspaceRoot) { const tsRoot = this.client.toTsFilePath(workspaceRoot); return tsRoot?.startsWith(inMemoryResourcePrefix) ? undefined : tsRoot; diff --git a/extensions/typescript-language-features/src/tsServer/protocol/protocol.const.ts b/extensions/typescript-language-features/src/tsServer/protocol/protocol.const.ts index ed4806e6fdda..f1b0cca26a48 100644 --- a/extensions/typescript-language-features/src/tsServer/protocol/protocol.const.ts +++ b/extensions/typescript-language-features/src/tsServer/protocol/protocol.const.ts @@ -92,7 +92,6 @@ export enum EventName { createFileWatcher = 'createFileWatcher', createDirectoryWatcher = 'createDirectoryWatcher', closeFileWatcher = 'closeFileWatcher', - requestCompleted = 'requestCompleted', } export enum OrganizeImportsMode { diff --git a/extensions/typescript-language-features/src/tsServer/spawner.ts b/extensions/typescript-language-features/src/tsServer/spawner.ts index aac3186631e2..407242eeeb77 100644 --- a/extensions/typescript-language-features/src/tsServer/spawner.ts +++ b/extensions/typescript-language-features/src/tsServer/spawner.ts @@ -268,20 +268,8 @@ export class TypeScriptServerSpawner { args.push('--noGetErrOnBackgroundUpdate'); - const configUseVsCodeWatcher = configuration.useVsCodeWatcher; - const isYarnPnp = apiVersion.isYarnPnp(); - if ( - apiVersion.gte(API.v544) - && configUseVsCodeWatcher - && !isYarnPnp // Disable for yarn pnp as it currently breaks with the VS Code watcher - ) { + if (apiVersion.gte(API.v544) && configuration.useVsCodeWatcher) { args.push('--canUseWatchEvents'); - } else { - if (!configUseVsCodeWatcher) { - this._logger.info(`<${kind}> Falling back to legacy node.js based file watching because of user settings.`); - } else if (isYarnPnp) { - this._logger.info(`<${kind}> Falling back to legacy node.js based file watching because of Yarn PnP.`); - } } args.push('--validateDefaultNpmLocation'); diff --git a/extensions/typescript-language-features/src/typescriptServiceClient.ts b/extensions/typescript-language-features/src/typescriptServiceClient.ts index d9f47c1b85a5..a9f780529519 100644 --- a/extensions/typescript-language-features/src/typescriptServiceClient.ts +++ b/extensions/typescript-language-features/src/typescriptServiceClient.ts @@ -30,8 +30,10 @@ import { TypeScriptServerSpawner } from './tsServer/spawner'; import { TypeScriptVersionManager } from './tsServer/versionManager'; import { ITypeScriptVersionProvider, TypeScriptVersion } from './tsServer/versionProvider'; import { ClientCapabilities, ClientCapability, ExecConfig, ITypeScriptServiceClient, ServerResponse, TypeScriptRequests } from './typescriptService'; +import { ServiceConfigurationProvider, SyntaxServerConfiguration, TsServerLogLevel, TypeScriptServiceConfiguration, areServiceConfigurationsEqual } from './configuration/configuration'; import { Disposable, DisposableStore, disposeAll } from './utils/dispose'; -import { hash } from './utils/hash'; +import * as fileSchemes from './configuration/fileSchemes'; +import { Logger } from './logging/logger'; import { isWeb, isWebAndHasSharedArrayBuffers } from './utils/platform'; @@ -138,8 +140,6 @@ export default class TypeScriptServiceClient extends Disposable implements IType private readonly processFactory: TsServerProcessFactory; private readonly watches = new Map(); - private readonly watchEvents = new Map(); - private watchChangeTimeout: NodeJS.Timeout | undefined; constructor( private readonly context: vscode.ExtensionContext, @@ -522,8 +522,8 @@ export default class TypeScriptServiceClient extends Disposable implements IType } private resetWatchers() { - clearTimeout(this.watchChangeTimeout); - disposeAll(Array.from(this.watches.values())); + disposeAll(this.watches.values()); + this.watches.clear(); } public async showVersionPicker(): Promise { @@ -626,7 +626,7 @@ export default class TypeScriptServiceClient extends Disposable implements IType }; } - private serviceExited(restart: boolean, tsVersion: API): void { + private serviceExited(restart: boolean): void { this.resetWatchers(); this.loadingIndicator.reset(); @@ -836,9 +836,10 @@ export default class TypeScriptServiceClient extends Disposable implements IType } } - for (const root of roots.sort((a, b) => a.uri.fsPath.length - b.uri.fsPath.length)) { + // Find the highest level workspace folder that contains the file + for (const root of roots.sort((a, b) => a.uri.path.length - b.uri.path.length)) { if (root.uri.scheme === resource.scheme && root.uri.authority === resource.authority) { - if (resource.fsPath.startsWith(root.uri.fsPath + path.sep)) { + if (resource.path.startsWith(root.uri.path + '/')) { return root.uri; } } @@ -1127,15 +1128,32 @@ export default class TypeScriptServiceClient extends Disposable implements IType removeEvent('updated'); aggregateEvent(); break; - case 'updated': - if (event?.created?.has(path)) { - return; - } - removeEvent('deleted'); - aggregateEvent(); + + case EventName.createDirectoryWatcher: + this.createFileSystemWatcher( + (event.body as Proto.CreateDirectoryWatcherEventBody).id, + new vscode.RelativePattern( + vscode.Uri.file((event.body as Proto.CreateDirectoryWatcherEventBody).path), + (event.body as Proto.CreateDirectoryWatcherEventBody).recursive ? '**' : '*' + ), + event.body.ignoreUpdate // TODO when typescript.d.ts gets updated update the type info + ); + break; + + case EventName.createFileWatcher: + this.createFileSystemWatcher( + (event.body as Proto.CreateFileWatcherEventBody).id, + new vscode.RelativePattern( + vscode.Uri.file((event.body as Proto.CreateFileWatcherEventBody).path), + '*' + ) + ); + break; + + case EventName.closeFileWatcher: + this.closeFileSystemWatcher(event.body.id); break; } - this.scheduleExecuteWatchChangeRequest(); } private createFileSystemWatcher( @@ -1144,22 +1162,49 @@ export default class TypeScriptServiceClient extends Disposable implements IType ignoreChangeEvents?: boolean, ) { const disposable = new DisposableStore(); - const watcher = disposable.add(vscode.workspace.createFileSystemWatcher(pattern, undefined, ignoreChangeEvents)); - disposable.add(watcher.onDidChange(changeFile => - this.addWatchEvent(id, 'updated', changeFile.fsPath) - )); - disposable.add(watcher.onDidCreate(createFile => - this.addWatchEvent(id, 'created', createFile.fsPath) - )); - disposable.add(watcher.onDidDelete(deletedFile => - this.addWatchEvent(id, 'deleted', deletedFile.fsPath) - )); - disposable.add({ - dispose: () => { - this.watchEvents.delete(id); - this.watches.delete(id); + + const events = { updated: new Set(), created: new Set(), deleted: new Set() }; + + let timeout: NodeJS.Timeout | undefined; + disposable.add({ dispose: () => clearTimeout(timeout) }); + + const executeWatchChangeRequest = () => { + try { + // TODO:: not sure whwere we check typescript version but this has to be 5.4+ or 5.5 depeneding on which version of typescript the protocol changes go into + this.executeWithoutWaitingForResponse('watchChange', { + id, + updated: events.updated.size > 0 ? Array.from(events.updated) : undefined, + created: events.created.size > 0 ? Array.from(events.created) : undefined, + deleted: events.deleted.size > 0 ? Array.from(events.deleted) : undefined + }); + } finally { + events.updated.clear(); + events.created.clear(); + events.deleted.clear(); + + timeout = undefined; } - }); + }; + + const scheduleExecuteWatchChangeRequest = () => { + if (!timeout) { + timeout = setTimeout(() => executeWatchChangeRequest(), 100 /* aggregate events over 100ms to reduce client<->server IPC overhead */); + } + }; + + const watcher = disposable.add(vscode.workspace.createFileSystemWatcher(pattern, { excludes: [] /* TODO:: need to fill in excludes list */, ignoreChangeEvents })); + disposable.add(watcher.onDidChange(changeFile => { + events.updated.add(changeFile.fsPath); + scheduleExecuteWatchChangeRequest(); + })); + disposable.add(watcher.onDidCreate(createFile => { + events.created.add(createFile.fsPath); + scheduleExecuteWatchChangeRequest(); + })); + disposable.add(watcher.onDidDelete(deletedFile => { + events.deleted.add(deletedFile.fsPath); + scheduleExecuteWatchChangeRequest(); + })); if (this.watches.has(id)) { this.closeFileSystemWatcher(id); @@ -1167,6 +1212,17 @@ export default class TypeScriptServiceClient extends Disposable implements IType this.watches.set(id, disposable); } + private closeFileSystemWatcher( + id: number, + ) { + const existing = this.watches.get(id); + if (existing) { + existing.dispose(); + this.watches.delete(id); + } + this.watches.set(id, disposable); + } + private closeFileSystemWatcher(id: number) { const existing = this.watches.get(id); existing?.dispose(); diff --git a/extensions/typescript-language-features/src/ui/intellisenseStatus.ts b/extensions/typescript-language-features/src/ui/intellisenseStatus.ts index e26e2b3719f4..2c3ffb783c85 100644 --- a/extensions/typescript-language-features/src/ui/intellisenseStatus.ts +++ b/extensions/typescript-language-features/src/ui/intellisenseStatus.ts @@ -182,8 +182,8 @@ export class IntellisenseStatus extends Disposable { statusItem.command = { command: this.createOrOpenConfigCommandId, title: this._state.projectType === ProjectType.TypeScript - ? vscode.l10n.t("Configure tsconfig") - : vscode.l10n.t("Configure jsconfig"), + ? vscode.l10n.t("Configure TSConfig") + : vscode.l10n.t("Configure JSConfig"), arguments: [rootPath, this._state.projectType] satisfies CreateOrOpenConfigCommandArgs, }; } else { @@ -191,7 +191,7 @@ export class IntellisenseStatus extends Disposable { statusItem.detail = undefined; statusItem.command = { command: this.openOpenConfigCommandId, - title: vscode.l10n.t("Open config file"), + title: vscode.l10n.t("Open Config File"), arguments: [rootPath, this._state.projectType] satisfies CreateOrOpenConfigCommandArgs, }; } @@ -200,8 +200,8 @@ export class IntellisenseStatus extends Disposable { case IntellisenseState.Type.SyntaxOnly: { const statusItem = this.ensureStatusItem(); statusItem.severity = vscode.LanguageStatusSeverity.Warning; - statusItem.text = vscode.l10n.t("Partial Mode"); - statusItem.detail = vscode.l10n.t("Project Wide IntelliSense not available"); + statusItem.text = vscode.l10n.t("Partial mode"); + statusItem.detail = vscode.l10n.t("Project wide IntelliSense not available"); statusItem.busy = false; statusItem.command = { title: vscode.l10n.t("Learn More"), diff --git a/extensions/typescript-language-features/src/ui/versionStatus.ts b/extensions/typescript-language-features/src/ui/versionStatus.ts index a4f377782a8f..901e548f4a8d 100644 --- a/extensions/typescript-language-features/src/ui/versionStatus.ts +++ b/extensions/typescript-language-features/src/ui/versionStatus.ts @@ -23,7 +23,7 @@ export class VersionStatus extends Disposable { this._statusItem = this._register(vscode.languages.createLanguageStatusItem('typescript.version', jsTsLanguageModes)); this._statusItem.name = vscode.l10n.t("TypeScript Version"); - this._statusItem.detail = vscode.l10n.t("TypeScript Version"); + this._statusItem.detail = vscode.l10n.t("TypeScript version"); this._register(this._client.onTsServerStarted(({ version }) => this.onDidChangeTypeScriptVersion(version))); } diff --git a/extensions/typescript-language-features/src/utils/dispose.ts b/extensions/typescript-language-features/src/utils/dispose.ts index e7687bb6941e..effe8b726fc3 100644 --- a/extensions/typescript-language-features/src/utils/dispose.ts +++ b/extensions/typescript-language-features/src/utils/dispose.ts @@ -5,22 +5,9 @@ import * as vscode from 'vscode'; - export function disposeAll(disposables: Iterable) { - const errors: any[] = []; - for (const disposable of disposables) { - try { - disposable.dispose(); - } catch (e) { - errors.push(e); - } - } - - if (errors.length === 1) { - throw errors[0]; - } else if (errors.length > 1) { - throw new AggregateError(errors, 'Encountered errors while disposing of store'); + disposable.dispose(); } } diff --git a/extensions/typescript-language-features/tsconfig.json b/extensions/typescript-language-features/tsconfig.json index 776a71efaf85..d864a129f5e5 100644 --- a/extensions/typescript-language-features/tsconfig.json +++ b/extensions/typescript-language-features/tsconfig.json @@ -11,10 +11,10 @@ "include": [ "src/**/*", "../../src/vscode-dts/vscode.d.ts", + "../../src/vscode-dts/vscode.proposed.createFileSystemWatcher.d.ts", "../../src/vscode-dts/vscode.proposed.codeActionAI.d.ts", "../../src/vscode-dts/vscode.proposed.codeActionRanges.d.ts", "../../src/vscode-dts/vscode.proposed.multiDocumentHighlightProvider.d.ts", - "../../src/vscode-dts/vscode.proposed.workspaceTrust.d.ts", - "../../src/vscode-dts/vscode.proposed.editorHoverVerbosityLevel.d.ts", + "../../src/vscode-dts/vscode.proposed.workspaceTrust.d.ts" ] } diff --git a/extensions/vscode-api-tests/package-lock.json b/extensions/vscode-api-tests/package-lock.json index cd90b33ca496..154cf4b768eb 100644 --- a/extensions/vscode-api-tests/package-lock.json +++ b/extensions/vscode-api-tests/package-lock.json @@ -9,10 +9,10 @@ "version": "0.0.1", "license": "MIT", "devDependencies": { - "@types/mocha": "^9.1.1", - "@types/node": "20.x", + "@types/mocha": "^10.0.10", + "@types/node": "25.x", "@types/node-forge": "^1.3.11", - "node-forge": "^1.3.1", + "node-forge": "^1.3.2", "straightforward": "^4.2.2" }, "engines": { @@ -20,18 +20,20 @@ } }, "node_modules/@types/mocha": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", - "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==", - "dev": true + "version": "10.0.10", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz", + "integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==", + "dev": true, + "license": "MIT" }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/@types/node-forge": { @@ -156,10 +158,11 @@ "dev": true }, "node_modules/node-forge": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.1.tgz", - "integrity": "sha512-dPEtOeMvF9VMcYV/1Wb8CPoVAXtp6MKMlcbAt4ddqmGqUJ6fQZFXkNZNkNlfevtNkGtaSoXf/vNNNSvgrdXwtA==", + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/node-forge/-/node-forge-1.3.2.tgz", + "integrity": "sha512-6xKiQ+cph9KImrRh0VsjH2d8/GXA4FIMlgU4B757iI1ApvcyA9VlouP0yZJha01V+huImO+kKMU7ih+2+E14fw==", "dev": true, + "license": "(BSD-3-Clause OR GPL-2.0)", "engines": { "node": ">= 6.13.0" } @@ -216,10 +219,11 @@ } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" }, "node_modules/wrap-ansi": { "version": "7.0.0", diff --git a/extensions/vscode-api-tests/package.json b/extensions/vscode-api-tests/package.json index 66a80b8867f8..b3c8f0cd1bfa 100644 --- a/extensions/vscode-api-tests/package.json +++ b/extensions/vscode-api-tests/package.json @@ -251,10 +251,10 @@ "vscode:prepublish": "node ../../node_modules/gulp/bin/gulp.js --gulpfile ../../build/gulpfile.extensions.js compile-extension:vscode-api-tests ./tsconfig.json" }, "devDependencies": { - "@types/mocha": "^9.1.1", - "@types/node": "20.x", + "@types/mocha": "^10.0.10", + "@types/node": "25.x", "@types/node-forge": "^1.3.11", - "node-forge": "^1.3.1", + "node-forge": "^1.3.2", "straightforward": "^4.2.2" }, "repository": { diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/terminal.shellIntegration.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/terminal.shellIntegration.test.ts index 8f094091f962..e276a1c7bc61 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/terminal.shellIntegration.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/terminal.shellIntegration.test.ts @@ -30,7 +30,7 @@ import { assertNoRpc } from '../utils'; disposables.length = 0; }); - function createTerminalAndWaitForShellIntegration(): Promise<{ terminal: Terminal; shellIntegration: TerminalShellIntegration }> { + function createTerminalAndWaitForShellIntegration(shellPath?: string): Promise<{ terminal: Terminal; shellIntegration: TerminalShellIntegration }> { return new Promise<{ terminal: Terminal; shellIntegration: TerminalShellIntegration }>(resolve => { disposables.push(window.onDidChangeTerminalShellIntegration(e => { if (e.terminal === terminal) { @@ -41,8 +41,8 @@ import { assertNoRpc } from '../utils'; } })); const terminal = platform() === 'win32' - ? window.createTerminal() - : window.createTerminal({ shellPath: '/bin/bash' }); + ? window.createTerminal({ shellPath }) + : window.createTerminal({ shellPath: shellPath ?? '/bin/bash' }); terminal.show(); }); } @@ -99,6 +99,21 @@ import { assertNoRpc } from '../utils'; })); }); ok(shellIntegration.env); + ok(shellIntegration.env.value); + ok(shellIntegration.env.value.PATH); + ok(shellIntegration.env.value.PATH.length > 0, 'env.value.PATH should have a length greater than 0'); + }); + + test.skip('Test if zsh env is set', async () => { + const { shellIntegration } = await createTerminalAndWaitForShellIntegration('/bin/zsh'); + await new Promise(r => { + disposables.push(window.onDidChangeTerminalShellIntegration(e => { + if (e.shellIntegration.env) { + r(); + } + })); + }); + ok(shellIntegration.env); ok(shellIntegration.env.PATH); ok(shellIntegration.env.PATH.length > 0, 'env.PATH should have a length greater than 0'); }); diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts index 9c1c097aab21..9e666879933c 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/terminal.test.ts @@ -257,12 +257,12 @@ import { assertNoRpc, poll } from '../utils'; test('onDidChangeTerminalState should fire with shellType when created', async () => { const terminal = window.createTerminal(); - if (terminal.state.shellType) { + if (terminal.state.shell) { return; } await new Promise(r => { disposables.push(window.onDidChangeTerminalState(e => { - if (e === terminal && e.state.shellType) { + if (e === terminal && e.state.shell) { r(); } })); diff --git a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts index 97a99fe56469..ceb3aea00d53 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts @@ -1311,7 +1311,7 @@ suite('vscode API - workspace', () => { return deleteFile(file); } - test('text document encodings', async () => { + test('encoding: text document encodings', async () => { const uri1 = await createRandomFile(); const uri2 = await createRandomFile(new Uint8Array([0xEF, 0xBB, 0xBF]) /* UTF-8 with BOM */); const uri3 = await createRandomFile(new Uint8Array([0xFF, 0xFE]) /* UTF-16 LE BOM */); @@ -1333,7 +1333,66 @@ suite('vscode API - workspace', () => { assert.strictEqual(doc5.encoding, 'utf8'); }); - test('fs.decode', async function () { + test('encoding: openTextDocument', async () => { + const uri1 = await createRandomFile(); + + let doc1 = await vscode.workspace.openTextDocument(uri1, { encoding: 'cp1252' }); + assert.strictEqual(doc1.encoding, 'cp1252'); + + let listener: vscode.Disposable | undefined; + const documentChangePromise = new Promise(resolve => { + listener = vscode.workspace.onDidChangeTextDocument(e => { + if (e.document.uri.toString() === uri1.toString()) { + resolve(); + } + }); + }); + + doc1 = await vscode.workspace.openTextDocument(uri1, { encoding: 'utf16le' }); + assert.strictEqual(doc1.encoding, 'utf16le'); + await documentChangePromise; + + const doc2 = await vscode.workspace.openTextDocument({ encoding: 'utf16be' }); + assert.strictEqual(doc2.encoding, 'utf16be'); + + const doc3 = await vscode.workspace.openTextDocument({ content: 'Hello World', encoding: 'utf16le' }); + assert.strictEqual(doc3.encoding, 'utf16le'); + + listener?.dispose(); + }); + + test('encoding: openTextDocument - throws for dirty documents', async () => { + const uri1 = await createRandomFile(); + + const doc1 = await vscode.workspace.openTextDocument(uri1, { encoding: 'cp1252' }); + + const edit = new vscode.WorkspaceEdit(); + edit.insert(doc1.uri, new vscode.Position(0, 0), 'Hello World'); + await vscode.workspace.applyEdit(edit); + assert.strictEqual(doc1.isDirty, true); + + let err; + try { + await vscode.workspace.decode(new Uint8Array([0, 0, 0, 0]), doc1.uri); + } catch (e) { + err = e; + } + assert.ok(err); + }); + + test('encoding: openTextDocument - multiple requests with different encoding work', async () => { + const uri1 = await createRandomFile(); + + const doc1P = vscode.workspace.openTextDocument(uri1); + const doc2P = vscode.workspace.openTextDocument(uri1, { encoding: 'cp1252' }); + + const [doc1, doc2] = await Promise.all([doc1P, doc2P]); + + assert.strictEqual(doc1.encoding, 'cp1252'); + assert.strictEqual(doc2.encoding, 'cp1252'); + }); + + test('encoding: decode', async function () { const uri = root.with({ path: posix.join(root.path, 'file.txt') }); // without setting @@ -1375,7 +1434,7 @@ suite('vscode API - workspace', () => { assert.ok(err); }); - test('fs.encode', async function () { + test('encoding: encode', async function () { const uri = root.with({ path: posix.join(root.path, 'file.txt') }); // without setting @@ -1421,4 +1480,39 @@ suite('vscode API - workspace', () => { } return true; } + + test('encoding: save text document with a different encoding', async () => { + const originalText = 'Hellö\nWörld'; + const uri = await createRandomFile(originalText); + + let doc = await vscode.workspace.openTextDocument(uri); + assert.strictEqual(doc.encoding, 'utf8'); + + const text = doc.getText(); + assert.strictEqual(text, originalText); + const buf = await vscode.workspace.encode(text, uri, { encoding: 'windows1252' }); + await vscode.workspace.fs.writeFile(uri, buf); + + doc = await vscode.workspace.openTextDocument(uri, { encoding: 'windows1252' }); + assert.strictEqual(doc.encoding, 'windows1252'); + const updatedText = doc.getText(); + assert.strictEqual(updatedText, text); + }); + + test('encoding: utf8bom does not explode (https://github.com/microsoft/vscode/issues/242132)', async function () { + const buffer = [0xEF, 0xBB, 0xBF, 72, 101, 108, 108, 111, 32, 87, 111, 114, 108, 100]; + const uri = await createRandomFile(new Uint8Array(buffer) /* UTF-8 with BOM */); + + let doc = await vscode.workspace.openTextDocument(uri); + assert.strictEqual(doc.encoding, 'utf8bom'); + + doc = await vscode.workspace.openTextDocument(uri, { encoding: 'utf8bom' }); + assert.strictEqual(doc.encoding, 'utf8bom'); + + const decoded = await vscode.workspace.decode(new Uint8Array(buffer), uri, { encoding: 'utf8bom' }); + assert.strictEqual(decoded, 'Hello World'); + + const encoded = await vscode.workspace.encode('Hello World', uri, { encoding: 'utf8bom' }); + assert.ok(equalsUint8Array(encoded, new Uint8Array(buffer))); + }); }); diff --git a/extensions/vscode-colorize-perf-tests/package-lock.json b/extensions/vscode-colorize-perf-tests/package-lock.json index b0516cc1e280..9cac2b08f68f 100644 --- a/extensions/vscode-colorize-perf-tests/package-lock.json +++ b/extensions/vscode-colorize-perf-tests/package-lock.json @@ -9,34 +9,37 @@ "version": "0.0.1", "license": "MIT", "dependencies": { - "jsonc-parser": "^3.2.0" + "jsonc-parser": "^3.3.1" }, "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "engines": { "vscode": "*" } }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/jsonc-parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", - "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==" + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "license": "MIT" }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" } } } diff --git a/extensions/vscode-colorize-perf-tests/package.json b/extensions/vscode-colorize-perf-tests/package.json index 2e72152e2847..475b206a57fe 100644 --- a/extensions/vscode-colorize-perf-tests/package.json +++ b/extensions/vscode-colorize-perf-tests/package.json @@ -19,10 +19,10 @@ "compile": "gulp compile-extension:vscode-colorize-perf-tests" }, "dependencies": { - "jsonc-parser": "^3.2.0" + "jsonc-parser": "^3.3.1" }, "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "repository": { "type": "git", diff --git a/extensions/vscode-colorize-tests/package-lock.json b/extensions/vscode-colorize-tests/package-lock.json index 4011d73bcac0..234908d6ba88 100644 --- a/extensions/vscode-colorize-tests/package-lock.json +++ b/extensions/vscode-colorize-tests/package-lock.json @@ -9,34 +9,37 @@ "version": "0.0.1", "license": "MIT", "dependencies": { - "jsonc-parser": "^3.2.0" + "jsonc-parser": "^3.3.1" }, "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "engines": { "vscode": "*" } }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/jsonc-parser": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.2.0.tgz", - "integrity": "sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==" + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "license": "MIT" }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" } } } diff --git a/extensions/vscode-colorize-tests/package.json b/extensions/vscode-colorize-tests/package.json index b416aeee1039..443aa726dd6f 100644 --- a/extensions/vscode-colorize-tests/package.json +++ b/extensions/vscode-colorize-tests/package.json @@ -19,10 +19,10 @@ "compile": "gulp compile-extension:vscode-colorize-tests" }, "dependencies": { - "jsonc-parser": "^3.2.0" + "jsonc-parser": "^3.3.1" }, "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "contributes": { "semanticTokenTypes": [ diff --git a/extensions/vscode-colorize-tests/test/colorize-fixtures/Dockerfile b/extensions/vscode-colorize-tests/test/colorize-fixtures/Dockerfile index 096488908cd6..8de1648b0a52 100644 --- a/extensions/vscode-colorize-tests/test/colorize-fixtures/Dockerfile +++ b/extensions/vscode-colorize-tests/test/colorize-fixtures/Dockerfile @@ -1,4 +1,4 @@ -FROM mcr.microsoft.com/devcontainers/base:ubuntu +FROM mcr.microsoft.com/devcontainers/base:ubuntu@sha256:c1613dbcdafe28e4b36ede9f3cb84a96e76765f50b3aeee87cb589eeb4c6dbca MAINTAINER Microsoft RUN apt-get install -y software-properties-common python diff --git a/extensions/vscode-colorize-tests/test/colorize-fixtures/test-241001.ts b/extensions/vscode-colorize-tests/test/colorize-fixtures/test-241001.ts new file mode 100644 index 000000000000..7405806f14ec --- /dev/null +++ b/extensions/vscode-colorize-tests/test/colorize-fixtures/test-241001.ts @@ -0,0 +1,36 @@ +const n = null; +const u = undefined; +const a = true || false; +type T = null | unknown | undefined | true | false; + +function bar(a?: string): string { return '' } +interface A { + b?: 2; + a: 1; +} +const obj = { + a: 1, +}; + +const obj1 = {}; +const obj2 = { + ...obj1, +} +function foo(param: string, ...rest) {} + +undefined +null +NaN +Infinity + +type One = 'zcxvf'; + +type Two = 'one' | 'two' & 'three'; + +const obj = { + one: 1 +}; + +type Rec = Record; + +new URL('./../../renderer/dist/index.html', import.meta.url); diff --git a/extensions/vscode-colorize-tests/test/colorize-fixtures/test-issue241715.ts b/extensions/vscode-colorize-tests/test/colorize-fixtures/test-issue241715.ts new file mode 100644 index 000000000000..ff95456b8563 --- /dev/null +++ b/extensions/vscode-colorize-tests/test/colorize-fixtures/test-issue241715.ts @@ -0,0 +1,47 @@ +type obj = { [key: string]: any }; + +function destruct({ start, end, message }: any): any { return start + end + message} +function destructArr([first, second]: any): any { return first + second} + +interface WebviewMessageUpdateEverything extends WebviewMessageBase {} + +type NegNum = -1 | -2 | -10; +const n: NegNum = -10; + +export interface OptionalMethod { + optMeth?(): any; +} + +window.addEventListener('message', event => { return event }); + +export function dayOfTheWeek(date: dayjs.Dayjs): string { + return date.format('ddd'); +} + +type N = never | any | unknown; + +type Truthy = T extends '' | 0 | false | null | undefined ? never : T; +export function guardedBoolean(value: T): value is Truthy { + return Boolean(value); +} + +type Truthy = T extends '' | 0 | false | null | undefined ? never : T; + +const enum EnumName { + one = 1, +} + +void 0; + +function* makeIterator(start = 0, end = Infinity, step = 1) { +} + +function makeDate(timestamp: number): Date; +function makeDate(m: number, d: number, y: number): Date; +function makeDate(mOrTimestamp: number, d?: number, y?: number): Date { + +} + +type StringNumberBooleans = [string, number, ...boolean[]]; +type StringBooleansNumber = [string, ...boolean[], number]; +type BooleansStringNumber = [...boolean[], string, number]; diff --git a/extensions/vscode-colorize-tests/test/colorize-results/test-241001_ts.json b/extensions/vscode-colorize-tests/test/colorize-results/test-241001_ts.json new file mode 100644 index 000000000000..67b874115af3 --- /dev/null +++ b/extensions/vscode-colorize-tests/test/colorize-results/test-241001_ts.json @@ -0,0 +1,3124 @@ +[ + { + "c": "const", + "t": "source.ts meta.var.expr.ts storage.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": " ", + "t": "source.ts meta.var.expr.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "n", + "t": "source.ts meta.var.expr.ts meta.var-single-variable.expr.ts meta.definition.variable.ts variable.other.constant.ts", + "r": { + "dark_plus": "variable.other.constant: #4FC1FF", + "light_plus": "variable.other.constant: #0070C1", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable.other.constant: #4FC1FF", + "hc_light": "variable.other.constant: #02715D", + "light_modern": "variable.other.constant: #0070C1" + } + }, + { + "c": " ", + "t": "source.ts meta.var.expr.ts meta.var-single-variable.expr.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.var.expr.ts keyword.operator.assignment.ts", + "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.ts meta.var.expr.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "null", + "t": "source.ts meta.var.expr.ts constant.language.null.ts", + "r": { + "dark_plus": "constant.language: #569CD6", + "light_plus": "constant.language: #0000FF", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "constant.language: #569CD6", + "dark_modern": "constant.language: #569CD6", + "hc_light": "constant.language: #0F4A85", + "light_modern": "constant.language: #0000FF" + } + }, + { + "c": ";", + "t": "source.ts punctuation.terminator.statement.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "const", + "t": "source.ts meta.var.expr.ts storage.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": " ", + "t": "source.ts meta.var.expr.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "u", + "t": "source.ts meta.var.expr.ts meta.var-single-variable.expr.ts meta.definition.variable.ts variable.other.constant.ts", + "r": { + "dark_plus": "variable.other.constant: #4FC1FF", + "light_plus": "variable.other.constant: #0070C1", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable.other.constant: #4FC1FF", + "hc_light": "variable.other.constant: #02715D", + "light_modern": "variable.other.constant: #0070C1" + } + }, + { + "c": " ", + "t": "source.ts meta.var.expr.ts meta.var-single-variable.expr.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.var.expr.ts keyword.operator.assignment.ts", + "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.ts meta.var.expr.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "undefined", + "t": "source.ts meta.var.expr.ts constant.language.undefined.ts", + "r": { + "dark_plus": "constant.language: #569CD6", + "light_plus": "constant.language: #0000FF", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "constant.language: #569CD6", + "dark_modern": "constant.language: #569CD6", + "hc_light": "constant.language: #0F4A85", + "light_modern": "constant.language: #0000FF" + } + }, + { + "c": ";", + "t": "source.ts punctuation.terminator.statement.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "const", + "t": "source.ts meta.var.expr.ts storage.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": " ", + "t": "source.ts meta.var.expr.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "a", + "t": "source.ts meta.var.expr.ts meta.var-single-variable.expr.ts meta.definition.variable.ts variable.other.constant.ts", + "r": { + "dark_plus": "variable.other.constant: #4FC1FF", + "light_plus": "variable.other.constant: #0070C1", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable.other.constant: #4FC1FF", + "hc_light": "variable.other.constant: #02715D", + "light_modern": "variable.other.constant: #0070C1" + } + }, + { + "c": " ", + "t": "source.ts meta.var.expr.ts meta.var-single-variable.expr.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.var.expr.ts keyword.operator.assignment.ts", + "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.ts meta.var.expr.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "true", + "t": "source.ts meta.var.expr.ts constant.language.boolean.true.ts", + "r": { + "dark_plus": "constant.language: #569CD6", + "light_plus": "constant.language: #0000FF", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "constant.language: #569CD6", + "dark_modern": "constant.language: #569CD6", + "hc_light": "constant.language: #0F4A85", + "light_modern": "constant.language: #0000FF" + } + }, + { + "c": " ", + "t": "source.ts meta.var.expr.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.var.expr.ts keyword.operator.logical.ts", + "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.ts meta.var.expr.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "false", + "t": "source.ts meta.var.expr.ts constant.language.boolean.false.ts", + "r": { + "dark_plus": "constant.language: #569CD6", + "light_plus": "constant.language: #0000FF", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "constant.language: #569CD6", + "dark_modern": "constant.language: #569CD6", + "hc_light": "constant.language: #0F4A85", + "light_modern": "constant.language: #0000FF" + } + }, + { + "c": ";", + "t": "source.ts punctuation.terminator.statement.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "type", + "t": "source.ts meta.type.declaration.ts storage.type.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": " ", + "t": "source.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts entity.name.type.alias.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": " ", + "t": "source.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts keyword.operator.assignment.ts", + "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.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "null", + "t": "source.ts meta.type.declaration.ts support.type.builtin.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": " ", + "t": "source.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts keyword.operator.type.ts", + "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.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "unknown", + "t": "source.ts meta.type.declaration.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": " ", + "t": "source.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts keyword.operator.type.ts", + "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.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "undefined", + "t": "source.ts meta.type.declaration.ts support.type.builtin.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": " ", + "t": "source.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts keyword.operator.type.ts", + "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.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "true", + "t": "source.ts meta.type.declaration.ts support.type.builtin.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": " ", + "t": "source.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts keyword.operator.type.ts", + "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.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "false", + "t": "source.ts meta.type.declaration.ts support.type.builtin.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": ";", + "t": "source.ts punctuation.terminator.statement.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "function", + "t": "source.ts meta.function.ts storage.type.function.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": " ", + "t": "source.ts meta.function.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "bar", + "t": "source.ts meta.function.ts meta.definition.function.ts entity.name.function.ts", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA", + "dark_modern": "entity.name.function: #DCDCAA", + "hc_light": "entity.name.function: #5E2CBC", + "light_modern": "entity.name.function: #795E26" + } + }, + { + "c": "(", + "t": "source.ts meta.function.ts meta.parameters.ts punctuation.definition.parameters.begin.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "a", + "t": "source.ts meta.function.ts meta.parameters.ts variable.parameter.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": "?", + "t": "source.ts meta.function.ts meta.parameters.ts keyword.operator.optional.ts", + "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.ts meta.function.ts meta.parameters.ts meta.type.annotation.ts keyword.operator.type.annotation.ts", + "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.ts meta.function.ts meta.parameters.ts meta.type.annotation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "string", + "t": "source.ts meta.function.ts meta.parameters.ts meta.type.annotation.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": ")", + "t": "source.ts meta.function.ts meta.parameters.ts punctuation.definition.parameters.end.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.return.type.ts keyword.operator.type.annotation.ts", + "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.ts meta.function.ts meta.return.type.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "string", + "t": "source.ts meta.function.ts meta.return.type.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": " ", + "t": "source.ts meta.function.ts meta.return.type.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.block.ts punctuation.definition.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "return", + "t": "source.ts meta.function.ts meta.block.ts keyword.control.flow.ts", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0", + "dark_modern": "keyword.control: #C586C0", + "hc_light": "keyword.control: #B5200D", + "light_modern": "keyword.control: #AF00DB" + } + }, + { + "c": " ", + "t": "source.ts meta.function.ts meta.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.block.ts string.quoted.single.ts punctuation.definition.string.begin.ts", + "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.ts meta.function.ts meta.block.ts string.quoted.single.ts punctuation.definition.string.end.ts", + "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.ts meta.function.ts meta.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.block.ts punctuation.definition.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "interface", + "t": "source.ts meta.interface.ts storage.type.interface.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": " ", + "t": "source.ts meta.interface.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "A", + "t": "source.ts meta.interface.ts entity.name.type.interface.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": " ", + "t": "source.ts meta.interface.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.interface.ts punctuation.definition.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.interface.ts meta.field.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "b", + "t": "source.ts meta.interface.ts meta.field.declaration.ts meta.definition.property.ts variable.object.property.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": "?", + "t": "source.ts meta.interface.ts meta.field.declaration.ts keyword.operator.optional.ts", + "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.ts meta.interface.ts meta.field.declaration.ts meta.type.annotation.ts keyword.operator.type.annotation.ts", + "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.ts meta.interface.ts meta.field.declaration.ts meta.type.annotation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "2", + "t": "source.ts meta.interface.ts meta.field.declaration.ts meta.type.annotation.ts constant.numeric.decimal.ts", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #098658", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #098658", + "hc_black": "constant.numeric: #B5CEA8", + "dark_modern": "constant.numeric: #B5CEA8", + "hc_light": "constant.numeric: #096D48", + "light_modern": "constant.numeric: #098658" + } + }, + { + "c": ";", + "t": "source.ts meta.interface.ts punctuation.terminator.statement.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.interface.ts meta.field.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "a", + "t": "source.ts meta.interface.ts meta.field.declaration.ts meta.definition.property.ts variable.object.property.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": ":", + "t": "source.ts meta.interface.ts meta.field.declaration.ts meta.type.annotation.ts keyword.operator.type.annotation.ts", + "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.ts meta.interface.ts meta.field.declaration.ts meta.type.annotation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "1", + "t": "source.ts meta.interface.ts meta.field.declaration.ts meta.type.annotation.ts constant.numeric.decimal.ts", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #098658", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #098658", + "hc_black": "constant.numeric: #B5CEA8", + "dark_modern": "constant.numeric: #B5CEA8", + "hc_light": "constant.numeric: #096D48", + "light_modern": "constant.numeric: #098658" + } + }, + { + "c": ";", + "t": "source.ts meta.interface.ts punctuation.terminator.statement.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.interface.ts punctuation.definition.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "const", + "t": "source.ts meta.var.expr.ts storage.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": " ", + "t": "source.ts meta.var.expr.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "obj", + "t": "source.ts meta.var.expr.ts meta.var-single-variable.expr.ts meta.definition.variable.ts variable.other.constant.ts", + "r": { + "dark_plus": "variable.other.constant: #4FC1FF", + "light_plus": "variable.other.constant: #0070C1", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable.other.constant: #4FC1FF", + "hc_light": "variable.other.constant: #02715D", + "light_modern": "variable.other.constant: #0070C1" + } + }, + { + "c": " ", + "t": "source.ts meta.var.expr.ts meta.var-single-variable.expr.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.var.expr.ts keyword.operator.assignment.ts", + "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.ts meta.var.expr.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.var.expr.ts meta.objectliteral.ts punctuation.definition.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.var.expr.ts meta.objectliteral.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "a", + "t": "source.ts meta.var.expr.ts meta.objectliteral.ts meta.object.member.ts meta.object-literal.key.ts", + "r": { + "dark_plus": "meta.object-literal.key: #9CDCFE", + "light_plus": "meta.object-literal.key: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "meta.object-literal.key: #9CDCFE", + "dark_modern": "meta.object-literal.key: #9CDCFE", + "hc_light": "meta.object-literal.key: #001080", + "light_modern": "meta.object-literal.key: #001080" + } + }, + { + "c": ":", + "t": "source.ts meta.var.expr.ts meta.objectliteral.ts meta.object.member.ts meta.object-literal.key.ts punctuation.separator.key-value.ts", + "r": { + "dark_plus": "meta.object-literal.key: #9CDCFE", + "light_plus": "meta.object-literal.key: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "meta.object-literal.key: #9CDCFE", + "dark_modern": "meta.object-literal.key: #9CDCFE", + "hc_light": "meta.object-literal.key: #001080", + "light_modern": "meta.object-literal.key: #001080" + } + }, + { + "c": " ", + "t": "source.ts meta.var.expr.ts meta.objectliteral.ts meta.object.member.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "1", + "t": "source.ts meta.var.expr.ts meta.objectliteral.ts meta.object.member.ts constant.numeric.decimal.ts", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #098658", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #098658", + "hc_black": "constant.numeric: #B5CEA8", + "dark_modern": "constant.numeric: #B5CEA8", + "hc_light": "constant.numeric: #096D48", + "light_modern": "constant.numeric: #098658" + } + }, + { + "c": ",", + "t": "source.ts meta.var.expr.ts meta.objectliteral.ts punctuation.separator.comma.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.var.expr.ts meta.objectliteral.ts punctuation.definition.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts punctuation.terminator.statement.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "const", + "t": "source.ts meta.var.expr.ts storage.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": " ", + "t": "source.ts meta.var.expr.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "obj1", + "t": "source.ts meta.var.expr.ts meta.var-single-variable.expr.ts meta.definition.variable.ts variable.other.constant.ts", + "r": { + "dark_plus": "variable.other.constant: #4FC1FF", + "light_plus": "variable.other.constant: #0070C1", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable.other.constant: #4FC1FF", + "hc_light": "variable.other.constant: #02715D", + "light_modern": "variable.other.constant: #0070C1" + } + }, + { + "c": " ", + "t": "source.ts meta.var.expr.ts meta.var-single-variable.expr.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.var.expr.ts keyword.operator.assignment.ts", + "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.ts meta.var.expr.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.var.expr.ts meta.objectliteral.ts punctuation.definition.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts punctuation.terminator.statement.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "const", + "t": "source.ts meta.var.expr.ts storage.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": " ", + "t": "source.ts meta.var.expr.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "obj2", + "t": "source.ts meta.var.expr.ts meta.var-single-variable.expr.ts meta.definition.variable.ts variable.other.constant.ts", + "r": { + "dark_plus": "variable.other.constant: #4FC1FF", + "light_plus": "variable.other.constant: #0070C1", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable.other.constant: #4FC1FF", + "hc_light": "variable.other.constant: #02715D", + "light_modern": "variable.other.constant: #0070C1" + } + }, + { + "c": " ", + "t": "source.ts meta.var.expr.ts meta.var-single-variable.expr.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.var.expr.ts keyword.operator.assignment.ts", + "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.ts meta.var.expr.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.var.expr.ts meta.objectliteral.ts punctuation.definition.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.var.expr.ts meta.objectliteral.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.var.expr.ts meta.objectliteral.ts meta.object.member.ts keyword.operator.spread.ts", + "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": "obj1", + "t": "source.ts meta.var.expr.ts meta.objectliteral.ts meta.object.member.ts variable.other.readwrite.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": ",", + "t": "source.ts meta.var.expr.ts meta.objectliteral.ts punctuation.separator.comma.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.var.expr.ts meta.objectliteral.ts punctuation.definition.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "function", + "t": "source.ts meta.function.ts storage.type.function.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": " ", + "t": "source.ts meta.function.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "foo", + "t": "source.ts meta.function.ts meta.definition.function.ts entity.name.function.ts", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA", + "dark_modern": "entity.name.function: #DCDCAA", + "hc_light": "entity.name.function: #5E2CBC", + "light_modern": "entity.name.function: #795E26" + } + }, + { + "c": "(", + "t": "source.ts meta.function.ts meta.parameters.ts punctuation.definition.parameters.begin.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "param", + "t": "source.ts meta.function.ts meta.parameters.ts variable.parameter.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": ":", + "t": "source.ts meta.function.ts meta.parameters.ts meta.type.annotation.ts keyword.operator.type.annotation.ts", + "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.ts meta.function.ts meta.parameters.ts meta.type.annotation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "string", + "t": "source.ts meta.function.ts meta.parameters.ts meta.type.annotation.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": ",", + "t": "source.ts meta.function.ts meta.parameters.ts punctuation.separator.parameter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.parameters.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.parameters.ts keyword.operator.rest.ts", + "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": "rest", + "t": "source.ts meta.function.ts meta.parameters.ts variable.parameter.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": ")", + "t": "source.ts meta.function.ts meta.parameters.ts punctuation.definition.parameters.end.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.block.ts punctuation.definition.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "undefined", + "t": "source.ts constant.language.undefined.ts", + "r": { + "dark_plus": "constant.language: #569CD6", + "light_plus": "constant.language: #0000FF", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "constant.language: #569CD6", + "dark_modern": "constant.language: #569CD6", + "hc_light": "constant.language: #0F4A85", + "light_modern": "constant.language: #0000FF" + } + }, + { + "c": "null", + "t": "source.ts constant.language.null.ts", + "r": { + "dark_plus": "constant.language: #569CD6", + "light_plus": "constant.language: #0000FF", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "constant.language: #569CD6", + "dark_modern": "constant.language: #569CD6", + "hc_light": "constant.language: #0F4A85", + "light_modern": "constant.language: #0000FF" + } + }, + { + "c": "NaN", + "t": "source.ts constant.language.nan.ts", + "r": { + "dark_plus": "constant.language: #569CD6", + "light_plus": "constant.language: #0000FF", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "constant.language: #569CD6", + "dark_modern": "constant.language: #569CD6", + "hc_light": "constant.language: #0F4A85", + "light_modern": "constant.language: #0000FF" + } + }, + { + "c": "Infinity", + "t": "source.ts constant.language.infinity.ts", + "r": { + "dark_plus": "constant.language: #569CD6", + "light_plus": "constant.language: #0000FF", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "constant.language: #569CD6", + "dark_modern": "constant.language: #569CD6", + "hc_light": "constant.language: #0F4A85", + "light_modern": "constant.language: #0000FF" + } + }, + { + "c": "type", + "t": "source.ts meta.type.declaration.ts storage.type.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": " ", + "t": "source.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "One", + "t": "source.ts meta.type.declaration.ts entity.name.type.alias.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": " ", + "t": "source.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts keyword.operator.assignment.ts", + "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.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts string.quoted.single.ts punctuation.definition.string.begin.ts", + "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": "zcxvf", + "t": "source.ts meta.type.declaration.ts string.quoted.single.ts", + "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.ts meta.type.declaration.ts string.quoted.single.ts punctuation.definition.string.end.ts", + "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.ts punctuation.terminator.statement.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "type", + "t": "source.ts meta.type.declaration.ts storage.type.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": " ", + "t": "source.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "Two", + "t": "source.ts meta.type.declaration.ts entity.name.type.alias.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": " ", + "t": "source.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts keyword.operator.assignment.ts", + "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.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts string.quoted.single.ts punctuation.definition.string.begin.ts", + "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": "one", + "t": "source.ts meta.type.declaration.ts string.quoted.single.ts", + "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.ts meta.type.declaration.ts string.quoted.single.ts punctuation.definition.string.end.ts", + "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.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts keyword.operator.type.ts", + "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.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts string.quoted.single.ts punctuation.definition.string.begin.ts", + "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": "two", + "t": "source.ts meta.type.declaration.ts string.quoted.single.ts", + "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.ts meta.type.declaration.ts string.quoted.single.ts punctuation.definition.string.end.ts", + "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.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts keyword.operator.type.ts", + "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.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts string.quoted.single.ts punctuation.definition.string.begin.ts", + "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": "three", + "t": "source.ts meta.type.declaration.ts string.quoted.single.ts", + "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.ts meta.type.declaration.ts string.quoted.single.ts punctuation.definition.string.end.ts", + "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.ts punctuation.terminator.statement.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "const", + "t": "source.ts meta.var.expr.ts storage.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": " ", + "t": "source.ts meta.var.expr.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "obj", + "t": "source.ts meta.var.expr.ts meta.var-single-variable.expr.ts meta.definition.variable.ts variable.other.constant.ts", + "r": { + "dark_plus": "variable.other.constant: #4FC1FF", + "light_plus": "variable.other.constant: #0070C1", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable.other.constant: #4FC1FF", + "hc_light": "variable.other.constant: #02715D", + "light_modern": "variable.other.constant: #0070C1" + } + }, + { + "c": " ", + "t": "source.ts meta.var.expr.ts meta.var-single-variable.expr.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.var.expr.ts keyword.operator.assignment.ts", + "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.ts meta.var.expr.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.var.expr.ts meta.objectliteral.ts punctuation.definition.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.var.expr.ts meta.objectliteral.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "one", + "t": "source.ts meta.var.expr.ts meta.objectliteral.ts meta.object.member.ts meta.object-literal.key.ts", + "r": { + "dark_plus": "meta.object-literal.key: #9CDCFE", + "light_plus": "meta.object-literal.key: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "meta.object-literal.key: #9CDCFE", + "dark_modern": "meta.object-literal.key: #9CDCFE", + "hc_light": "meta.object-literal.key: #001080", + "light_modern": "meta.object-literal.key: #001080" + } + }, + { + "c": ":", + "t": "source.ts meta.var.expr.ts meta.objectliteral.ts meta.object.member.ts meta.object-literal.key.ts punctuation.separator.key-value.ts", + "r": { + "dark_plus": "meta.object-literal.key: #9CDCFE", + "light_plus": "meta.object-literal.key: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "meta.object-literal.key: #9CDCFE", + "dark_modern": "meta.object-literal.key: #9CDCFE", + "hc_light": "meta.object-literal.key: #001080", + "light_modern": "meta.object-literal.key: #001080" + } + }, + { + "c": " ", + "t": "source.ts meta.var.expr.ts meta.objectliteral.ts meta.object.member.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "1", + "t": "source.ts meta.var.expr.ts meta.objectliteral.ts meta.object.member.ts constant.numeric.decimal.ts", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #098658", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #098658", + "hc_black": "constant.numeric: #B5CEA8", + "dark_modern": "constant.numeric: #B5CEA8", + "hc_light": "constant.numeric: #096D48", + "light_modern": "constant.numeric: #098658" + } + }, + { + "c": "}", + "t": "source.ts meta.var.expr.ts meta.objectliteral.ts punctuation.definition.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts punctuation.terminator.statement.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "type", + "t": "source.ts meta.type.declaration.ts storage.type.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": " ", + "t": "source.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "Rec", + "t": "source.ts meta.type.declaration.ts entity.name.type.alias.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": " ", + "t": "source.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts keyword.operator.assignment.ts", + "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.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "Record", + "t": "source.ts meta.type.declaration.ts entity.name.type.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": "<", + "t": "source.ts meta.type.declaration.ts meta.type.parameters.ts punctuation.definition.typeparameters.begin.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "string", + "t": "source.ts meta.type.declaration.ts meta.type.parameters.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": ",", + "t": "source.ts meta.type.declaration.ts meta.type.parameters.ts punctuation.separator.comma.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts meta.type.parameters.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "string", + "t": "source.ts meta.type.declaration.ts meta.type.parameters.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": ">", + "t": "source.ts meta.type.declaration.ts meta.type.parameters.ts punctuation.definition.typeparameters.end.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts punctuation.terminator.statement.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "new", + "t": "source.ts new.expr.ts keyword.operator.new.ts", + "r": { + "dark_plus": "keyword.operator.new: #569CD6", + "light_plus": "keyword.operator.new: #0000FF", + "dark_vs": "keyword.operator.new: #569CD6", + "light_vs": "keyword.operator.new: #0000FF", + "hc_black": "keyword.operator.new: #569CD6", + "dark_modern": "keyword.operator.new: #569CD6", + "hc_light": "keyword.operator.new: #0F4A85", + "light_modern": "keyword.operator.new: #0000FF" + } + }, + { + "c": " ", + "t": "source.ts new.expr.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "URL", + "t": "source.ts new.expr.ts meta.function-call.ts entity.name.function.ts", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA", + "dark_modern": "entity.name.function: #DCDCAA", + "hc_light": "entity.name.function: #5E2CBC", + "light_modern": "entity.name.function: #795E26" + } + }, + { + "c": "(", + "t": "source.ts new.expr.ts meta.brace.round.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts new.expr.ts string.quoted.single.ts punctuation.definition.string.begin.ts", + "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": "./../../renderer/dist/index.html", + "t": "source.ts new.expr.ts string.quoted.single.ts", + "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.ts new.expr.ts string.quoted.single.ts punctuation.definition.string.end.ts", + "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.ts new.expr.ts punctuation.separator.comma.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts new.expr.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "import", + "t": "source.ts new.expr.ts keyword.control.import.ts", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0", + "dark_modern": "keyword.control: #C586C0", + "hc_light": "keyword.control: #B5200D", + "light_modern": "keyword.control: #AF00DB" + } + }, + { + "c": ".", + "t": "source.ts new.expr.ts punctuation.accessor.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "meta", + "t": "source.ts new.expr.ts support.variable.property.importmeta.ts", + "r": { + "dark_plus": "support.variable: #9CDCFE", + "light_plus": "support.variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.variable: #9CDCFE", + "dark_modern": "support.variable: #9CDCFE", + "hc_light": "support.variable: #001080", + "light_modern": "support.variable: #001080" + } + }, + { + "c": ".", + "t": "source.ts new.expr.ts punctuation.accessor.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "url", + "t": "source.ts new.expr.ts variable.other.property.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": ")", + "t": "source.ts new.expr.ts meta.brace.round.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts punctuation.terminator.statement.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "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-issue241715_ts.json b/extensions/vscode-colorize-tests/test/colorize-results/test-issue241715_ts.json new file mode 100644 index 000000000000..96a67b213ca2 --- /dev/null +++ b/extensions/vscode-colorize-tests/test/colorize-results/test-issue241715_ts.json @@ -0,0 +1,6876 @@ +[ + { + "c": "type", + "t": "source.ts meta.type.declaration.ts storage.type.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": " ", + "t": "source.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "obj", + "t": "source.ts meta.type.declaration.ts entity.name.type.alias.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": " ", + "t": "source.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts keyword.operator.assignment.ts", + "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.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts meta.object.type.ts punctuation.definition.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts meta.object.type.ts meta.indexer.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts meta.object.type.ts meta.indexer.declaration.ts meta.brace.square.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "key", + "t": "source.ts meta.type.declaration.ts meta.object.type.ts meta.indexer.declaration.ts variable.parameter.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": ":", + "t": "source.ts meta.type.declaration.ts meta.object.type.ts meta.indexer.declaration.ts meta.type.annotation.ts keyword.operator.type.annotation.ts", + "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.ts meta.type.declaration.ts meta.object.type.ts meta.indexer.declaration.ts meta.type.annotation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "string", + "t": "source.ts meta.type.declaration.ts meta.object.type.ts meta.indexer.declaration.ts meta.type.annotation.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": "]", + "t": "source.ts meta.type.declaration.ts meta.object.type.ts meta.indexer.declaration.ts meta.brace.square.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts meta.object.type.ts meta.type.annotation.ts keyword.operator.type.annotation.ts", + "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.ts meta.type.declaration.ts meta.object.type.ts meta.type.annotation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "any", + "t": "source.ts meta.type.declaration.ts meta.object.type.ts meta.type.annotation.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": " ", + "t": "source.ts meta.type.declaration.ts meta.object.type.ts meta.type.annotation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts meta.object.type.ts punctuation.definition.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts punctuation.terminator.statement.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "function", + "t": "source.ts meta.function.ts storage.type.function.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": " ", + "t": "source.ts meta.function.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "destruct", + "t": "source.ts meta.function.ts meta.definition.function.ts entity.name.function.ts", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA", + "dark_modern": "entity.name.function: #DCDCAA", + "hc_light": "entity.name.function: #5E2CBC", + "light_modern": "entity.name.function: #795E26" + } + }, + { + "c": "(", + "t": "source.ts meta.function.ts meta.parameters.ts punctuation.definition.parameters.begin.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.parameters.ts meta.parameter.object-binding-pattern.ts punctuation.definition.binding-pattern.object.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.parameters.ts meta.parameter.object-binding-pattern.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "start", + "t": "source.ts meta.function.ts meta.parameters.ts meta.parameter.object-binding-pattern.ts variable.parameter.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": ",", + "t": "source.ts meta.function.ts meta.parameters.ts meta.parameter.object-binding-pattern.ts punctuation.separator.comma.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.parameters.ts meta.parameter.object-binding-pattern.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "end", + "t": "source.ts meta.function.ts meta.parameters.ts meta.parameter.object-binding-pattern.ts variable.parameter.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": ",", + "t": "source.ts meta.function.ts meta.parameters.ts meta.parameter.object-binding-pattern.ts punctuation.separator.comma.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.parameters.ts meta.parameter.object-binding-pattern.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "message", + "t": "source.ts meta.function.ts meta.parameters.ts meta.parameter.object-binding-pattern.ts variable.parameter.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": " ", + "t": "source.ts meta.function.ts meta.parameters.ts meta.parameter.object-binding-pattern.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.parameters.ts meta.parameter.object-binding-pattern.ts punctuation.definition.binding-pattern.object.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.parameters.ts meta.type.annotation.ts keyword.operator.type.annotation.ts", + "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.ts meta.function.ts meta.parameters.ts meta.type.annotation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "any", + "t": "source.ts meta.function.ts meta.parameters.ts meta.type.annotation.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": ")", + "t": "source.ts meta.function.ts meta.parameters.ts punctuation.definition.parameters.end.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.return.type.ts keyword.operator.type.annotation.ts", + "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.ts meta.function.ts meta.return.type.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "any", + "t": "source.ts meta.function.ts meta.return.type.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": " ", + "t": "source.ts meta.function.ts meta.return.type.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.block.ts punctuation.definition.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "return", + "t": "source.ts meta.function.ts meta.block.ts keyword.control.flow.ts", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0", + "dark_modern": "keyword.control: #C586C0", + "hc_light": "keyword.control: #B5200D", + "light_modern": "keyword.control: #AF00DB" + } + }, + { + "c": " ", + "t": "source.ts meta.function.ts meta.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "start", + "t": "source.ts meta.function.ts meta.block.ts variable.other.readwrite.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": " ", + "t": "source.ts meta.function.ts meta.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.block.ts keyword.operator.arithmetic.ts", + "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.ts meta.function.ts meta.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "end", + "t": "source.ts meta.function.ts meta.block.ts variable.other.readwrite.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": " ", + "t": "source.ts meta.function.ts meta.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.block.ts keyword.operator.arithmetic.ts", + "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.ts meta.function.ts meta.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "message", + "t": "source.ts meta.function.ts meta.block.ts variable.other.readwrite.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": "}", + "t": "source.ts meta.function.ts meta.block.ts punctuation.definition.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "function", + "t": "source.ts meta.function.ts storage.type.function.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": " ", + "t": "source.ts meta.function.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "destructArr", + "t": "source.ts meta.function.ts meta.definition.function.ts entity.name.function.ts", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA", + "dark_modern": "entity.name.function: #DCDCAA", + "hc_light": "entity.name.function: #5E2CBC", + "light_modern": "entity.name.function: #795E26" + } + }, + { + "c": "(", + "t": "source.ts meta.function.ts meta.parameters.ts punctuation.definition.parameters.begin.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.parameters.ts meta.paramter.array-binding-pattern.ts punctuation.definition.binding-pattern.array.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "first", + "t": "source.ts meta.function.ts meta.parameters.ts meta.paramter.array-binding-pattern.ts variable.parameter.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": ",", + "t": "source.ts meta.function.ts meta.parameters.ts meta.paramter.array-binding-pattern.ts punctuation.separator.comma.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.parameters.ts meta.paramter.array-binding-pattern.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "second", + "t": "source.ts meta.function.ts meta.parameters.ts meta.paramter.array-binding-pattern.ts variable.parameter.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": "]", + "t": "source.ts meta.function.ts meta.parameters.ts meta.paramter.array-binding-pattern.ts punctuation.definition.binding-pattern.array.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.parameters.ts meta.type.annotation.ts keyword.operator.type.annotation.ts", + "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.ts meta.function.ts meta.parameters.ts meta.type.annotation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "any", + "t": "source.ts meta.function.ts meta.parameters.ts meta.type.annotation.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": ")", + "t": "source.ts meta.function.ts meta.parameters.ts punctuation.definition.parameters.end.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.return.type.ts keyword.operator.type.annotation.ts", + "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.ts meta.function.ts meta.return.type.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "any", + "t": "source.ts meta.function.ts meta.return.type.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": " ", + "t": "source.ts meta.function.ts meta.return.type.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.block.ts punctuation.definition.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "return", + "t": "source.ts meta.function.ts meta.block.ts keyword.control.flow.ts", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0", + "dark_modern": "keyword.control: #C586C0", + "hc_light": "keyword.control: #B5200D", + "light_modern": "keyword.control: #AF00DB" + } + }, + { + "c": " ", + "t": "source.ts meta.function.ts meta.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "first", + "t": "source.ts meta.function.ts meta.block.ts variable.other.readwrite.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": " ", + "t": "source.ts meta.function.ts meta.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.block.ts keyword.operator.arithmetic.ts", + "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.ts meta.function.ts meta.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "second", + "t": "source.ts meta.function.ts meta.block.ts variable.other.readwrite.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": "}", + "t": "source.ts meta.function.ts meta.block.ts punctuation.definition.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "interface", + "t": "source.ts meta.interface.ts storage.type.interface.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": " ", + "t": "source.ts meta.interface.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "WebviewMessageUpdateEverything", + "t": "source.ts meta.interface.ts entity.name.type.interface.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": " ", + "t": "source.ts meta.interface.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "extends", + "t": "source.ts meta.interface.ts storage.modifier.ts", + "r": { + "dark_plus": "storage.modifier: #569CD6", + "light_plus": "storage.modifier: #0000FF", + "dark_vs": "storage.modifier: #569CD6", + "light_vs": "storage.modifier: #0000FF", + "hc_black": "storage.modifier: #569CD6", + "dark_modern": "storage.modifier: #569CD6", + "hc_light": "storage.modifier: #0F4A85", + "light_modern": "storage.modifier: #0000FF" + } + }, + { + "c": " ", + "t": "source.ts meta.interface.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "WebviewMessageBase", + "t": "source.ts meta.interface.ts entity.other.inherited-class.ts", + "r": { + "dark_plus": "entity.other.inherited-class: #4EC9B0", + "light_plus": "entity.other.inherited-class: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.other.inherited-class: #4EC9B0", + "dark_modern": "entity.other.inherited-class: #4EC9B0", + "hc_light": "entity.other.inherited-class: #185E73", + "light_modern": "entity.other.inherited-class: #267F99" + } + }, + { + "c": " ", + "t": "source.ts meta.interface.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.interface.ts punctuation.definition.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "type", + "t": "source.ts meta.type.declaration.ts storage.type.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": " ", + "t": "source.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "NegNum", + "t": "source.ts meta.type.declaration.ts entity.name.type.alias.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": " ", + "t": "source.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts keyword.operator.assignment.ts", + "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.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "1", + "t": "source.ts meta.type.declaration.ts constant.numeric.decimal.ts", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #098658", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #098658", + "hc_black": "constant.numeric: #B5CEA8", + "dark_modern": "constant.numeric: #B5CEA8", + "hc_light": "constant.numeric: #096D48", + "light_modern": "constant.numeric: #098658" + } + }, + { + "c": " ", + "t": "source.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts keyword.operator.type.ts", + "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.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "2", + "t": "source.ts meta.type.declaration.ts constant.numeric.decimal.ts", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #098658", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #098658", + "hc_black": "constant.numeric: #B5CEA8", + "dark_modern": "constant.numeric: #B5CEA8", + "hc_light": "constant.numeric: #096D48", + "light_modern": "constant.numeric: #098658" + } + }, + { + "c": " ", + "t": "source.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts keyword.operator.type.ts", + "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.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "source.ts meta.type.declaration.ts constant.numeric.decimal.ts", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #098658", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #098658", + "hc_black": "constant.numeric: #B5CEA8", + "dark_modern": "constant.numeric: #B5CEA8", + "hc_light": "constant.numeric: #096D48", + "light_modern": "constant.numeric: #098658" + } + }, + { + "c": ";", + "t": "source.ts punctuation.terminator.statement.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "const", + "t": "source.ts meta.var.expr.ts storage.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": " ", + "t": "source.ts meta.var.expr.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "n", + "t": "source.ts meta.var.expr.ts meta.var-single-variable.expr.ts meta.definition.variable.ts variable.other.constant.ts", + "r": { + "dark_plus": "variable.other.constant: #4FC1FF", + "light_plus": "variable.other.constant: #0070C1", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable.other.constant: #4FC1FF", + "hc_light": "variable.other.constant: #02715D", + "light_modern": "variable.other.constant: #0070C1" + } + }, + { + "c": ":", + "t": "source.ts meta.var.expr.ts meta.var-single-variable.expr.ts meta.type.annotation.ts keyword.operator.type.annotation.ts", + "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.ts meta.var.expr.ts meta.var-single-variable.expr.ts meta.type.annotation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "NegNum", + "t": "source.ts meta.var.expr.ts meta.var-single-variable.expr.ts meta.type.annotation.ts entity.name.type.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": " ", + "t": "source.ts meta.var.expr.ts meta.var-single-variable.expr.ts meta.type.annotation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.var.expr.ts keyword.operator.assignment.ts", + "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.ts meta.var.expr.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.var.expr.ts keyword.operator.arithmetic.ts", + "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": "10", + "t": "source.ts meta.var.expr.ts constant.numeric.decimal.ts", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #098658", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #098658", + "hc_black": "constant.numeric: #B5CEA8", + "dark_modern": "constant.numeric: #B5CEA8", + "hc_light": "constant.numeric: #096D48", + "light_modern": "constant.numeric: #098658" + } + }, + { + "c": ";", + "t": "source.ts punctuation.terminator.statement.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "export", + "t": "source.ts meta.interface.ts keyword.control.export.ts", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0", + "dark_modern": "keyword.control: #C586C0", + "hc_light": "keyword.control: #B5200D", + "light_modern": "keyword.control: #AF00DB" + } + }, + { + "c": " ", + "t": "source.ts meta.interface.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "interface", + "t": "source.ts meta.interface.ts storage.type.interface.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": " ", + "t": "source.ts meta.interface.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "OptionalMethod", + "t": "source.ts meta.interface.ts entity.name.type.interface.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": " ", + "t": "source.ts meta.interface.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.interface.ts punctuation.definition.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.interface.ts meta.method.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "optMeth", + "t": "source.ts meta.interface.ts meta.method.declaration.ts meta.definition.method.ts entity.name.function.ts", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA", + "dark_modern": "entity.name.function: #DCDCAA", + "hc_light": "entity.name.function: #5E2CBC", + "light_modern": "entity.name.function: #795E26" + } + }, + { + "c": "?", + "t": "source.ts meta.interface.ts meta.method.declaration.ts keyword.operator.optional.ts", + "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.ts meta.interface.ts meta.method.declaration.ts meta.parameters.ts punctuation.definition.parameters.begin.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.interface.ts meta.method.declaration.ts meta.parameters.ts punctuation.definition.parameters.end.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.interface.ts meta.method.declaration.ts meta.return.type.ts keyword.operator.type.annotation.ts", + "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.ts meta.interface.ts meta.method.declaration.ts meta.return.type.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "any", + "t": "source.ts meta.interface.ts meta.method.declaration.ts meta.return.type.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": ";", + "t": "source.ts meta.interface.ts punctuation.terminator.statement.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.interface.ts punctuation.definition.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "window", + "t": "source.ts meta.function-call.ts variable.other.object.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": ".", + "t": "source.ts meta.function-call.ts punctuation.accessor.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "addEventListener", + "t": "source.ts meta.function-call.ts entity.name.function.ts", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA", + "dark_modern": "entity.name.function: #DCDCAA", + "hc_light": "entity.name.function: #5E2CBC", + "light_modern": "entity.name.function: #795E26" + } + }, + { + "c": "(", + "t": "source.ts meta.brace.round.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts string.quoted.single.ts punctuation.definition.string.begin.ts", + "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": "message", + "t": "source.ts string.quoted.single.ts", + "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.ts string.quoted.single.ts punctuation.definition.string.end.ts", + "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.ts punctuation.separator.comma.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "event", + "t": "source.ts meta.arrow.ts variable.parameter.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": " ", + "t": "source.ts meta.arrow.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.arrow.ts storage.type.function.arrow.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": " ", + "t": "source.ts meta.arrow.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.arrow.ts meta.block.ts punctuation.definition.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.arrow.ts meta.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "return", + "t": "source.ts meta.arrow.ts meta.block.ts keyword.control.flow.ts", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0", + "dark_modern": "keyword.control: #C586C0", + "hc_light": "keyword.control: #B5200D", + "light_modern": "keyword.control: #AF00DB" + } + }, + { + "c": " ", + "t": "source.ts meta.arrow.ts meta.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "event", + "t": "source.ts meta.arrow.ts meta.block.ts variable.other.readwrite.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": " ", + "t": "source.ts meta.arrow.ts meta.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.arrow.ts meta.block.ts punctuation.definition.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.brace.round.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts punctuation.terminator.statement.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "export", + "t": "source.ts meta.function.ts keyword.control.export.ts", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0", + "dark_modern": "keyword.control: #C586C0", + "hc_light": "keyword.control: #B5200D", + "light_modern": "keyword.control: #AF00DB" + } + }, + { + "c": " ", + "t": "source.ts meta.function.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "function", + "t": "source.ts meta.function.ts storage.type.function.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": " ", + "t": "source.ts meta.function.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "dayOfTheWeek", + "t": "source.ts meta.function.ts meta.definition.function.ts entity.name.function.ts", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA", + "dark_modern": "entity.name.function: #DCDCAA", + "hc_light": "entity.name.function: #5E2CBC", + "light_modern": "entity.name.function: #795E26" + } + }, + { + "c": "(", + "t": "source.ts meta.function.ts meta.parameters.ts punctuation.definition.parameters.begin.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "date", + "t": "source.ts meta.function.ts meta.parameters.ts variable.parameter.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": ":", + "t": "source.ts meta.function.ts meta.parameters.ts meta.type.annotation.ts keyword.operator.type.annotation.ts", + "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.ts meta.function.ts meta.parameters.ts meta.type.annotation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "dayjs", + "t": "source.ts meta.function.ts meta.parameters.ts meta.type.annotation.ts entity.name.type.module.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": ".", + "t": "source.ts meta.function.ts meta.parameters.ts meta.type.annotation.ts punctuation.accessor.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "Dayjs", + "t": "source.ts meta.function.ts meta.parameters.ts meta.type.annotation.ts entity.name.type.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": ")", + "t": "source.ts meta.function.ts meta.parameters.ts punctuation.definition.parameters.end.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.return.type.ts keyword.operator.type.annotation.ts", + "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.ts meta.function.ts meta.return.type.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "string", + "t": "source.ts meta.function.ts meta.return.type.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": " ", + "t": "source.ts meta.function.ts meta.return.type.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.block.ts punctuation.definition.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "return", + "t": "source.ts meta.function.ts meta.block.ts keyword.control.flow.ts", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0", + "dark_modern": "keyword.control: #C586C0", + "hc_light": "keyword.control: #B5200D", + "light_modern": "keyword.control: #AF00DB" + } + }, + { + "c": " ", + "t": "source.ts meta.function.ts meta.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "date", + "t": "source.ts meta.function.ts meta.block.ts meta.function-call.ts variable.other.object.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": ".", + "t": "source.ts meta.function.ts meta.block.ts meta.function-call.ts punctuation.accessor.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "format", + "t": "source.ts meta.function.ts meta.block.ts meta.function-call.ts entity.name.function.ts", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA", + "dark_modern": "entity.name.function: #DCDCAA", + "hc_light": "entity.name.function: #5E2CBC", + "light_modern": "entity.name.function: #795E26" + } + }, + { + "c": "(", + "t": "source.ts meta.function.ts meta.block.ts meta.brace.round.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.block.ts string.quoted.single.ts punctuation.definition.string.begin.ts", + "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": "ddd", + "t": "source.ts meta.function.ts meta.block.ts string.quoted.single.ts", + "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.ts meta.function.ts meta.block.ts string.quoted.single.ts punctuation.definition.string.end.ts", + "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.ts meta.function.ts meta.block.ts meta.brace.round.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.block.ts punctuation.terminator.statement.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.block.ts punctuation.definition.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "type", + "t": "source.ts meta.type.declaration.ts storage.type.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": " ", + "t": "source.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "N", + "t": "source.ts meta.type.declaration.ts entity.name.type.alias.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": " ", + "t": "source.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts keyword.operator.assignment.ts", + "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.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "never", + "t": "source.ts meta.type.declaration.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": " ", + "t": "source.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts keyword.operator.type.ts", + "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.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "any", + "t": "source.ts meta.type.declaration.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": " ", + "t": "source.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts keyword.operator.type.ts", + "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.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "unknown", + "t": "source.ts meta.type.declaration.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": ";", + "t": "source.ts punctuation.terminator.statement.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "type", + "t": "source.ts meta.type.declaration.ts storage.type.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": " ", + "t": "source.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "Truthy", + "t": "source.ts meta.type.declaration.ts entity.name.type.alias.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": "<", + "t": "source.ts meta.type.declaration.ts meta.type.parameters.ts punctuation.definition.typeparameters.begin.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts meta.type.parameters.ts entity.name.type.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": ">", + "t": "source.ts meta.type.declaration.ts meta.type.parameters.ts punctuation.definition.typeparameters.end.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts keyword.operator.assignment.ts", + "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.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts entity.name.type.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": " ", + "t": "source.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "extends", + "t": "source.ts meta.type.declaration.ts storage.modifier.ts", + "r": { + "dark_plus": "storage.modifier: #569CD6", + "light_plus": "storage.modifier: #0000FF", + "dark_vs": "storage.modifier: #569CD6", + "light_vs": "storage.modifier: #0000FF", + "hc_black": "storage.modifier: #569CD6", + "dark_modern": "storage.modifier: #569CD6", + "hc_light": "storage.modifier: #0F4A85", + "light_modern": "storage.modifier: #0000FF" + } + }, + { + "c": " ", + "t": "source.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts string.quoted.single.ts punctuation.definition.string.begin.ts", + "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.ts meta.type.declaration.ts string.quoted.single.ts punctuation.definition.string.end.ts", + "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.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts keyword.operator.type.ts", + "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.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "0", + "t": "source.ts meta.type.declaration.ts constant.numeric.decimal.ts", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #098658", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #098658", + "hc_black": "constant.numeric: #B5CEA8", + "dark_modern": "constant.numeric: #B5CEA8", + "hc_light": "constant.numeric: #096D48", + "light_modern": "constant.numeric: #098658" + } + }, + { + "c": " ", + "t": "source.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts keyword.operator.type.ts", + "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.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "false", + "t": "source.ts meta.type.declaration.ts support.type.builtin.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": " ", + "t": "source.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts keyword.operator.type.ts", + "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.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "null", + "t": "source.ts meta.type.declaration.ts support.type.builtin.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": " ", + "t": "source.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts keyword.operator.type.ts", + "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.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "undefined", + "t": "source.ts meta.type.declaration.ts support.type.builtin.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": " ", + "t": "source.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts keyword.operator.ternary.ts", + "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.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "never", + "t": "source.ts meta.type.declaration.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": " ", + "t": "source.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts keyword.operator.ternary.ts", + "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.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts entity.name.type.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": ";", + "t": "source.ts punctuation.terminator.statement.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "export", + "t": "source.ts meta.function.ts keyword.control.export.ts", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0", + "dark_modern": "keyword.control: #C586C0", + "hc_light": "keyword.control: #B5200D", + "light_modern": "keyword.control: #AF00DB" + } + }, + { + "c": " ", + "t": "source.ts meta.function.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "function", + "t": "source.ts meta.function.ts storage.type.function.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": " ", + "t": "source.ts meta.function.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "guardedBoolean", + "t": "source.ts meta.function.ts meta.definition.function.ts entity.name.function.ts", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA", + "dark_modern": "entity.name.function: #DCDCAA", + "hc_light": "entity.name.function: #5E2CBC", + "light_modern": "entity.name.function: #795E26" + } + }, + { + "c": "<", + "t": "source.ts meta.function.ts meta.type.parameters.ts punctuation.definition.typeparameters.begin.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.type.parameters.ts entity.name.type.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": ">", + "t": "source.ts meta.function.ts meta.type.parameters.ts punctuation.definition.typeparameters.end.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.parameters.ts punctuation.definition.parameters.begin.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "value", + "t": "source.ts meta.function.ts meta.parameters.ts variable.parameter.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": ":", + "t": "source.ts meta.function.ts meta.parameters.ts meta.type.annotation.ts keyword.operator.type.annotation.ts", + "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.ts meta.function.ts meta.parameters.ts meta.type.annotation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.parameters.ts meta.type.annotation.ts entity.name.type.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": ")", + "t": "source.ts meta.function.ts meta.parameters.ts punctuation.definition.parameters.end.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.return.type.ts keyword.operator.type.annotation.ts", + "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.ts meta.function.ts meta.return.type.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "value", + "t": "source.ts meta.function.ts meta.return.type.ts variable.parameter.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": " ", + "t": "source.ts meta.function.ts meta.return.type.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "is", + "t": "source.ts meta.function.ts meta.return.type.ts keyword.operator.expression.is.ts", + "r": { + "dark_plus": "keyword.operator.expression: #569CD6", + "light_plus": "keyword.operator.expression: #0000FF", + "dark_vs": "keyword.operator.expression: #569CD6", + "light_vs": "keyword.operator.expression: #0000FF", + "hc_black": "keyword.operator.expression: #569CD6", + "dark_modern": "keyword.operator.expression: #569CD6", + "hc_light": "keyword.operator.expression: #0F4A85", + "light_modern": "keyword.operator.expression: #0000FF" + } + }, + { + "c": " ", + "t": "source.ts meta.function.ts meta.return.type.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "Truthy", + "t": "source.ts meta.function.ts meta.return.type.ts entity.name.type.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": "<", + "t": "source.ts meta.function.ts meta.return.type.ts meta.type.parameters.ts punctuation.definition.typeparameters.begin.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.return.type.ts meta.type.parameters.ts entity.name.type.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": ">", + "t": "source.ts meta.function.ts meta.return.type.ts meta.type.parameters.ts punctuation.definition.typeparameters.end.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.return.type.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.block.ts punctuation.definition.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "return", + "t": "source.ts meta.function.ts meta.block.ts keyword.control.flow.ts", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0", + "dark_modern": "keyword.control: #C586C0", + "hc_light": "keyword.control: #B5200D", + "light_modern": "keyword.control: #AF00DB" + } + }, + { + "c": " ", + "t": "source.ts meta.function.ts meta.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "Boolean", + "t": "source.ts meta.function.ts meta.block.ts meta.function-call.ts entity.name.function.ts", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA", + "dark_modern": "entity.name.function: #DCDCAA", + "hc_light": "entity.name.function: #5E2CBC", + "light_modern": "entity.name.function: #795E26" + } + }, + { + "c": "(", + "t": "source.ts meta.function.ts meta.block.ts meta.brace.round.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "value", + "t": "source.ts meta.function.ts meta.block.ts variable.other.readwrite.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": ")", + "t": "source.ts meta.function.ts meta.block.ts meta.brace.round.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.block.ts punctuation.terminator.statement.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.block.ts punctuation.definition.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "type", + "t": "source.ts meta.type.declaration.ts storage.type.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": " ", + "t": "source.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "Truthy", + "t": "source.ts meta.type.declaration.ts entity.name.type.alias.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": "<", + "t": "source.ts meta.type.declaration.ts meta.type.parameters.ts punctuation.definition.typeparameters.begin.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts meta.type.parameters.ts entity.name.type.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": ">", + "t": "source.ts meta.type.declaration.ts meta.type.parameters.ts punctuation.definition.typeparameters.end.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts keyword.operator.assignment.ts", + "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.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts entity.name.type.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": " ", + "t": "source.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "extends", + "t": "source.ts meta.type.declaration.ts storage.modifier.ts", + "r": { + "dark_plus": "storage.modifier: #569CD6", + "light_plus": "storage.modifier: #0000FF", + "dark_vs": "storage.modifier: #569CD6", + "light_vs": "storage.modifier: #0000FF", + "hc_black": "storage.modifier: #569CD6", + "dark_modern": "storage.modifier: #569CD6", + "hc_light": "storage.modifier: #0F4A85", + "light_modern": "storage.modifier: #0000FF" + } + }, + { + "c": " ", + "t": "source.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts string.quoted.single.ts punctuation.definition.string.begin.ts", + "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.ts meta.type.declaration.ts string.quoted.single.ts punctuation.definition.string.end.ts", + "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.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts keyword.operator.type.ts", + "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.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "0", + "t": "source.ts meta.type.declaration.ts constant.numeric.decimal.ts", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #098658", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #098658", + "hc_black": "constant.numeric: #B5CEA8", + "dark_modern": "constant.numeric: #B5CEA8", + "hc_light": "constant.numeric: #096D48", + "light_modern": "constant.numeric: #098658" + } + }, + { + "c": " ", + "t": "source.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts keyword.operator.type.ts", + "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.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "false", + "t": "source.ts meta.type.declaration.ts support.type.builtin.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": " ", + "t": "source.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts keyword.operator.type.ts", + "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.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "null", + "t": "source.ts meta.type.declaration.ts support.type.builtin.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": " ", + "t": "source.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts keyword.operator.type.ts", + "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.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "undefined", + "t": "source.ts meta.type.declaration.ts support.type.builtin.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": " ", + "t": "source.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts keyword.operator.ternary.ts", + "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.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "never", + "t": "source.ts meta.type.declaration.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": " ", + "t": "source.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts keyword.operator.ternary.ts", + "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.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts entity.name.type.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": ";", + "t": "source.ts punctuation.terminator.statement.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "const", + "t": "source.ts meta.enum.declaration.ts storage.modifier.ts", + "r": { + "dark_plus": "storage.modifier: #569CD6", + "light_plus": "storage.modifier: #0000FF", + "dark_vs": "storage.modifier: #569CD6", + "light_vs": "storage.modifier: #0000FF", + "hc_black": "storage.modifier: #569CD6", + "dark_modern": "storage.modifier: #569CD6", + "hc_light": "storage.modifier: #0F4A85", + "light_modern": "storage.modifier: #0000FF" + } + }, + { + "c": " ", + "t": "source.ts meta.enum.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "enum", + "t": "source.ts meta.enum.declaration.ts storage.type.enum.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": " ", + "t": "source.ts meta.enum.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "EnumName", + "t": "source.ts meta.enum.declaration.ts entity.name.type.enum.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": " ", + "t": "source.ts meta.enum.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.enum.declaration.ts punctuation.definition.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.enum.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "one", + "t": "source.ts meta.enum.declaration.ts variable.other.enummember.ts", + "r": { + "dark_plus": "variable.other.enummember: #4FC1FF", + "light_plus": "variable.other.enummember: #0070C1", + "dark_vs": "variable.other.enummember: #B5CEA8", + "light_vs": "variable.other.enummember: #098658", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable.other.enummember: #4FC1FF", + "hc_light": "variable.other.enummember: #02715D", + "light_modern": "variable.other.enummember: #0070C1" + } + }, + { + "c": " ", + "t": "source.ts meta.enum.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.enum.declaration.ts keyword.operator.assignment.ts", + "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.ts meta.enum.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "1", + "t": "source.ts meta.enum.declaration.ts constant.numeric.decimal.ts", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #098658", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #098658", + "hc_black": "constant.numeric: #B5CEA8", + "dark_modern": "constant.numeric: #B5CEA8", + "hc_light": "constant.numeric: #096D48", + "light_modern": "constant.numeric: #098658" + } + }, + { + "c": ",", + "t": "source.ts meta.enum.declaration.ts punctuation.separator.comma.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.enum.declaration.ts punctuation.definition.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "void", + "t": "source.ts keyword.operator.expression.void.ts", + "r": { + "dark_plus": "keyword.operator.expression: #569CD6", + "light_plus": "keyword.operator.expression: #0000FF", + "dark_vs": "keyword.operator.expression: #569CD6", + "light_vs": "keyword.operator.expression: #0000FF", + "hc_black": "keyword.operator.expression: #569CD6", + "dark_modern": "keyword.operator.expression: #569CD6", + "hc_light": "keyword.operator.expression: #0F4A85", + "light_modern": "keyword.operator.expression: #0000FF" + } + }, + { + "c": " ", + "t": "source.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "0", + "t": "source.ts constant.numeric.decimal.ts", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #098658", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #098658", + "hc_black": "constant.numeric: #B5CEA8", + "dark_modern": "constant.numeric: #B5CEA8", + "hc_light": "constant.numeric: #096D48", + "light_modern": "constant.numeric: #098658" + } + }, + { + "c": ";", + "t": "source.ts punctuation.terminator.statement.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "function", + "t": "source.ts meta.function.ts storage.type.function.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": "*", + "t": "source.ts meta.function.ts keyword.generator.asterisk.ts", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6", + "dark_modern": "keyword: #569CD6", + "hc_light": "keyword: #0F4A85", + "light_modern": "keyword: #0000FF" + } + }, + { + "c": " ", + "t": "source.ts meta.function.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "makeIterator", + "t": "source.ts meta.function.ts meta.definition.function.ts entity.name.function.ts", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA", + "dark_modern": "entity.name.function: #DCDCAA", + "hc_light": "entity.name.function: #5E2CBC", + "light_modern": "entity.name.function: #795E26" + } + }, + { + "c": "(", + "t": "source.ts meta.function.ts meta.parameters.ts punctuation.definition.parameters.begin.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "start", + "t": "source.ts meta.function.ts meta.parameters.ts variable.parameter.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": " ", + "t": "source.ts meta.function.ts meta.parameters.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.parameters.ts keyword.operator.assignment.ts", + "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.ts meta.function.ts meta.parameters.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "0", + "t": "source.ts meta.function.ts meta.parameters.ts constant.numeric.decimal.ts", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #098658", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #098658", + "hc_black": "constant.numeric: #B5CEA8", + "dark_modern": "constant.numeric: #B5CEA8", + "hc_light": "constant.numeric: #096D48", + "light_modern": "constant.numeric: #098658" + } + }, + { + "c": ",", + "t": "source.ts meta.function.ts meta.parameters.ts punctuation.separator.parameter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.parameters.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "end", + "t": "source.ts meta.function.ts meta.parameters.ts variable.parameter.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": " ", + "t": "source.ts meta.function.ts meta.parameters.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.parameters.ts keyword.operator.assignment.ts", + "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.ts meta.function.ts meta.parameters.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "Infinity", + "t": "source.ts meta.function.ts meta.parameters.ts constant.language.infinity.ts", + "r": { + "dark_plus": "constant.language: #569CD6", + "light_plus": "constant.language: #0000FF", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "constant.language: #569CD6", + "dark_modern": "constant.language: #569CD6", + "hc_light": "constant.language: #0F4A85", + "light_modern": "constant.language: #0000FF" + } + }, + { + "c": ",", + "t": "source.ts meta.function.ts meta.parameters.ts punctuation.separator.parameter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.parameters.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "step", + "t": "source.ts meta.function.ts meta.parameters.ts variable.parameter.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": " ", + "t": "source.ts meta.function.ts meta.parameters.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.parameters.ts keyword.operator.assignment.ts", + "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.ts meta.function.ts meta.parameters.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "1", + "t": "source.ts meta.function.ts meta.parameters.ts constant.numeric.decimal.ts", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #098658", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #098658", + "hc_black": "constant.numeric: #B5CEA8", + "dark_modern": "constant.numeric: #B5CEA8", + "hc_light": "constant.numeric: #096D48", + "light_modern": "constant.numeric: #098658" + } + }, + { + "c": ")", + "t": "source.ts meta.function.ts meta.parameters.ts punctuation.definition.parameters.end.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.block.ts punctuation.definition.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.block.ts punctuation.definition.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "function", + "t": "source.ts meta.function.ts storage.type.function.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": " ", + "t": "source.ts meta.function.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "makeDate", + "t": "source.ts meta.function.ts meta.definition.function.ts entity.name.function.ts", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA", + "dark_modern": "entity.name.function: #DCDCAA", + "hc_light": "entity.name.function: #5E2CBC", + "light_modern": "entity.name.function: #795E26" + } + }, + { + "c": "(", + "t": "source.ts meta.function.ts meta.parameters.ts punctuation.definition.parameters.begin.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "timestamp", + "t": "source.ts meta.function.ts meta.parameters.ts variable.parameter.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": ":", + "t": "source.ts meta.function.ts meta.parameters.ts meta.type.annotation.ts keyword.operator.type.annotation.ts", + "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.ts meta.function.ts meta.parameters.ts meta.type.annotation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "number", + "t": "source.ts meta.function.ts meta.parameters.ts meta.type.annotation.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": ")", + "t": "source.ts meta.function.ts meta.parameters.ts punctuation.definition.parameters.end.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.return.type.ts keyword.operator.type.annotation.ts", + "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.ts meta.function.ts meta.return.type.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "Date", + "t": "source.ts meta.function.ts meta.return.type.ts entity.name.type.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": ";", + "t": "source.ts punctuation.terminator.statement.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "function", + "t": "source.ts meta.function.ts storage.type.function.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": " ", + "t": "source.ts meta.function.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "makeDate", + "t": "source.ts meta.function.ts meta.definition.function.ts entity.name.function.ts", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA", + "dark_modern": "entity.name.function: #DCDCAA", + "hc_light": "entity.name.function: #5E2CBC", + "light_modern": "entity.name.function: #795E26" + } + }, + { + "c": "(", + "t": "source.ts meta.function.ts meta.parameters.ts punctuation.definition.parameters.begin.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "m", + "t": "source.ts meta.function.ts meta.parameters.ts variable.parameter.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": ":", + "t": "source.ts meta.function.ts meta.parameters.ts meta.type.annotation.ts keyword.operator.type.annotation.ts", + "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.ts meta.function.ts meta.parameters.ts meta.type.annotation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "number", + "t": "source.ts meta.function.ts meta.parameters.ts meta.type.annotation.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": ",", + "t": "source.ts meta.function.ts meta.parameters.ts punctuation.separator.parameter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.parameters.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "d", + "t": "source.ts meta.function.ts meta.parameters.ts variable.parameter.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": ":", + "t": "source.ts meta.function.ts meta.parameters.ts meta.type.annotation.ts keyword.operator.type.annotation.ts", + "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.ts meta.function.ts meta.parameters.ts meta.type.annotation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "number", + "t": "source.ts meta.function.ts meta.parameters.ts meta.type.annotation.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": ",", + "t": "source.ts meta.function.ts meta.parameters.ts punctuation.separator.parameter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.parameters.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "y", + "t": "source.ts meta.function.ts meta.parameters.ts variable.parameter.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": ":", + "t": "source.ts meta.function.ts meta.parameters.ts meta.type.annotation.ts keyword.operator.type.annotation.ts", + "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.ts meta.function.ts meta.parameters.ts meta.type.annotation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "number", + "t": "source.ts meta.function.ts meta.parameters.ts meta.type.annotation.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": ")", + "t": "source.ts meta.function.ts meta.parameters.ts punctuation.definition.parameters.end.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.return.type.ts keyword.operator.type.annotation.ts", + "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.ts meta.function.ts meta.return.type.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "Date", + "t": "source.ts meta.function.ts meta.return.type.ts entity.name.type.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": ";", + "t": "source.ts punctuation.terminator.statement.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "function", + "t": "source.ts meta.function.ts storage.type.function.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": " ", + "t": "source.ts meta.function.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "makeDate", + "t": "source.ts meta.function.ts meta.definition.function.ts entity.name.function.ts", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA", + "dark_modern": "entity.name.function: #DCDCAA", + "hc_light": "entity.name.function: #5E2CBC", + "light_modern": "entity.name.function: #795E26" + } + }, + { + "c": "(", + "t": "source.ts meta.function.ts meta.parameters.ts punctuation.definition.parameters.begin.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "mOrTimestamp", + "t": "source.ts meta.function.ts meta.parameters.ts variable.parameter.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": ":", + "t": "source.ts meta.function.ts meta.parameters.ts meta.type.annotation.ts keyword.operator.type.annotation.ts", + "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.ts meta.function.ts meta.parameters.ts meta.type.annotation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "number", + "t": "source.ts meta.function.ts meta.parameters.ts meta.type.annotation.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": ",", + "t": "source.ts meta.function.ts meta.parameters.ts punctuation.separator.parameter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.parameters.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "d", + "t": "source.ts meta.function.ts meta.parameters.ts variable.parameter.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": "?", + "t": "source.ts meta.function.ts meta.parameters.ts keyword.operator.optional.ts", + "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.ts meta.function.ts meta.parameters.ts meta.type.annotation.ts keyword.operator.type.annotation.ts", + "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.ts meta.function.ts meta.parameters.ts meta.type.annotation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "number", + "t": "source.ts meta.function.ts meta.parameters.ts meta.type.annotation.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": ",", + "t": "source.ts meta.function.ts meta.parameters.ts punctuation.separator.parameter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.parameters.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "y", + "t": "source.ts meta.function.ts meta.parameters.ts variable.parameter.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": "?", + "t": "source.ts meta.function.ts meta.parameters.ts keyword.operator.optional.ts", + "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.ts meta.function.ts meta.parameters.ts meta.type.annotation.ts keyword.operator.type.annotation.ts", + "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.ts meta.function.ts meta.parameters.ts meta.type.annotation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "number", + "t": "source.ts meta.function.ts meta.parameters.ts meta.type.annotation.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": ")", + "t": "source.ts meta.function.ts meta.parameters.ts punctuation.definition.parameters.end.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.return.type.ts keyword.operator.type.annotation.ts", + "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.ts meta.function.ts meta.return.type.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "Date", + "t": "source.ts meta.function.ts meta.return.type.ts entity.name.type.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": " ", + "t": "source.ts meta.function.ts meta.return.type.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.block.ts punctuation.definition.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.function.ts meta.block.ts punctuation.definition.block.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "type", + "t": "source.ts meta.type.declaration.ts storage.type.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": " ", + "t": "source.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "StringNumberBooleans", + "t": "source.ts meta.type.declaration.ts entity.name.type.alias.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": " ", + "t": "source.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts keyword.operator.assignment.ts", + "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.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts meta.type.tuple.ts meta.brace.square.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "string", + "t": "source.ts meta.type.declaration.ts meta.type.tuple.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": ",", + "t": "source.ts meta.type.declaration.ts meta.type.tuple.ts punctuation.separator.comma.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts meta.type.tuple.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "number", + "t": "source.ts meta.type.declaration.ts meta.type.tuple.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": ",", + "t": "source.ts meta.type.declaration.ts meta.type.tuple.ts punctuation.separator.comma.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts meta.type.tuple.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts meta.type.tuple.ts keyword.operator.rest.ts", + "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": "boolean", + "t": "source.ts meta.type.declaration.ts meta.type.tuple.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": "[]", + "t": "source.ts meta.type.declaration.ts meta.type.tuple.ts meta.type.tuple.ts meta.brace.square.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts meta.type.tuple.ts meta.brace.square.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts punctuation.terminator.statement.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "type", + "t": "source.ts meta.type.declaration.ts storage.type.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": " ", + "t": "source.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "StringBooleansNumber", + "t": "source.ts meta.type.declaration.ts entity.name.type.alias.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": " ", + "t": "source.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts keyword.operator.assignment.ts", + "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.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts meta.type.tuple.ts meta.brace.square.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "string", + "t": "source.ts meta.type.declaration.ts meta.type.tuple.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": ",", + "t": "source.ts meta.type.declaration.ts meta.type.tuple.ts punctuation.separator.comma.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts meta.type.tuple.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts meta.type.tuple.ts keyword.operator.rest.ts", + "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": "boolean", + "t": "source.ts meta.type.declaration.ts meta.type.tuple.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": "[]", + "t": "source.ts meta.type.declaration.ts meta.type.tuple.ts meta.type.tuple.ts meta.brace.square.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts meta.type.tuple.ts punctuation.separator.comma.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts meta.type.tuple.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "number", + "t": "source.ts meta.type.declaration.ts meta.type.tuple.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": "]", + "t": "source.ts meta.type.declaration.ts meta.type.tuple.ts meta.brace.square.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts punctuation.terminator.statement.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "type", + "t": "source.ts meta.type.declaration.ts storage.type.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": " ", + "t": "source.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "BooleansStringNumber", + "t": "source.ts meta.type.declaration.ts entity.name.type.alias.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": " ", + "t": "source.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts keyword.operator.assignment.ts", + "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.ts meta.type.declaration.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts meta.type.tuple.ts meta.brace.square.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts meta.type.tuple.ts keyword.operator.rest.ts", + "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": "boolean", + "t": "source.ts meta.type.declaration.ts meta.type.tuple.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": "[]", + "t": "source.ts meta.type.declaration.ts meta.type.tuple.ts meta.type.tuple.ts meta.brace.square.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts meta.type.tuple.ts punctuation.separator.comma.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts meta.type.tuple.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "string", + "t": "source.ts meta.type.declaration.ts meta.type.tuple.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": ",", + "t": "source.ts meta.type.declaration.ts meta.type.tuple.ts punctuation.separator.comma.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts meta.type.declaration.ts meta.type.tuple.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "number", + "t": "source.ts meta.type.declaration.ts meta.type.tuple.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": "]", + "t": "source.ts meta.type.declaration.ts meta.type.tuple.ts meta.brace.square.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ts punctuation.terminator.statement.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "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_rb.json b/extensions/vscode-colorize-tests/test/colorize-results/test_rb.json index 6230a5f14b3b..032617573e41 100644 --- a/extensions/vscode-colorize-tests/test/colorize-results/test_rb.json +++ b/extensions/vscode-colorize-tests/test/colorize-results/test_rb.json @@ -113,7 +113,7 @@ }, { "c": "module", - "t": "source.ruby keyword.control.ruby", + "t": "source.ruby meta.module.ruby keyword.control.module.ruby", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -127,7 +127,7 @@ }, { "c": " ", - "t": "source.ruby", + "t": "source.ruby meta.module.ruby", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -140,50 +140,22 @@ } }, { - "c": "Azure", - "t": "source.ruby support.class.ruby", - "r": { - "dark_plus": "support.class: #4EC9B0", - "light_plus": "support.class: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "support.class: #4EC9B0", - "dark_modern": "support.class: #4EC9B0", - "hc_light": "support.class: #185E73", - "light_modern": "support.class: #267F99" - } - }, - { - "c": "::", - "t": "source.ruby punctuation.separator.namespace.ruby", - "r": { - "dark_plus": "punctuation.separator.namespace.ruby: #4EC9B0", - "light_plus": "punctuation.separator.namespace.ruby: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "punctuation.separator.namespace.ruby: #4EC9B0", - "dark_modern": "punctuation.separator.namespace.ruby: #4EC9B0", - "hc_light": "punctuation.separator.namespace.ruby: #185E73", - "light_modern": "punctuation.separator.namespace.ruby: #267F99" - } - }, - { - "c": "ARM", - "t": "source.ruby support.class.ruby", + "c": "Azure::ARM", + "t": "source.ruby meta.module.ruby entity.name.type.module.ruby", "r": { - "dark_plus": "support.class: #4EC9B0", - "light_plus": "support.class: #267F99", + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "support.class: #4EC9B0", - "dark_modern": "support.class: #4EC9B0", - "hc_light": "support.class: #185E73", - "light_modern": "support.class: #267F99" + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" } }, { "c": "::", - "t": "source.ruby punctuation.separator.namespace.ruby", + "t": "source.ruby meta.module.ruby entity.name.type.module.ruby punctuation.separator.namespace.ruby", "r": { "dark_plus": "punctuation.separator.namespace.ruby: #4EC9B0", "light_plus": "punctuation.separator.namespace.ruby: #267F99", @@ -197,16 +169,16 @@ }, { "c": "Scheduler", - "t": "source.ruby variable.other.constant.ruby", + "t": "source.ruby meta.module.ruby entity.name.type.module.ruby", "r": { - "dark_plus": "variable.other.constant: #4FC1FF", - "light_plus": "variable.other.constant: #0070C1", + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "variable: #9CDCFE", - "dark_modern": "variable.other.constant: #4FC1FF", - "hc_light": "variable.other.constant: #02715D", - "light_modern": "variable.other.constant: #0070C1" + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" } }, { @@ -323,7 +295,7 @@ }, { "c": "class", - "t": "source.ruby keyword.control.ruby", + "t": "source.ruby meta.class.ruby keyword.control.class.ruby", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -337,7 +309,7 @@ }, { "c": " ", - "t": "source.ruby", + "t": "source.ruby meta.class.ruby", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -351,21 +323,21 @@ }, { "c": "SchedulerManagementClient", - "t": "source.ruby variable.other.constant.ruby", + "t": "source.ruby meta.class.ruby entity.name.type.class.ruby", "r": { - "dark_plus": "variable.other.constant: #4FC1FF", - "light_plus": "variable.other.constant: #0070C1", + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "variable: #9CDCFE", - "dark_modern": "variable.other.constant: #4FC1FF", - "hc_light": "variable.other.constant: #02715D", - "light_modern": "variable.other.constant: #0070C1" + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" } }, { "c": " ", - "t": "source.ruby", + "t": "source.ruby meta.class.ruby", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -379,21 +351,21 @@ }, { "c": "<", - "t": "source.ruby keyword.operator.comparison.ruby", + "t": "source.ruby meta.class.ruby punctuation.separator.inheritance.ruby", "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" + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ruby", + "t": "source.ruby meta.class.ruby", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -407,21 +379,21 @@ }, { "c": "MsRestAzure", - "t": "source.ruby support.class.ruby", + "t": "source.ruby meta.class.ruby entity.other.inherited-class.ruby", "r": { - "dark_plus": "support.class: #4EC9B0", - "light_plus": "support.class: #267F99", + "dark_plus": "entity.other.inherited-class: #4EC9B0", + "light_plus": "entity.other.inherited-class: #267F99", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "support.class: #4EC9B0", - "dark_modern": "support.class: #4EC9B0", - "hc_light": "support.class: #185E73", - "light_modern": "support.class: #267F99" + "hc_black": "entity.other.inherited-class: #4EC9B0", + "dark_modern": "entity.other.inherited-class: #4EC9B0", + "hc_light": "entity.other.inherited-class: #185E73", + "light_modern": "entity.other.inherited-class: #267F99" } }, { "c": "::", - "t": "source.ruby punctuation.separator.namespace.ruby", + "t": "source.ruby meta.class.ruby entity.other.inherited-class.ruby punctuation.separator.namespace.ruby", "r": { "dark_plus": "punctuation.separator.namespace.ruby: #4EC9B0", "light_plus": "punctuation.separator.namespace.ruby: #267F99", @@ -435,16 +407,16 @@ }, { "c": "AzureServiceClient", - "t": "source.ruby variable.other.constant.ruby", + "t": "source.ruby meta.class.ruby entity.other.inherited-class.ruby", "r": { - "dark_plus": "variable.other.constant: #4FC1FF", - "light_plus": "variable.other.constant: #0070C1", + "dark_plus": "entity.other.inherited-class: #4EC9B0", + "light_plus": "entity.other.inherited-class: #267F99", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "variable: #9CDCFE", - "dark_modern": "variable.other.constant: #4FC1FF", - "hc_light": "variable.other.constant: #02715D", - "light_modern": "variable.other.constant: #0070C1" + "hc_black": "entity.other.inherited-class: #4EC9B0", + "dark_modern": "entity.other.inherited-class: #4EC9B0", + "hc_light": "entity.other.inherited-class: #185E73", + "light_modern": "entity.other.inherited-class: #267F99" } }, { @@ -3318,7 +3290,35 @@ } }, { - "c": " = ", + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ruby keyword.operator.assignment.ruby", + "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.ruby", "r": { "dark_plus": "default: #D4D4D4", @@ -3472,7 +3472,35 @@ } }, { - "c": " = ", + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ruby keyword.operator.assignment.ruby", + "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.ruby", "r": { "dark_plus": "default: #D4D4D4", @@ -3584,7 +3612,35 @@ } }, { - "c": " = version", + "c": " ", + "t": "source.ruby", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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.ruby keyword.operator.assignment.ruby", + "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": " version", "t": "source.ruby", "r": { "dark_plus": "default: #D4D4D4", diff --git a/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-241001_ts.json b/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-241001_ts.json new file mode 100644 index 000000000000..a5a8c33bf22b --- /dev/null +++ b/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-241001_ts.json @@ -0,0 +1,2144 @@ +[ + { + "c": "const", + "t": "storage.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": "n", + "t": "variable.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": "=", + "t": "keyword.operator.assignment.ts", + "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": "null", + "t": "constant.language.null.ts", + "r": { + "dark_plus": "constant.language: #569CD6", + "light_plus": "constant.language: #0000FF", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "constant.language: #569CD6", + "dark_modern": "constant.language: #569CD6", + "hc_light": "constant.language: #0F4A85", + "light_modern": "constant.language: #0000FF" + } + }, + { + "c": ";", + "t": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "const", + "t": "storage.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": "u", + "t": "variable.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": "=", + "t": "keyword.operator.assignment.ts", + "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": "undefined", + "t": "constant.language.undefined.ts", + "r": { + "dark_plus": "constant.language: #569CD6", + "light_plus": "constant.language: #0000FF", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "constant.language: #569CD6", + "dark_modern": "constant.language: #569CD6", + "hc_light": "constant.language: #0F4A85", + "light_modern": "constant.language: #0000FF" + } + }, + { + "c": ";", + "t": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "const", + "t": "storage.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": "a", + "t": "variable.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": "=", + "t": "keyword.operator.assignment.ts", + "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": "true", + "t": "constant.language.boolean.true.ts", + "r": { + "dark_plus": "constant.language: #569CD6", + "light_plus": "constant.language: #0000FF", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "constant.language: #569CD6", + "dark_modern": "constant.language: #569CD6", + "hc_light": "constant.language: #0F4A85", + "light_modern": "constant.language: #0000FF" + } + }, + { + "c": "||", + "t": "keyword.operator.logical.ts", + "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": "false", + "t": "constant.language.boolean.false.ts", + "r": { + "dark_plus": "constant.language: #569CD6", + "light_plus": "constant.language: #0000FF", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "constant.language: #569CD6", + "dark_modern": "constant.language: #569CD6", + "hc_light": "constant.language: #0F4A85", + "light_modern": "constant.language: #0000FF" + } + }, + { + "c": ";", + "t": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "type", + "t": "storage.type.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": "T", + "t": "entity.name.type.ts entity.name.type.alias.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": "=", + "t": "keyword.operator.assignment.ts", + "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": "null", + "t": "constant.language.null.ts support.type.builtin.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": "|", + "t": "keyword.operator.ts keyword.operator.type.ts", + "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": "unknown", + "t": "support.type.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": "|", + "t": "keyword.operator.ts keyword.operator.type.ts", + "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": "undefined", + "t": "constant.language.undefined.ts support.type.builtin.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": "|", + "t": "keyword.operator.ts keyword.operator.type.ts", + "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": "true", + "t": "constant.language.boolean.true.ts support.type.builtin.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": "|", + "t": "keyword.operator.ts keyword.operator.type.ts", + "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": "false", + "t": "constant.language.boolean.false.ts support.type.builtin.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": ";", + "t": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "function", + "t": "storage.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": "bar", + "t": "variable.ts entity.name.function.ts", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA", + "dark_modern": "entity.name.function: #DCDCAA", + "hc_light": "entity.name.function: #5E2CBC", + "light_modern": "entity.name.function: #795E26" + } + }, + { + "c": "(", + "t": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "a", + "t": "variable.ts variable.parameter.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": "?", + "t": "punctuation.delimiter.ts keyword.operator.optional.ts", + "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": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", + "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": "string", + "t": "support.type.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": ")", + "t": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", + "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": "string", + "t": "support.type.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": "{", + "t": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "return", + "t": "keyword.control.ts", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0", + "dark_modern": "keyword.control: #C586C0", + "hc_light": "keyword.control: #B5200D", + "light_modern": "keyword.control: #AF00DB" + } + }, + { + "c": "'", + "t": "string.quoted.single.ts punctuation.definition.string.begin.ts punctuation.definition.string.end.ts", + "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": "string.quoted.single.ts punctuation.definition.string.begin.ts punctuation.definition.string.end.ts", + "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": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "interface", + "t": "storage.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": "A", + "t": "entity.name.type.ts entity.name.type.interface.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": "{", + "t": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "b", + "t": "variable.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": "?", + "t": "punctuation.delimiter.ts keyword.operator.optional.ts", + "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": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", + "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": "constant.numeric.ts", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #098658", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #098658", + "hc_black": "constant.numeric: #B5CEA8", + "dark_modern": "constant.numeric: #B5CEA8", + "hc_light": "constant.numeric: #096D48", + "light_modern": "constant.numeric: #098658" + } + }, + { + "c": ";", + "t": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "a", + "t": "variable.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": ":", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", + "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": "constant.numeric.ts", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #098658", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #098658", + "hc_black": "constant.numeric: #B5CEA8", + "dark_modern": "constant.numeric: #B5CEA8", + "hc_light": "constant.numeric: #096D48", + "light_modern": "constant.numeric: #098658" + } + }, + { + "c": ";", + "t": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "const", + "t": "storage.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": "obj", + "t": "variable.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": "=", + "t": "keyword.operator.assignment.ts", + "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": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "a", + "t": "variable.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": ":", + "t": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "1", + "t": "constant.numeric.ts", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #098658", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #098658", + "hc_black": "constant.numeric: #B5CEA8", + "dark_modern": "constant.numeric: #B5CEA8", + "hc_light": "constant.numeric: #096D48", + "light_modern": "constant.numeric: #098658" + } + }, + { + "c": ",", + "t": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "const", + "t": "storage.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": "obj1", + "t": "variable.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": "=", + "t": "keyword.operator.assignment.ts", + "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": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "const", + "t": "storage.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": "obj2", + "t": "variable.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": "=", + "t": "keyword.operator.assignment.ts", + "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": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "keyword.operator.spread.ts", + "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": "obj1", + "t": "variable.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": ",", + "t": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "function", + "t": "storage.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": "foo", + "t": "variable.ts entity.name.function.ts", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA", + "dark_modern": "entity.name.function: #DCDCAA", + "hc_light": "entity.name.function: #5E2CBC", + "light_modern": "entity.name.function: #795E26" + } + }, + { + "c": "(", + "t": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "param", + "t": "variable.ts variable.parameter.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": ":", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", + "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": "string", + "t": "support.type.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": ",", + "t": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "keyword.operator.rest.ts", + "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": "rest", + "t": "variable.ts variable.parameter.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": ")", + "t": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "undefined", + "t": "constant.language.undefined.ts", + "r": { + "dark_plus": "constant.language: #569CD6", + "light_plus": "constant.language: #0000FF", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "constant.language: #569CD6", + "dark_modern": "constant.language: #569CD6", + "hc_light": "constant.language: #0F4A85", + "light_modern": "constant.language: #0000FF" + } + }, + { + "c": "null", + "t": "constant.language.null.ts", + "r": { + "dark_plus": "constant.language: #569CD6", + "light_plus": "constant.language: #0000FF", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "constant.language: #569CD6", + "dark_modern": "constant.language: #569CD6", + "hc_light": "constant.language: #0F4A85", + "light_modern": "constant.language: #0000FF" + } + }, + { + "c": "NaN", + "t": "variable.ts constant.language.nan.ts", + "r": { + "dark_plus": "constant.language: #569CD6", + "light_plus": "constant.language: #0000FF", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "constant.language: #569CD6", + "dark_modern": "constant.language: #569CD6", + "hc_light": "constant.language: #0F4A85", + "light_modern": "constant.language: #0000FF" + } + }, + { + "c": "Infinity", + "t": "variable.ts constant.language.infinity.ts", + "r": { + "dark_plus": "constant.language: #569CD6", + "light_plus": "constant.language: #0000FF", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "constant.language: #569CD6", + "dark_modern": "constant.language: #569CD6", + "hc_light": "constant.language: #0F4A85", + "light_modern": "constant.language: #0000FF" + } + }, + { + "c": "type", + "t": "storage.type.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": "One", + "t": "entity.name.type.ts entity.name.type.alias.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": "=", + "t": "keyword.operator.assignment.ts", + "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": "string.quoted.single.ts punctuation.definition.string.begin.ts", + "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": "zcxvf", + "t": "string.quoted.single.ts", + "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": "string.quoted.single.ts punctuation.definition.string.end.ts", + "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": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "type", + "t": "storage.type.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": "Two", + "t": "entity.name.type.ts entity.name.type.alias.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": "=", + "t": "keyword.operator.assignment.ts", + "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": "string.quoted.single.ts punctuation.definition.string.begin.ts", + "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": "one", + "t": "string.quoted.single.ts", + "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": "string.quoted.single.ts punctuation.definition.string.end.ts", + "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": "keyword.operator.ts keyword.operator.type.ts", + "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": "string.quoted.single.ts punctuation.definition.string.begin.ts", + "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": "two", + "t": "string.quoted.single.ts", + "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": "string.quoted.single.ts punctuation.definition.string.end.ts", + "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": "keyword.operator.ts keyword.operator.type.ts", + "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": "string.quoted.single.ts punctuation.definition.string.begin.ts", + "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": "three", + "t": "string.quoted.single.ts", + "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": "string.quoted.single.ts punctuation.definition.string.end.ts", + "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": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "const", + "t": "storage.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": "obj", + "t": "variable.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": "=", + "t": "keyword.operator.assignment.ts", + "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": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "one", + "t": "variable.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": ":", + "t": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "1", + "t": "constant.numeric.ts", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #098658", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #098658", + "hc_black": "constant.numeric: #B5CEA8", + "dark_modern": "constant.numeric: #B5CEA8", + "hc_light": "constant.numeric: #096D48", + "light_modern": "constant.numeric: #098658" + } + }, + { + "c": "}", + "t": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "type", + "t": "storage.type.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": "Rec", + "t": "entity.name.type.ts entity.name.type.alias.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": "=", + "t": "keyword.operator.assignment.ts", + "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": "Record", + "t": "entity.name.type.ts entity.name.type.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": "<", + "t": "punctuation.definition.typeparameters.begin.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "string", + "t": "support.type.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": ",", + "t": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "string", + "t": "support.type.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": ">", + "t": "punctuation.definition.typeparameters.end.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "new", + "t": "new.expr.ts keyword.operator.new.ts", + "r": { + "dark_plus": "keyword.operator.new: #569CD6", + "light_plus": "keyword.operator.new: #0000FF", + "dark_vs": "keyword.operator.new: #569CD6", + "light_vs": "keyword.operator.new: #0000FF", + "hc_black": "keyword.operator.new: #569CD6", + "dark_modern": "keyword.operator.new: #569CD6", + "hc_light": "keyword.operator.new: #0F4A85", + "light_modern": "keyword.operator.new: #0000FF" + } + }, + { + "c": "URL", + "t": "new.expr.ts variable.ts entity.name.function.ts", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA", + "dark_modern": "entity.name.function: #DCDCAA", + "hc_light": "entity.name.function: #5E2CBC", + "light_modern": "entity.name.function: #795E26" + } + }, + { + "c": "(", + "t": "new.expr.ts punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "new.expr.ts string.quoted.single.ts punctuation.definition.string.begin.ts", + "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": "./../../renderer/dist/index.html", + "t": "new.expr.ts string.quoted.single.ts", + "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": "new.expr.ts string.quoted.single.ts punctuation.definition.string.end.ts", + "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": "new.expr.ts punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "import", + "t": "new.expr.ts keyword.control.ts", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0", + "dark_modern": "keyword.control: #C586C0", + "hc_light": "keyword.control: #B5200D", + "light_modern": "keyword.control: #AF00DB" + } + }, + { + "c": ".", + "t": "new.expr.ts punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "meta", + "t": "new.expr.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "new.expr.ts punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "url", + "t": "new.expr.ts variable.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": ")", + "t": "new.expr.ts punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "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-tree-sitter-results/test-function-inv_ts.json b/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-function-inv_ts.json index cffeb46fdca8..046a20d140a9 100644 --- a/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-function-inv_ts.json +++ b/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-function-inv_ts.json @@ -1,7 +1,7 @@ [ { "c": "rowData", - "t": "variable", + "t": "variable.ts variable.other.object.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -15,7 +15,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -29,7 +29,7 @@ }, { "c": "push", - "t": "variable entity.name.function", + "t": "variable.ts entity.name.function.ts", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -43,7 +43,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -57,7 +57,7 @@ }, { "c": "callback", - "t": "variable entity.name.function", + "t": "variable.ts entity.name.function.ts", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -71,7 +71,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -85,7 +85,7 @@ }, { "c": "new", - "t": "new.expr keyword.operator.new", + "t": "new.expr.ts keyword.operator.new.ts", "r": { "dark_plus": "keyword.operator.new: #569CD6", "light_plus": "keyword.operator.new: #0000FF", @@ -99,7 +99,7 @@ }, { "c": "Cell", - "t": "new.expr variable entity.name.function", + "t": "new.expr.ts variable.ts entity.name.function.ts", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -113,7 +113,7 @@ }, { "c": "(", - "t": "new.expr punctuation", + "t": "new.expr.ts punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -127,7 +127,7 @@ }, { "c": "row", - "t": "new.expr variable", + "t": "new.expr.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -141,7 +141,7 @@ }, { "c": ",", - "t": "new.expr punctuation.delimiter", + "t": "new.expr.ts punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -155,7 +155,7 @@ }, { "c": "col", - "t": "new.expr variable", + "t": "new.expr.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -169,7 +169,7 @@ }, { "c": ",", - "t": "new.expr punctuation.delimiter", + "t": "new.expr.ts punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -183,7 +183,7 @@ }, { "c": "false", - "t": "new.expr constant.language.boolean.false", + "t": "new.expr.ts constant.language.boolean.false.ts", "r": { "dark_plus": "constant.language: #569CD6", "light_plus": "constant.language: #0000FF", @@ -197,7 +197,7 @@ }, { "c": ")", - "t": "new.expr punctuation", + "t": "new.expr.ts punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -211,7 +211,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -225,7 +225,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -239,7 +239,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", diff --git a/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-issue11_ts.json b/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-issue11_ts.json index 71c61dfc5cbb..5e8481b6289e 100644 --- a/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-issue11_ts.json +++ b/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-issue11_ts.json @@ -1,7 +1,7 @@ [ { "c": "let", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -15,7 +15,7 @@ }, { "c": "keyCode", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -29,7 +29,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -43,7 +43,7 @@ }, { "c": "0", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -57,7 +57,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -71,7 +71,7 @@ }, { "c": "if", - "t": "keyword.control", + "t": "keyword.control.ts", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -85,7 +85,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -99,7 +99,7 @@ }, { "c": "!", - "t": "keyword.operator.logical", + "t": "keyword.operator.logical.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -113,7 +113,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -127,7 +127,7 @@ }, { "c": "keyCode", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -141,7 +141,7 @@ }, { "c": "===", - "t": "keyword.operator.logical", + "t": "keyword.operator.logical.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -155,7 +155,7 @@ }, { "c": "8", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -169,7 +169,7 @@ }, { "c": "||", - "t": "keyword.operator.logical", + "t": "keyword.operator.logical.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -183,7 +183,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -197,7 +197,7 @@ }, { "c": "keyCode", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -211,7 +211,7 @@ }, { "c": ">=", - "t": "keyword.operator.relational", + "t": "keyword.operator.relational.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -225,7 +225,7 @@ }, { "c": "48", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -239,7 +239,7 @@ }, { "c": "&&", - "t": "keyword.operator.logical", + "t": "keyword.operator.logical.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -253,7 +253,7 @@ }, { "c": "keyCode", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -267,7 +267,7 @@ }, { "c": "<=", - "t": "keyword.operator.relational", + "t": "keyword.operator.relational.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -281,7 +281,7 @@ }, { "c": "57", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -295,7 +295,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -309,7 +309,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -323,7 +323,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -337,7 +337,7 @@ }, { "c": "{", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -351,7 +351,7 @@ }, { "c": "}", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -365,7 +365,7 @@ }, { "c": "for", - "t": "keyword.control", + "t": "keyword.control.ts", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -379,7 +379,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -393,7 +393,7 @@ }, { "c": "let", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -407,7 +407,7 @@ }, { "c": "i", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -421,7 +421,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -435,7 +435,7 @@ }, { "c": "0", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -449,7 +449,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -463,7 +463,7 @@ }, { "c": "i", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -477,7 +477,7 @@ }, { "c": "<", - "t": "keyword.operator.relational", + "t": "keyword.operator.relational.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -491,7 +491,7 @@ }, { "c": "5", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -505,7 +505,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -519,7 +519,7 @@ }, { "c": "i", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -533,7 +533,7 @@ }, { "c": "++", - "t": "keyword.operator.increment", + "t": "keyword.operator.increment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -547,7 +547,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -561,7 +561,7 @@ }, { "c": "{", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -575,7 +575,7 @@ }, { "c": "}", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -589,7 +589,7 @@ }, { "c": "for", - "t": "keyword.control", + "t": "keyword.control.ts", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -603,7 +603,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -617,7 +617,7 @@ }, { "c": "var", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -631,7 +631,7 @@ }, { "c": "i", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -645,7 +645,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -659,7 +659,7 @@ }, { "c": "0", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -673,7 +673,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -687,7 +687,7 @@ }, { "c": "i", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -701,7 +701,7 @@ }, { "c": "<", - "t": "keyword.operator.relational", + "t": "keyword.operator.relational.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -715,7 +715,7 @@ }, { "c": "5", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -729,7 +729,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -743,7 +743,7 @@ }, { "c": "i", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -757,7 +757,7 @@ }, { "c": "++", - "t": "keyword.operator.increment", + "t": "keyword.operator.increment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -771,7 +771,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -785,7 +785,7 @@ }, { "c": "{", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -799,7 +799,7 @@ }, { "c": "}", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -813,7 +813,7 @@ }, { "c": "for", - "t": "keyword.control", + "t": "keyword.control.ts", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -827,7 +827,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -841,7 +841,7 @@ }, { "c": "let", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -855,7 +855,7 @@ }, { "c": "i", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -869,7 +869,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -883,7 +883,7 @@ }, { "c": "0", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -897,7 +897,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -911,7 +911,7 @@ }, { "c": "i", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -925,7 +925,7 @@ }, { "c": "<", - "t": "keyword.operator.relational", + "t": "keyword.operator.relational.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -939,7 +939,7 @@ }, { "c": "5", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -953,7 +953,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -967,7 +967,7 @@ }, { "c": "i", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -981,7 +981,7 @@ }, { "c": "++", - "t": "keyword.operator.increment", + "t": "keyword.operator.increment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -995,7 +995,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1009,7 +1009,7 @@ }, { "c": "{", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1023,7 +1023,7 @@ }, { "c": "}", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1037,7 +1037,7 @@ }, { "c": "for", - "t": "keyword.control", + "t": "keyword.control.ts", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -1051,7 +1051,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1065,7 +1065,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1079,7 +1079,7 @@ }, { "c": "i", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -1093,7 +1093,7 @@ }, { "c": "<", - "t": "keyword.operator.relational", + "t": "keyword.operator.relational.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1107,7 +1107,7 @@ }, { "c": "5", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -1121,7 +1121,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1135,7 +1135,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1149,7 +1149,7 @@ }, { "c": "{", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1163,7 +1163,7 @@ }, { "c": "}", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1177,7 +1177,7 @@ }, { "c": "for", - "t": "keyword.control", + "t": "keyword.control.ts", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -1191,7 +1191,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1205,7 +1205,7 @@ }, { "c": "let", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -1219,7 +1219,7 @@ }, { "c": "i", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -1233,7 +1233,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1247,7 +1247,7 @@ }, { "c": "0", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -1261,7 +1261,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1275,7 +1275,7 @@ }, { "c": "1", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -1289,7 +1289,7 @@ }, { "c": "+", - "t": "keyword.operator.arithmetic", + "t": "keyword.operator.arithmetic.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1303,7 +1303,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1317,7 +1317,7 @@ }, { "c": "i", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -1331,7 +1331,7 @@ }, { "c": "<<", - "t": "keyword.operator", + "t": "keyword.operator.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1345,7 +1345,7 @@ }, { "c": "5", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -1359,7 +1359,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1373,7 +1373,7 @@ }, { "c": "<", - "t": "keyword.operator.relational", + "t": "keyword.operator.relational.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1387,7 +1387,7 @@ }, { "c": "5", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -1401,7 +1401,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1415,7 +1415,7 @@ }, { "c": "i", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -1429,7 +1429,7 @@ }, { "c": "++", - "t": "keyword.operator.increment", + "t": "keyword.operator.increment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1443,7 +1443,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1457,7 +1457,7 @@ }, { "c": "{", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1471,7 +1471,7 @@ }, { "c": "}", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1485,7 +1485,7 @@ }, { "c": "var", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -1499,7 +1499,7 @@ }, { "c": "p", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -1513,7 +1513,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1527,7 +1527,7 @@ }, { "c": "1", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -1541,7 +1541,7 @@ }, { "c": "?", - "t": "punctuation.delimiter keyword.operator.ternary", + "t": "punctuation.delimiter.ts keyword.operator.ternary.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1555,7 +1555,7 @@ }, { "c": "2", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -1569,7 +1569,7 @@ }, { "c": ":", - "t": "punctuation.delimiter keyword.operator.ternary", + "t": "punctuation.delimiter.ts keyword.operator.ternary.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1583,7 +1583,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1597,7 +1597,7 @@ }, { "c": "3", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -1611,7 +1611,7 @@ }, { "c": "<", - "t": "keyword.operator.relational", + "t": "keyword.operator.relational.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1625,7 +1625,7 @@ }, { "c": "4", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -1639,7 +1639,7 @@ }, { "c": "?", - "t": "punctuation.delimiter keyword.operator.ternary", + "t": "punctuation.delimiter.ts keyword.operator.ternary.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1653,7 +1653,7 @@ }, { "c": "5", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -1667,7 +1667,7 @@ }, { "c": ":", - "t": "punctuation.delimiter keyword.operator.ternary", + "t": "punctuation.delimiter.ts keyword.operator.ternary.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1681,7 +1681,7 @@ }, { "c": "6", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -1695,7 +1695,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1709,7 +1709,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1723,7 +1723,7 @@ }, { "c": "class", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -1737,7 +1737,7 @@ }, { "c": "A", - "t": "entity.name.type", + "t": "entity.name.type.ts entity.name.type.class.ts", "r": { "dark_plus": "entity.name.type: #4EC9B0", "light_plus": "entity.name.type: #267F99", @@ -1751,7 +1751,7 @@ }, { "c": "<", - "t": "", + "t": "punctuation.definition.typeparameters.begin.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1765,7 +1765,7 @@ }, { "c": "X", - "t": "entity.name.type", + "t": "entity.name.type.ts", "r": { "dark_plus": "entity.name.type: #4EC9B0", "light_plus": "entity.name.type: #267F99", @@ -1779,7 +1779,7 @@ }, { "c": ",", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1793,7 +1793,7 @@ }, { "c": "Y", - "t": "entity.name.type", + "t": "entity.name.type.ts", "r": { "dark_plus": "entity.name.type: #4EC9B0", "light_plus": "entity.name.type: #267F99", @@ -1807,7 +1807,7 @@ }, { "c": ">", - "t": "", + "t": "punctuation.definition.typeparameters.end.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1821,7 +1821,7 @@ }, { "c": "{", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1835,7 +1835,7 @@ }, { "c": "}", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1849,7 +1849,7 @@ }, { "c": "class", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -1863,7 +1863,7 @@ }, { "c": "A1", - "t": "entity.name.type", + "t": "entity.name.type.ts entity.name.type.class.ts", "r": { "dark_plus": "entity.name.type: #4EC9B0", "light_plus": "entity.name.type: #267F99", @@ -1877,7 +1877,7 @@ }, { "c": "<", - "t": "", + "t": "punctuation.definition.typeparameters.begin.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1891,7 +1891,7 @@ }, { "c": "T", - "t": "entity.name.type", + "t": "entity.name.type.ts", "r": { "dark_plus": "entity.name.type: #4EC9B0", "light_plus": "entity.name.type: #267F99", @@ -1905,7 +1905,7 @@ }, { "c": "extends", - "t": "storage.modifier", + "t": "storage.modifier.ts", "r": { "dark_plus": "storage.modifier: #569CD6", "light_plus": "storage.modifier: #0000FF", @@ -1919,7 +1919,7 @@ }, { "c": "{", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1933,7 +1933,7 @@ }, { "c": "a", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -1947,21 +1947,21 @@ }, { "c": ":", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "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": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1975,7 +1975,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1989,7 +1989,7 @@ }, { "c": "=>", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -2003,7 +2003,7 @@ }, { "c": "string", - "t": "support.type support.type.primitive", + "t": "support.type.ts support.type.primitive.ts", "r": { "dark_plus": "support.type: #4EC9B0", "light_plus": "support.type: #267F99", @@ -2017,7 +2017,7 @@ }, { "c": "}", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2031,7 +2031,7 @@ }, { "c": ">", - "t": "", + "t": "punctuation.definition.typeparameters.end.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2045,7 +2045,7 @@ }, { "c": "{", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2059,7 +2059,7 @@ }, { "c": "}", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2073,7 +2073,7 @@ }, { "c": "class", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -2087,7 +2087,7 @@ }, { "c": "B", - "t": "entity.name.type", + "t": "entity.name.type.ts entity.name.type.class.ts", "r": { "dark_plus": "entity.name.type: #4EC9B0", "light_plus": "entity.name.type: #267F99", @@ -2101,7 +2101,7 @@ }, { "c": "{", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2115,7 +2115,7 @@ }, { "c": "}", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2129,7 +2129,7 @@ }, { "c": "class", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -2143,7 +2143,7 @@ }, { "c": "C", - "t": "entity.name.type", + "t": "entity.name.type.ts entity.name.type.class.ts", "r": { "dark_plus": "entity.name.type: #4EC9B0", "light_plus": "entity.name.type: #267F99", @@ -2157,7 +2157,7 @@ }, { "c": "{", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2171,7 +2171,7 @@ }, { "c": "}", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2185,7 +2185,7 @@ }, { "c": "function", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -2199,7 +2199,7 @@ }, { "c": "foo", - "t": "variable entity.name.function", + "t": "variable.ts entity.name.function.ts", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -2213,7 +2213,7 @@ }, { "c": "<", - "t": "", + "t": "punctuation.definition.typeparameters.begin.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2227,7 +2227,7 @@ }, { "c": "T", - "t": "entity.name.type", + "t": "entity.name.type.ts", "r": { "dark_plus": "entity.name.type: #4EC9B0", "light_plus": "entity.name.type: #267F99", @@ -2241,7 +2241,7 @@ }, { "c": ">", - "t": "", + "t": "punctuation.definition.typeparameters.end.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2255,7 +2255,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2269,7 +2269,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2283,7 +2283,7 @@ }, { "c": "{", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2297,7 +2297,7 @@ }, { "c": "return", - "t": "keyword.control", + "t": "keyword.control.ts", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -2311,7 +2311,7 @@ }, { "c": "1", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -2325,7 +2325,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2339,7 +2339,7 @@ }, { "c": "}", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2353,7 +2353,7 @@ }, { "c": "let", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -2367,7 +2367,7 @@ }, { "c": "x1", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -2381,21 +2381,21 @@ }, { "c": ":", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "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": "A", - "t": "entity.name.type", + "t": "entity.name.type.ts", "r": { "dark_plus": "entity.name.type: #4EC9B0", "light_plus": "entity.name.type: #267F99", @@ -2409,7 +2409,7 @@ }, { "c": "<", - "t": "punctuation.definition.typeparameters", + "t": "punctuation.definition.typeparameters.begin.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2423,7 +2423,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2437,7 +2437,7 @@ }, { "c": "param", - "t": "variable variable.parameter", + "t": "variable.ts variable.parameter.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -2451,7 +2451,7 @@ }, { "c": "?", - "t": "punctuation.delimiter keyword.operator.optional", + "t": "punctuation.delimiter.ts keyword.operator.optional.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -2465,21 +2465,21 @@ }, { "c": ":", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "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": "number", - "t": "support.type support.type.primitive", + "t": "support.type.ts support.type.primitive.ts", "r": { "dark_plus": "support.type: #4EC9B0", "light_plus": "support.type: #267F99", @@ -2493,7 +2493,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2507,7 +2507,7 @@ }, { "c": "=>", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -2521,7 +2521,7 @@ }, { "c": "void", - "t": "support.type support.type.primitive", + "t": "support.type.ts support.type.primitive.ts", "r": { "dark_plus": "support.type: #4EC9B0", "light_plus": "support.type: #267F99", @@ -2535,7 +2535,7 @@ }, { "c": ",", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2549,7 +2549,7 @@ }, { "c": "B", - "t": "entity.name.type", + "t": "entity.name.type.ts", "r": { "dark_plus": "entity.name.type: #4EC9B0", "light_plus": "entity.name.type: #267F99", @@ -2563,7 +2563,7 @@ }, { "c": ">", - "t": "punctuation.definition.typeparameters", + "t": "punctuation.definition.typeparameters.end.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2577,7 +2577,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2591,7 +2591,7 @@ }, { "c": "let", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -2605,7 +2605,7 @@ }, { "c": "x2", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -2619,21 +2619,21 @@ }, { "c": ":", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "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": "A", - "t": "entity.name.type", + "t": "entity.name.type.ts", "r": { "dark_plus": "entity.name.type: #4EC9B0", "light_plus": "entity.name.type: #267F99", @@ -2647,7 +2647,7 @@ }, { "c": "<", - "t": "punctuation.definition.typeparameters", + "t": "punctuation.definition.typeparameters.begin.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2661,7 +2661,7 @@ }, { "c": "C", - "t": "entity.name.type", + "t": "entity.name.type.ts", "r": { "dark_plus": "entity.name.type: #4EC9B0", "light_plus": "entity.name.type: #267F99", @@ -2675,7 +2675,7 @@ }, { "c": "|", - "t": "keyword.operator keyword.operator.type", + "t": "keyword.operator.ts keyword.operator.type.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -2689,7 +2689,7 @@ }, { "c": "B", - "t": "entity.name.type", + "t": "entity.name.type.ts", "r": { "dark_plus": "entity.name.type: #4EC9B0", "light_plus": "entity.name.type: #267F99", @@ -2703,7 +2703,7 @@ }, { "c": ",", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2717,7 +2717,7 @@ }, { "c": "C", - "t": "entity.name.type", + "t": "entity.name.type.ts", "r": { "dark_plus": "entity.name.type: #4EC9B0", "light_plus": "entity.name.type: #267F99", @@ -2731,7 +2731,7 @@ }, { "c": "&", - "t": "keyword.operator keyword.operator.type", + "t": "keyword.operator.ts keyword.operator.type.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -2745,7 +2745,7 @@ }, { "c": "B", - "t": "entity.name.type", + "t": "entity.name.type.ts", "r": { "dark_plus": "entity.name.type: #4EC9B0", "light_plus": "entity.name.type: #267F99", @@ -2759,7 +2759,7 @@ }, { "c": ">", - "t": "punctuation.definition.typeparameters", + "t": "punctuation.definition.typeparameters.end.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2773,7 +2773,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2787,7 +2787,7 @@ }, { "c": "const", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -2801,7 +2801,7 @@ }, { "c": "t", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -2815,7 +2815,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -2829,7 +2829,7 @@ }, { "c": "1", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -2843,7 +2843,7 @@ }, { "c": "<", - "t": "keyword.operator.relational", + "t": "keyword.operator.relational.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -2857,7 +2857,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2871,7 +2871,7 @@ }, { "c": "5", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -2885,7 +2885,7 @@ }, { "c": ">", - "t": "keyword.operator.relational", + "t": "keyword.operator.relational.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -2899,7 +2899,7 @@ }, { "c": "10", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -2913,7 +2913,7 @@ }, { "c": "?", - "t": "punctuation.delimiter keyword.operator.ternary", + "t": "punctuation.delimiter.ts keyword.operator.ternary.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -2927,7 +2927,7 @@ }, { "c": "1", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -2941,7 +2941,7 @@ }, { "c": ":", - "t": "punctuation.delimiter keyword.operator.ternary", + "t": "punctuation.delimiter.ts keyword.operator.ternary.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -2955,7 +2955,7 @@ }, { "c": "2", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -2969,7 +2969,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2983,7 +2983,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2997,7 +2997,7 @@ }, { "c": "var", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -3011,7 +3011,7 @@ }, { "c": "f6", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -3025,7 +3025,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -3039,7 +3039,7 @@ }, { "c": "1", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -3053,7 +3053,7 @@ }, { "c": "<", - "t": "keyword.operator.relational", + "t": "keyword.operator.relational.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -3067,7 +3067,7 @@ }, { "c": "foo", - "t": "variable entity.name.function", + "t": "variable.ts entity.name.function.ts", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -3081,7 +3081,7 @@ }, { "c": "<", - "t": "punctuation.definition.typeparameters", + "t": "punctuation.definition.typeparameters.begin.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -3095,7 +3095,7 @@ }, { "c": "string", - "t": "support.type support.type.primitive", + "t": "support.type.ts support.type.primitive.ts", "r": { "dark_plus": "support.type: #4EC9B0", "light_plus": "support.type: #267F99", @@ -3109,7 +3109,7 @@ }, { "c": ">", - "t": "punctuation.definition.typeparameters", + "t": "punctuation.definition.typeparameters.end.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -3123,7 +3123,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -3137,7 +3137,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -3151,7 +3151,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", diff --git a/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-issue241715_ts.json b/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-issue241715_ts.json new file mode 100644 index 000000000000..6d4254d3ab7d --- /dev/null +++ b/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-issue241715_ts.json @@ -0,0 +1,4692 @@ +[ + { + "c": "type", + "t": "storage.type.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": "obj", + "t": "entity.name.type.ts entity.name.type.alias.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": "=", + "t": "keyword.operator.assignment.ts", + "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": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "key", + "t": "variable.ts variable.parameter.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": ":", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", + "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": "string", + "t": "support.type.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": "]", + "t": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", + "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": "any", + "t": "support.type.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": "}", + "t": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "function", + "t": "storage.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": "destruct", + "t": "variable.ts entity.name.function.ts", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA", + "dark_modern": "entity.name.function: #DCDCAA", + "hc_light": "entity.name.function: #5E2CBC", + "light_modern": "entity.name.function: #795E26" + } + }, + { + "c": "(", + "t": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "start", + "t": "variable.ts variable.parameter.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": ",", + "t": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "end", + "t": "variable.ts variable.parameter.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": ",", + "t": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "message", + "t": "variable.ts variable.parameter.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": "}", + "t": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", + "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": "any", + "t": "support.type.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": ")", + "t": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", + "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": "any", + "t": "support.type.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": "{", + "t": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "return", + "t": "keyword.control.ts", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0", + "dark_modern": "keyword.control: #C586C0", + "hc_light": "keyword.control: #B5200D", + "light_modern": "keyword.control: #AF00DB" + } + }, + { + "c": "start", + "t": "variable.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": "+", + "t": "keyword.operator.arithmetic.ts", + "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": "end", + "t": "variable.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": "+", + "t": "keyword.operator.arithmetic.ts", + "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": "message", + "t": "variable.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": "}", + "t": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "function", + "t": "storage.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": "destructArr", + "t": "variable.ts entity.name.function.ts", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA", + "dark_modern": "entity.name.function: #DCDCAA", + "hc_light": "entity.name.function: #5E2CBC", + "light_modern": "entity.name.function: #795E26" + } + }, + { + "c": "(", + "t": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "first", + "t": "variable.ts variable.parameter.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": ",", + "t": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "second", + "t": "variable.ts variable.parameter.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": "]", + "t": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", + "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": "any", + "t": "support.type.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": ")", + "t": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", + "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": "any", + "t": "support.type.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": "{", + "t": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "return", + "t": "keyword.control.ts", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0", + "dark_modern": "keyword.control: #C586C0", + "hc_light": "keyword.control: #B5200D", + "light_modern": "keyword.control: #AF00DB" + } + }, + { + "c": "first", + "t": "variable.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": "+", + "t": "keyword.operator.arithmetic.ts", + "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": "second", + "t": "variable.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": "}", + "t": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "interface", + "t": "storage.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": "WebviewMessageUpdateEverything", + "t": "entity.name.type.ts entity.name.type.interface.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": "extends", + "t": "storage.modifier.ts", + "r": { + "dark_plus": "storage.modifier: #569CD6", + "light_plus": "storage.modifier: #0000FF", + "dark_vs": "storage.modifier: #569CD6", + "light_vs": "storage.modifier: #0000FF", + "hc_black": "storage.modifier: #569CD6", + "dark_modern": "storage.modifier: #569CD6", + "hc_light": "storage.modifier: #0F4A85", + "light_modern": "storage.modifier: #0000FF" + } + }, + { + "c": "WebviewMessageBase", + "t": "entity.name.type.ts entity.other.inherited-class.ts", + "r": { + "dark_plus": "entity.other.inherited-class: #4EC9B0", + "light_plus": "entity.other.inherited-class: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.other.inherited-class: #4EC9B0", + "dark_modern": "entity.other.inherited-class: #4EC9B0", + "hc_light": "entity.other.inherited-class: #185E73", + "light_modern": "entity.other.inherited-class: #267F99" + } + }, + { + "c": "{", + "t": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "type", + "t": "storage.type.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": "NegNum", + "t": "entity.name.type.ts entity.name.type.alias.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": "=", + "t": "keyword.operator.assignment.ts", + "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": "keyword.operator.arithmetic.ts", + "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": "constant.numeric.ts", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #098658", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #098658", + "hc_black": "constant.numeric: #B5CEA8", + "dark_modern": "constant.numeric: #B5CEA8", + "hc_light": "constant.numeric: #096D48", + "light_modern": "constant.numeric: #098658" + } + }, + { + "c": "|", + "t": "keyword.operator.ts keyword.operator.type.ts", + "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": "keyword.operator.arithmetic.ts", + "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": "constant.numeric.ts", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #098658", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #098658", + "hc_black": "constant.numeric: #B5CEA8", + "dark_modern": "constant.numeric: #B5CEA8", + "hc_light": "constant.numeric: #096D48", + "light_modern": "constant.numeric: #098658" + } + }, + { + "c": "|", + "t": "keyword.operator.ts keyword.operator.type.ts", + "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": "keyword.operator.arithmetic.ts", + "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": "10", + "t": "constant.numeric.ts", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #098658", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #098658", + "hc_black": "constant.numeric: #B5CEA8", + "dark_modern": "constant.numeric: #B5CEA8", + "hc_light": "constant.numeric: #096D48", + "light_modern": "constant.numeric: #098658" + } + }, + { + "c": ";", + "t": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "const", + "t": "storage.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": "n", + "t": "variable.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": ":", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", + "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": "NegNum", + "t": "entity.name.type.ts meta.type.annotation.ts entity.name.type.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": "=", + "t": "keyword.operator.assignment.ts", + "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": "keyword.operator.arithmetic.ts", + "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": "10", + "t": "constant.numeric.ts", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #098658", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #098658", + "hc_black": "constant.numeric: #B5CEA8", + "dark_modern": "constant.numeric: #B5CEA8", + "hc_light": "constant.numeric: #096D48", + "light_modern": "constant.numeric: #098658" + } + }, + { + "c": ";", + "t": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "export", + "t": "keyword.control.ts", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0", + "dark_modern": "keyword.control: #C586C0", + "hc_light": "keyword.control: #B5200D", + "light_modern": "keyword.control: #AF00DB" + } + }, + { + "c": "interface", + "t": "storage.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": "OptionalMethod", + "t": "entity.name.type.ts entity.name.type.interface.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": "{", + "t": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "optMeth", + "t": "variable.ts meta.definition.method.ts entity.name.function.ts", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA", + "dark_modern": "entity.name.function: #DCDCAA", + "hc_light": "entity.name.function: #5E2CBC", + "light_modern": "entity.name.function: #795E26" + } + }, + { + "c": "?", + "t": "punctuation.delimiter.ts keyword.operator.optional.ts", + "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": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", + "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": "any", + "t": "support.type.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": ";", + "t": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "window", + "t": "variable.ts variable.other.object.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": ".", + "t": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "addEventListener", + "t": "variable.ts entity.name.function.ts", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA", + "dark_modern": "entity.name.function: #DCDCAA", + "hc_light": "entity.name.function: #5E2CBC", + "light_modern": "entity.name.function: #795E26" + } + }, + { + "c": "(", + "t": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "string.quoted.single.ts punctuation.definition.string.begin.ts", + "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": "message", + "t": "string.quoted.single.ts", + "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": "string.quoted.single.ts punctuation.definition.string.end.ts", + "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": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "event", + "t": "variable.ts variable.parameter.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": "=>", + "t": "storage.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": "{", + "t": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "return", + "t": "keyword.control.ts", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0", + "dark_modern": "keyword.control: #C586C0", + "hc_light": "keyword.control: #B5200D", + "light_modern": "keyword.control: #AF00DB" + } + }, + { + "c": "event", + "t": "variable.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": "}", + "t": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "export", + "t": "keyword.control.ts", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0", + "dark_modern": "keyword.control: #C586C0", + "hc_light": "keyword.control: #B5200D", + "light_modern": "keyword.control: #AF00DB" + } + }, + { + "c": "function", + "t": "storage.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": "dayOfTheWeek", + "t": "variable.ts entity.name.function.ts", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA", + "dark_modern": "entity.name.function: #DCDCAA", + "hc_light": "entity.name.function: #5E2CBC", + "light_modern": "entity.name.function: #795E26" + } + }, + { + "c": "(", + "t": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "date", + "t": "variable.ts variable.parameter.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": ":", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", + "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": "dayjs", + "t": "meta.type.annotation.ts entity.name.type.ts variable.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": ".", + "t": "meta.type.annotation.ts entity.name.type.ts punctuation.delimiter.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": "Dayjs", + "t": "meta.type.annotation.ts entity.name.type.ts entity.name.type.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": ")", + "t": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", + "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": "string", + "t": "support.type.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": "{", + "t": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "return", + "t": "keyword.control.ts", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0", + "dark_modern": "keyword.control: #C586C0", + "hc_light": "keyword.control: #B5200D", + "light_modern": "keyword.control: #AF00DB" + } + }, + { + "c": "date", + "t": "variable.ts variable.other.object.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": ".", + "t": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "format", + "t": "variable.ts entity.name.function.ts", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA", + "dark_modern": "entity.name.function: #DCDCAA", + "hc_light": "entity.name.function: #5E2CBC", + "light_modern": "entity.name.function: #795E26" + } + }, + { + "c": "(", + "t": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "string.quoted.single.ts punctuation.definition.string.begin.ts", + "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": "ddd", + "t": "string.quoted.single.ts", + "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": "string.quoted.single.ts punctuation.definition.string.end.ts", + "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": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "type", + "t": "storage.type.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": "N", + "t": "entity.name.type.ts entity.name.type.alias.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": "=", + "t": "keyword.operator.assignment.ts", + "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": "never", + "t": "support.type.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": "|", + "t": "keyword.operator.ts keyword.operator.type.ts", + "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": "any", + "t": "support.type.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": "|", + "t": "keyword.operator.ts keyword.operator.type.ts", + "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": "unknown", + "t": "support.type.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": ";", + "t": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "type", + "t": "storage.type.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": "Truthy", + "t": "entity.name.type.ts entity.name.type.alias.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": "<", + "t": "punctuation.definition.typeparameters.begin.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "entity.name.type.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": ">", + "t": "punctuation.definition.typeparameters.end.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "keyword.operator.assignment.ts", + "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", + "t": "entity.name.type.ts entity.name.type.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": "extends", + "t": "storage.modifier.ts", + "r": { + "dark_plus": "storage.modifier: #569CD6", + "light_plus": "storage.modifier: #0000FF", + "dark_vs": "storage.modifier: #569CD6", + "light_vs": "storage.modifier: #0000FF", + "hc_black": "storage.modifier: #569CD6", + "dark_modern": "storage.modifier: #569CD6", + "hc_light": "storage.modifier: #0F4A85", + "light_modern": "storage.modifier: #0000FF" + } + }, + { + "c": "'", + "t": "string.quoted.single.ts punctuation.definition.string.begin.ts punctuation.definition.string.end.ts", + "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": "string.quoted.single.ts punctuation.definition.string.begin.ts punctuation.definition.string.end.ts", + "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": "keyword.operator.ts keyword.operator.type.ts", + "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": "constant.numeric.ts", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #098658", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #098658", + "hc_black": "constant.numeric: #B5CEA8", + "dark_modern": "constant.numeric: #B5CEA8", + "hc_light": "constant.numeric: #096D48", + "light_modern": "constant.numeric: #098658" + } + }, + { + "c": "|", + "t": "keyword.operator.ts keyword.operator.type.ts", + "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": "false", + "t": "constant.language.boolean.false.ts support.type.builtin.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": "|", + "t": "keyword.operator.ts keyword.operator.type.ts", + "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": "null", + "t": "constant.language.null.ts support.type.builtin.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": "|", + "t": "keyword.operator.ts keyword.operator.type.ts", + "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": "undefined", + "t": "constant.language.undefined.ts support.type.builtin.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": "?", + "t": "punctuation.delimiter.ts keyword.operator.ternary.ts", + "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": "never", + "t": "support.type.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": ":", + "t": "punctuation.delimiter.ts keyword.operator.ternary.ts", + "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", + "t": "entity.name.type.ts entity.name.type.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": ";", + "t": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "export", + "t": "keyword.control.ts", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0", + "dark_modern": "keyword.control: #C586C0", + "hc_light": "keyword.control: #B5200D", + "light_modern": "keyword.control: #AF00DB" + } + }, + { + "c": "function", + "t": "storage.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": "guardedBoolean", + "t": "variable.ts entity.name.function.ts", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA", + "dark_modern": "entity.name.function: #DCDCAA", + "hc_light": "entity.name.function: #5E2CBC", + "light_modern": "entity.name.function: #795E26" + } + }, + { + "c": "<", + "t": "punctuation.definition.typeparameters.begin.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "entity.name.type.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": ">", + "t": "punctuation.definition.typeparameters.end.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "value", + "t": "variable.ts variable.parameter.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": ":", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", + "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", + "t": "entity.name.type.ts meta.type.annotation.ts entity.name.type.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": ")", + "t": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", + "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": "value", + "t": "variable.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": "is", + "t": "keyword.operator.expression.is.ts", + "r": { + "dark_plus": "keyword.operator.expression: #569CD6", + "light_plus": "keyword.operator.expression: #0000FF", + "dark_vs": "keyword.operator.expression: #569CD6", + "light_vs": "keyword.operator.expression: #0000FF", + "hc_black": "keyword.operator.expression: #569CD6", + "dark_modern": "keyword.operator.expression: #569CD6", + "hc_light": "keyword.operator.expression: #0F4A85", + "light_modern": "keyword.operator.expression: #0000FF" + } + }, + { + "c": "Truthy", + "t": "entity.name.type.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": "<", + "t": "punctuation.definition.typeparameters.begin.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "entity.name.type.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": ">", + "t": "punctuation.definition.typeparameters.end.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "return", + "t": "keyword.control.ts", + "r": { + "dark_plus": "keyword.control: #C586C0", + "light_plus": "keyword.control: #AF00DB", + "dark_vs": "keyword.control: #569CD6", + "light_vs": "keyword.control: #0000FF", + "hc_black": "keyword.control: #C586C0", + "dark_modern": "keyword.control: #C586C0", + "hc_light": "keyword.control: #B5200D", + "light_modern": "keyword.control: #AF00DB" + } + }, + { + "c": "Boolean", + "t": "variable.ts entity.name.function.ts", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA", + "dark_modern": "entity.name.function: #DCDCAA", + "hc_light": "entity.name.function: #5E2CBC", + "light_modern": "entity.name.function: #795E26" + } + }, + { + "c": "(", + "t": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "value", + "t": "variable.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": ")", + "t": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "type", + "t": "storage.type.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": "Truthy", + "t": "entity.name.type.ts entity.name.type.alias.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": "<", + "t": "punctuation.definition.typeparameters.begin.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "entity.name.type.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": ">", + "t": "punctuation.definition.typeparameters.end.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "keyword.operator.assignment.ts", + "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", + "t": "entity.name.type.ts entity.name.type.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": "extends", + "t": "storage.modifier.ts", + "r": { + "dark_plus": "storage.modifier: #569CD6", + "light_plus": "storage.modifier: #0000FF", + "dark_vs": "storage.modifier: #569CD6", + "light_vs": "storage.modifier: #0000FF", + "hc_black": "storage.modifier: #569CD6", + "dark_modern": "storage.modifier: #569CD6", + "hc_light": "storage.modifier: #0F4A85", + "light_modern": "storage.modifier: #0000FF" + } + }, + { + "c": "'", + "t": "string.quoted.single.ts punctuation.definition.string.begin.ts punctuation.definition.string.end.ts", + "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": "string.quoted.single.ts punctuation.definition.string.begin.ts punctuation.definition.string.end.ts", + "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": "keyword.operator.ts keyword.operator.type.ts", + "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": "constant.numeric.ts", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #098658", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #098658", + "hc_black": "constant.numeric: #B5CEA8", + "dark_modern": "constant.numeric: #B5CEA8", + "hc_light": "constant.numeric: #096D48", + "light_modern": "constant.numeric: #098658" + } + }, + { + "c": "|", + "t": "keyword.operator.ts keyword.operator.type.ts", + "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": "false", + "t": "constant.language.boolean.false.ts support.type.builtin.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": "|", + "t": "keyword.operator.ts keyword.operator.type.ts", + "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": "null", + "t": "constant.language.null.ts support.type.builtin.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": "|", + "t": "keyword.operator.ts keyword.operator.type.ts", + "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": "undefined", + "t": "constant.language.undefined.ts support.type.builtin.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": "?", + "t": "punctuation.delimiter.ts keyword.operator.ternary.ts", + "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": "never", + "t": "support.type.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": ":", + "t": "punctuation.delimiter.ts keyword.operator.ternary.ts", + "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", + "t": "entity.name.type.ts entity.name.type.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": ";", + "t": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "const", + "t": "storage.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": "enum", + "t": "storage.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": "EnumName", + "t": "variable.ts entity.name.type.enum.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": "{", + "t": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "one", + "t": "variable.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": "=", + "t": "keyword.operator.assignment.ts", + "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": "constant.numeric.ts", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #098658", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #098658", + "hc_black": "constant.numeric: #B5CEA8", + "dark_modern": "constant.numeric: #B5CEA8", + "hc_light": "constant.numeric: #096D48", + "light_modern": "constant.numeric: #098658" + } + }, + { + "c": ",", + "t": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "void", + "t": "keyword.operator.expression.void.ts", + "r": { + "dark_plus": "keyword.operator.expression: #569CD6", + "light_plus": "keyword.operator.expression: #0000FF", + "dark_vs": "keyword.operator.expression: #569CD6", + "light_vs": "keyword.operator.expression: #0000FF", + "hc_black": "keyword.operator.expression: #569CD6", + "dark_modern": "keyword.operator.expression: #569CD6", + "hc_light": "keyword.operator.expression: #0F4A85", + "light_modern": "keyword.operator.expression: #0000FF" + } + }, + { + "c": "0", + "t": "constant.numeric.ts", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #098658", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #098658", + "hc_black": "constant.numeric: #B5CEA8", + "dark_modern": "constant.numeric: #B5CEA8", + "hc_light": "constant.numeric: #096D48", + "light_modern": "constant.numeric: #098658" + } + }, + { + "c": ";", + "t": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "function", + "t": "storage.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": "*", + "t": "keyword.generator.asterisk.ts", + "r": { + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6", + "dark_modern": "keyword: #569CD6", + "hc_light": "keyword: #0F4A85", + "light_modern": "keyword: #0000FF" + } + }, + { + "c": "makeIterator", + "t": "variable.ts meta.definition.function.ts entity.name.function.ts", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA", + "dark_modern": "entity.name.function: #DCDCAA", + "hc_light": "entity.name.function: #5E2CBC", + "light_modern": "entity.name.function: #795E26" + } + }, + { + "c": "(", + "t": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "start", + "t": "variable.ts variable.parameter.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": "=", + "t": "keyword.operator.assignment.ts", + "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": "constant.numeric.ts", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #098658", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #098658", + "hc_black": "constant.numeric: #B5CEA8", + "dark_modern": "constant.numeric: #B5CEA8", + "hc_light": "constant.numeric: #096D48", + "light_modern": "constant.numeric: #098658" + } + }, + { + "c": ",", + "t": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "end", + "t": "variable.ts variable.parameter.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": "=", + "t": "keyword.operator.assignment.ts", + "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": "Infinity", + "t": "variable.ts variable.parameter.ts constant.language.infinity.ts", + "r": { + "dark_plus": "constant.language: #569CD6", + "light_plus": "constant.language: #0000FF", + "dark_vs": "constant.language: #569CD6", + "light_vs": "constant.language: #0000FF", + "hc_black": "constant.language: #569CD6", + "dark_modern": "constant.language: #569CD6", + "hc_light": "constant.language: #0F4A85", + "light_modern": "constant.language: #0000FF" + } + }, + { + "c": ",", + "t": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "step", + "t": "variable.ts variable.parameter.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": "=", + "t": "keyword.operator.assignment.ts", + "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": "constant.numeric.ts", + "r": { + "dark_plus": "constant.numeric: #B5CEA8", + "light_plus": "constant.numeric: #098658", + "dark_vs": "constant.numeric: #B5CEA8", + "light_vs": "constant.numeric: #098658", + "hc_black": "constant.numeric: #B5CEA8", + "dark_modern": "constant.numeric: #B5CEA8", + "hc_light": "constant.numeric: #096D48", + "light_modern": "constant.numeric: #098658" + } + }, + { + "c": ")", + "t": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "function", + "t": "storage.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": "makeDate", + "t": "variable.ts entity.name.function.ts", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA", + "dark_modern": "entity.name.function: #DCDCAA", + "hc_light": "entity.name.function: #5E2CBC", + "light_modern": "entity.name.function: #795E26" + } + }, + { + "c": "(", + "t": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "timestamp", + "t": "variable.ts variable.parameter.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": ":", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", + "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": "number", + "t": "support.type.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": ")", + "t": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", + "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": "Date", + "t": "entity.name.type.ts meta.type.annotation.ts entity.name.type.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": ";", + "t": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "function", + "t": "storage.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": "makeDate", + "t": "variable.ts entity.name.function.ts", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA", + "dark_modern": "entity.name.function: #DCDCAA", + "hc_light": "entity.name.function: #5E2CBC", + "light_modern": "entity.name.function: #795E26" + } + }, + { + "c": "(", + "t": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "m", + "t": "variable.ts variable.parameter.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": ":", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", + "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": "number", + "t": "support.type.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": ",", + "t": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "d", + "t": "variable.ts variable.parameter.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": ":", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", + "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": "number", + "t": "support.type.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": ",", + "t": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "y", + "t": "variable.ts variable.parameter.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": ":", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", + "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": "number", + "t": "support.type.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": ")", + "t": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", + "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": "Date", + "t": "entity.name.type.ts meta.type.annotation.ts entity.name.type.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": ";", + "t": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "function", + "t": "storage.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": "makeDate", + "t": "variable.ts entity.name.function.ts", + "r": { + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.function: #DCDCAA", + "dark_modern": "entity.name.function: #DCDCAA", + "hc_light": "entity.name.function: #5E2CBC", + "light_modern": "entity.name.function: #795E26" + } + }, + { + "c": "(", + "t": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "mOrTimestamp", + "t": "variable.ts variable.parameter.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": ":", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", + "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": "number", + "t": "support.type.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": ",", + "t": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "d", + "t": "variable.ts variable.parameter.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": "?", + "t": "punctuation.delimiter.ts keyword.operator.optional.ts", + "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": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", + "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": "number", + "t": "support.type.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": ",", + "t": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "y", + "t": "variable.ts variable.parameter.ts", + "r": { + "dark_plus": "variable: #9CDCFE", + "light_plus": "variable: #001080", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "variable: #9CDCFE", + "dark_modern": "variable: #9CDCFE", + "hc_light": "variable: #001080", + "light_modern": "variable: #001080" + } + }, + { + "c": "?", + "t": "punctuation.delimiter.ts keyword.operator.optional.ts", + "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": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", + "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": "number", + "t": "support.type.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": ")", + "t": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", + "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": "Date", + "t": "entity.name.type.ts meta.type.annotation.ts entity.name.type.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": "{", + "t": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "type", + "t": "storage.type.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": "StringNumberBooleans", + "t": "entity.name.type.ts entity.name.type.alias.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": "=", + "t": "keyword.operator.assignment.ts", + "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": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "string", + "t": "support.type.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": ",", + "t": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "number", + "t": "support.type.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": ",", + "t": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "keyword.operator.rest.ts", + "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": "boolean", + "t": "support.type.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": "[", + "t": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "type", + "t": "storage.type.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": "StringBooleansNumber", + "t": "entity.name.type.ts entity.name.type.alias.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": "=", + "t": "keyword.operator.assignment.ts", + "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": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "string", + "t": "support.type.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": ",", + "t": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "keyword.operator.rest.ts", + "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": "boolean", + "t": "support.type.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": "[", + "t": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "number", + "t": "support.type.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": "]", + "t": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "type", + "t": "storage.type.type.ts", + "r": { + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" + } + }, + { + "c": "BooleansStringNumber", + "t": "entity.name.type.ts entity.name.type.alias.ts", + "r": { + "dark_plus": "entity.name.type: #4EC9B0", + "light_plus": "entity.name.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "entity.name.type: #4EC9B0", + "dark_modern": "entity.name.type: #4EC9B0", + "hc_light": "entity.name.type: #185E73", + "light_modern": "entity.name.type: #267F99" + } + }, + { + "c": "=", + "t": "keyword.operator.assignment.ts", + "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": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "keyword.operator.rest.ts", + "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": "boolean", + "t": "support.type.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": "[", + "t": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "string", + "t": "support.type.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": ",", + "t": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "default: #FFFFFF", + "dark_modern": "default: #CCCCCC", + "hc_light": "default: #292929", + "light_modern": "default: #3B3B3B" + } + }, + { + "c": "number", + "t": "support.type.ts support.type.primitive.ts", + "r": { + "dark_plus": "support.type: #4EC9B0", + "light_plus": "support.type: #267F99", + "dark_vs": "default: #D4D4D4", + "light_vs": "default: #000000", + "hc_black": "support.type: #4EC9B0", + "dark_modern": "support.type: #4EC9B0", + "hc_light": "support.type: #185E73", + "light_modern": "support.type: #267F99" + } + }, + { + "c": "]", + "t": "punctuation.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "dark_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": "punctuation.delimiter.ts", + "r": { + "dark_plus": "default: #D4D4D4", + "light_plus": "default: #000000", + "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-tree-sitter-results/test-issue5431_ts.json b/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-issue5431_ts.json index bcbec84c6672..a3ed92081038 100644 --- a/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-issue5431_ts.json +++ b/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-issue5431_ts.json @@ -1,7 +1,7 @@ [ { "c": "function", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -15,7 +15,7 @@ }, { "c": "foo", - "t": "variable entity.name.function", + "t": "variable.ts entity.name.function.ts", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -29,7 +29,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -43,7 +43,7 @@ }, { "c": "isAll", - "t": "variable variable.parameter", + "t": "variable.ts variable.parameter.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -57,7 +57,7 @@ }, { "c": ",", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -71,7 +71,7 @@ }, { "c": "startTime", - "t": "variable variable.parameter", + "t": "variable.ts variable.parameter.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -85,7 +85,7 @@ }, { "c": ",", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -99,7 +99,7 @@ }, { "c": "endTime", - "t": "variable variable.parameter", + "t": "variable.ts variable.parameter.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -113,7 +113,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -127,7 +127,7 @@ }, { "c": "{", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -141,7 +141,7 @@ }, { "c": "const", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -155,7 +155,7 @@ }, { "c": "timeRange", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -169,7 +169,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -183,7 +183,7 @@ }, { "c": "isAll", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -197,7 +197,7 @@ }, { "c": "?", - "t": "punctuation.delimiter keyword.operator.ternary", + "t": "punctuation.delimiter.ts keyword.operator.ternary.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -211,7 +211,7 @@ }, { "c": "'", - "t": "string", + "t": "string.quoted.single.ts punctuation.definition.string.begin.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -225,7 +225,7 @@ }, { "c": "所有时间", - "t": "string", + "t": "string.quoted.single.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -239,7 +239,7 @@ }, { "c": "'", - "t": "string", + "t": "string.quoted.single.ts punctuation.definition.string.end.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -253,7 +253,7 @@ }, { "c": ":", - "t": "punctuation.delimiter keyword.operator.ternary", + "t": "punctuation.delimiter.ts keyword.operator.ternary.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -267,7 +267,7 @@ }, { "c": "`", - "t": "string", + "t": "string.template.ts punctuation.definition.string.template.begin.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -281,7 +281,7 @@ }, { "c": "${", - "t": "string punctuation.definition.template-expression.begin", + "t": "string.template.ts punctuation.definition.template-expression.begin.ts", "r": { "dark_plus": "punctuation.definition.template-expression.begin: #569CD6", "light_plus": "punctuation.definition.template-expression.begin: #0000FF", @@ -295,7 +295,7 @@ }, { "c": "startTime", - "t": "string variable", + "t": "string.template.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -309,7 +309,7 @@ }, { "c": "}", - "t": "string punctuation punctuation.definition.template-expression.end", + "t": "string.template.ts punctuation.ts punctuation.definition.template-expression.end.ts", "r": { "dark_plus": "punctuation.definition.template-expression.end: #569CD6", "light_plus": "punctuation.definition.template-expression.end: #0000FF", @@ -323,7 +323,7 @@ }, { "c": " - ", - "t": "string", + "t": "string.template.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -337,7 +337,7 @@ }, { "c": "${", - "t": "string punctuation.definition.template-expression.begin", + "t": "string.template.ts punctuation.definition.template-expression.begin.ts", "r": { "dark_plus": "punctuation.definition.template-expression.begin: #569CD6", "light_plus": "punctuation.definition.template-expression.begin: #0000FF", @@ -351,7 +351,7 @@ }, { "c": "endTime", - "t": "string variable", + "t": "string.template.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -365,7 +365,7 @@ }, { "c": "}", - "t": "string punctuation punctuation.definition.template-expression.end", + "t": "string.template.ts punctuation.ts punctuation.definition.template-expression.end.ts", "r": { "dark_plus": "punctuation.definition.template-expression.end: #569CD6", "light_plus": "punctuation.definition.template-expression.end: #0000FF", @@ -379,7 +379,7 @@ }, { "c": "`", - "t": "string", + "t": "string.template.ts punctuation.definition.string.template.end.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -393,7 +393,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -407,7 +407,7 @@ }, { "c": "return", - "t": "keyword.control", + "t": "keyword.control.ts", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -421,7 +421,7 @@ }, { "c": "true", - "t": "constant.language.boolean.true", + "t": "constant.language.boolean.true.ts", "r": { "dark_plus": "constant.language: #569CD6", "light_plus": "constant.language: #0000FF", @@ -435,7 +435,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -449,7 +449,7 @@ }, { "c": "}", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", diff --git a/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-issue5465_ts.json b/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-issue5465_ts.json index a7feac02ad5a..100bf5c3d07b 100644 --- a/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-issue5465_ts.json +++ b/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-issue5465_ts.json @@ -1,7 +1,7 @@ [ { "c": "function", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -15,35 +15,35 @@ }, { "c": "*", - "t": "", + "t": "keyword.generator.asterisk.ts", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "dark_plus": "keyword: #569CD6", + "light_plus": "keyword: #0000FF", + "dark_vs": "keyword: #569CD6", + "light_vs": "keyword: #0000FF", + "hc_black": "keyword: #569CD6", + "dark_modern": "keyword: #569CD6", + "hc_light": "keyword: #0F4A85", + "light_modern": "keyword: #0000FF" } }, { "c": "foo2", - "t": "variable", + "t": "variable.ts meta.definition.function.ts entity.name.function.ts", "r": { - "dark_plus": "variable: #9CDCFE", - "light_plus": "variable: #001080", + "dark_plus": "entity.name.function: #DCDCAA", + "light_plus": "entity.name.function: #795E26", "dark_vs": "default: #D4D4D4", "light_vs": "default: #000000", - "hc_black": "variable: #9CDCFE", - "dark_modern": "variable: #9CDCFE", - "hc_light": "variable: #001080", - "light_modern": "variable: #001080" + "hc_black": "entity.name.function: #DCDCAA", + "dark_modern": "entity.name.function: #DCDCAA", + "hc_light": "entity.name.function: #5E2CBC", + "light_modern": "entity.name.function: #795E26" } }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -57,7 +57,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -71,7 +71,7 @@ }, { "c": "{", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -85,7 +85,7 @@ }, { "c": "yield", - "t": "keyword.control", + "t": "keyword.control.ts", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -99,7 +99,7 @@ }, { "c": "'", - "t": "string", + "t": "string.quoted.single.ts punctuation.definition.string.begin.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -113,7 +113,7 @@ }, { "c": "bar", - "t": "string", + "t": "string.quoted.single.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -127,7 +127,7 @@ }, { "c": "'", - "t": "string", + "t": "string.quoted.single.ts punctuation.definition.string.end.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -141,7 +141,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -155,7 +155,7 @@ }, { "c": "yield", - "t": "keyword.control", + "t": "keyword.control.ts", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -183,7 +183,7 @@ }, { "c": "[", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -197,7 +197,7 @@ }, { "c": "'", - "t": "string", + "t": "string.quoted.single.ts punctuation.definition.string.begin.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -211,7 +211,7 @@ }, { "c": "bar", - "t": "string", + "t": "string.quoted.single.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -225,7 +225,7 @@ }, { "c": "'", - "t": "string", + "t": "string.quoted.single.ts punctuation.definition.string.end.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -239,7 +239,7 @@ }, { "c": "]", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -253,7 +253,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -267,7 +267,7 @@ }, { "c": "}", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", diff --git a/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-issue5566_ts.json b/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-issue5566_ts.json index 16bd135e41d5..c1a632297c20 100644 --- a/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-issue5566_ts.json +++ b/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-issue5566_ts.json @@ -1,7 +1,7 @@ [ { "c": "function", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -15,7 +15,7 @@ }, { "c": "foo3", - "t": "variable entity.name.function", + "t": "variable.ts entity.name.function.ts", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -29,7 +29,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -43,7 +43,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -57,7 +57,7 @@ }, { "c": "{", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -71,7 +71,7 @@ }, { "c": "const", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -85,7 +85,7 @@ }, { "c": "foo", - "t": "variable entity.name.function", + "t": "variable.ts entity.name.function.ts", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -99,7 +99,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -113,7 +113,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -127,7 +127,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -141,21 +141,21 @@ }, { "c": ":", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "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": "any", - "t": "support.type support.type.primitive", + "t": "support.type.ts support.type.primitive.ts", "r": { "dark_plus": "support.type: #4EC9B0", "light_plus": "support.type: #267F99", @@ -169,7 +169,7 @@ }, { "c": "=>", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -183,7 +183,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -197,7 +197,7 @@ }, { "c": "{", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -211,7 +211,7 @@ }, { "c": "'", - "t": "string", + "t": "string.quoted.single.ts punctuation.definition.string.begin.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -225,7 +225,7 @@ }, { "c": "bar", - "t": "string", + "t": "string.quoted.single.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -239,7 +239,7 @@ }, { "c": "'", - "t": "string", + "t": "string.quoted.single.ts punctuation.definition.string.end.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -253,7 +253,7 @@ }, { "c": ":", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -267,7 +267,7 @@ }, { "c": "'", - "t": "string", + "t": "string.quoted.single.ts punctuation.definition.string.begin.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -281,7 +281,7 @@ }, { "c": "baz", - "t": "string", + "t": "string.quoted.single.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -295,7 +295,7 @@ }, { "c": "'", - "t": "string", + "t": "string.quoted.single.ts punctuation.definition.string.end.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -309,7 +309,7 @@ }, { "c": "}", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -323,7 +323,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -337,7 +337,7 @@ }, { "c": "}", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", diff --git a/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-jsdoc-multiline-type_ts.json b/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-jsdoc-multiline-type_ts.json index c10c6d0f46ec..32a1169366fb 100644 --- a/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-jsdoc-multiline-type_ts.json +++ b/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-jsdoc-multiline-type_ts.json @@ -1,7 +1,7 @@ [ { "c": "/**\n * @typedef {{\n * id: number,\n * fn: !Function,\n * context: (!Object|undefined)\n * }}\n * @private\n */", - "t": "comment", + "t": "comment.ts", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -15,7 +15,7 @@ }, { "c": "goog", - "t": "variable", + "t": "variable.ts variable.other.object.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -29,7 +29,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -43,7 +43,7 @@ }, { "c": "dom", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -57,7 +57,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -71,7 +71,7 @@ }, { "c": "animationFrame", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -85,7 +85,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -99,7 +99,7 @@ }, { "c": "Task_", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -113,7 +113,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -127,7 +127,7 @@ }, { "c": "/**\n * @typedef {{\n * measureTask: goog.dom.animationFrame.Task_,\n * mutateTask: goog.dom.animationFrame.Task_,\n * state: (!Object|undefined),\n * args: (!Array|undefined),\n * isScheduled: boolean\n * }}\n * @private\n */", - "t": "comment", + "t": "comment.ts", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -141,7 +141,7 @@ }, { "c": "goog", - "t": "variable", + "t": "variable.ts variable.other.object.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -155,7 +155,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -169,7 +169,7 @@ }, { "c": "dom", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -183,7 +183,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -197,7 +197,7 @@ }, { "c": "animationFrame", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -211,7 +211,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -225,7 +225,7 @@ }, { "c": "TaskSet_", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -239,7 +239,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", diff --git a/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-keywords_ts.json b/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-keywords_ts.json index f484d1f7efc1..6753ae3fa4c2 100644 --- a/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-keywords_ts.json +++ b/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-keywords_ts.json @@ -1,7 +1,7 @@ [ { "c": "export", - "t": "keyword.control", + "t": "keyword.control.ts", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -15,7 +15,7 @@ }, { "c": "var", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -29,7 +29,7 @@ }, { "c": "foo", - "t": "variable entity.name.function", + "t": "variable.ts entity.name.function.ts", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -43,7 +43,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -57,7 +57,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -71,7 +71,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -85,7 +85,7 @@ }, { "c": "=>", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -99,7 +99,7 @@ }, { "c": "new", - "t": "new.expr keyword.operator.new", + "t": "new.expr.ts keyword.operator.new.ts", "r": { "dark_plus": "keyword.operator.new: #569CD6", "light_plus": "keyword.operator.new: #0000FF", @@ -113,7 +113,7 @@ }, { "c": "RegExp", - "t": "new.expr variable entity.name.function", + "t": "new.expr.ts variable.ts entity.name.function.ts", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -127,7 +127,7 @@ }, { "c": "(", - "t": "new.expr punctuation", + "t": "new.expr.ts punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -141,7 +141,7 @@ }, { "c": "'", - "t": "new.expr string", + "t": "new.expr.ts string.quoted.single.ts punctuation.definition.string.begin.ts punctuation.definition.string.end.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -155,7 +155,7 @@ }, { "c": "'", - "t": "new.expr string", + "t": "new.expr.ts string.quoted.single.ts punctuation.definition.string.begin.ts punctuation.definition.string.end.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -169,7 +169,7 @@ }, { "c": ")", - "t": "new.expr punctuation", + "t": "new.expr.ts punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -183,7 +183,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", diff --git a/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-members_ts.json b/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-members_ts.json index ed55ef245875..e57fa0af7ff6 100644 --- a/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-members_ts.json +++ b/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-members_ts.json @@ -1,7 +1,7 @@ [ { "c": "class", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -15,7 +15,7 @@ }, { "c": "A2", - "t": "entity.name.type", + "t": "entity.name.type.ts entity.name.type.class.ts", "r": { "dark_plus": "entity.name.type: #4EC9B0", "light_plus": "entity.name.type: #267F99", @@ -29,7 +29,7 @@ }, { "c": "extends", - "t": "storage.modifier", + "t": "storage.modifier.ts", "r": { "dark_plus": "storage.modifier: #569CD6", "light_plus": "storage.modifier: #0000FF", @@ -43,7 +43,7 @@ }, { "c": "B1", - "t": "variable entity.other.inherited-class", + "t": "variable.ts entity.other.inherited-class.ts", "r": { "dark_plus": "entity.other.inherited-class: #4EC9B0", "light_plus": "entity.other.inherited-class: #267F99", @@ -57,7 +57,7 @@ }, { "c": "{", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -71,7 +71,7 @@ }, { "c": "public", - "t": "storage.modifier", + "t": "storage.modifier.ts", "r": { "dark_plus": "storage.modifier: #569CD6", "light_plus": "storage.modifier: #0000FF", @@ -85,7 +85,7 @@ }, { "c": "count", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -99,21 +99,21 @@ }, { "c": ":", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "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": "number", - "t": "support.type support.type.primitive", + "t": "support.type.ts support.type.primitive.ts", "r": { "dark_plus": "support.type: #4EC9B0", "light_plus": "support.type: #267F99", @@ -127,7 +127,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -141,7 +141,7 @@ }, { "c": "9", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -155,7 +155,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -169,7 +169,7 @@ }, { "c": "public", - "t": "storage.modifier", + "t": "storage.modifier.ts", "r": { "dark_plus": "storage.modifier: #569CD6", "light_plus": "storage.modifier: #0000FF", @@ -183,7 +183,7 @@ }, { "c": "resolveNextGeneration", - "t": "variable meta.definition.method entity.name.function", + "t": "variable.ts meta.definition.method.ts entity.name.function.ts", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -197,7 +197,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -211,7 +211,7 @@ }, { "c": "cell", - "t": "variable variable.parameter", + "t": "variable.ts variable.parameter.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -225,21 +225,21 @@ }, { "c": ":", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "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": "A2", - "t": "entity.name.type", + "t": "entity.name.type.ts meta.type.annotation.ts entity.name.type.ts", "r": { "dark_plus": "entity.name.type: #4EC9B0", "light_plus": "entity.name.type: #267F99", @@ -253,7 +253,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -267,7 +267,7 @@ }, { "c": "{", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -281,7 +281,7 @@ }, { "c": "}", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -295,7 +295,7 @@ }, { "c": "}", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", diff --git a/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-object-literals_ts.json b/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-object-literals_ts.json index 27671c9cc248..ecc84c7516eb 100644 --- a/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-object-literals_ts.json +++ b/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-object-literals_ts.json @@ -1,7 +1,7 @@ [ { "c": "let", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -15,7 +15,7 @@ }, { "c": "s1", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -29,7 +29,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -43,7 +43,7 @@ }, { "c": "{", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -57,7 +57,7 @@ }, { "c": "k", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -71,7 +71,7 @@ }, { "c": ":", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -85,7 +85,7 @@ }, { "c": "{", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -99,7 +99,7 @@ }, { "c": "k1", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -113,7 +113,7 @@ }, { "c": ":", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -127,7 +127,7 @@ }, { "c": "s", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -141,7 +141,7 @@ }, { "c": ",", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -155,7 +155,7 @@ }, { "c": "k2", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -169,7 +169,7 @@ }, { "c": ":", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -183,7 +183,7 @@ }, { "c": "1", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -197,7 +197,7 @@ }, { "c": "}", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -211,7 +211,7 @@ }, { "c": "}", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -225,7 +225,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", diff --git a/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-strings_ts.json b/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-strings_ts.json index c7fd2b14cfaf..15d340449e4a 100644 --- a/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-strings_ts.json +++ b/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-strings_ts.json @@ -1,7 +1,7 @@ [ { "c": "var", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -15,7 +15,7 @@ }, { "c": "x", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -29,7 +29,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -43,7 +43,7 @@ }, { "c": "`", - "t": "string", + "t": "string.template.ts punctuation.definition.string.template.begin.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -57,7 +57,7 @@ }, { "c": "Hello ", - "t": "string", + "t": "string.template.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -71,7 +71,7 @@ }, { "c": "${", - "t": "string punctuation.definition.template-expression.begin", + "t": "string.template.ts punctuation.definition.template-expression.begin.ts", "r": { "dark_plus": "punctuation.definition.template-expression.begin: #569CD6", "light_plus": "punctuation.definition.template-expression.begin: #0000FF", @@ -85,7 +85,7 @@ }, { "c": "foo", - "t": "string variable", + "t": "string.template.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -99,7 +99,7 @@ }, { "c": "}", - "t": "string punctuation punctuation.definition.template-expression.end", + "t": "string.template.ts punctuation.ts punctuation.definition.template-expression.end.ts", "r": { "dark_plus": "punctuation.definition.template-expression.end: #569CD6", "light_plus": "punctuation.definition.template-expression.end: #0000FF", @@ -113,7 +113,7 @@ }, { "c": "!", - "t": "string", + "t": "string.template.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -127,7 +127,7 @@ }, { "c": "`", - "t": "string", + "t": "string.template.ts punctuation.definition.string.template.end.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -141,7 +141,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -155,7 +155,7 @@ }, { "c": "console", - "t": "variable", + "t": "variable.ts variable.other.object.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -169,7 +169,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -183,7 +183,7 @@ }, { "c": "log", - "t": "variable entity.name.function", + "t": "variable.ts entity.name.function.ts", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -197,7 +197,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -211,7 +211,7 @@ }, { "c": "`", - "t": "string", + "t": "string.template.ts punctuation.definition.string.template.begin.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -225,7 +225,7 @@ }, { "c": "string text line 1\nstring text line 2", - "t": "string", + "t": "string.template.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -239,7 +239,7 @@ }, { "c": "`", - "t": "string", + "t": "string.template.ts punctuation.definition.string.template.end.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -253,7 +253,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -267,7 +267,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -281,7 +281,7 @@ }, { "c": "x", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -295,7 +295,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -309,7 +309,7 @@ }, { "c": "tag", - "t": "variable entity.name.function", + "t": "variable.ts entity.name.function.ts", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -323,7 +323,7 @@ }, { "c": "`", - "t": "string", + "t": "string.template.ts punctuation.definition.string.template.begin.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -337,7 +337,7 @@ }, { "c": "Hello ", - "t": "string", + "t": "string.template.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -351,7 +351,7 @@ }, { "c": "${", - "t": "string punctuation.definition.template-expression.begin", + "t": "string.template.ts punctuation.definition.template-expression.begin.ts", "r": { "dark_plus": "punctuation.definition.template-expression.begin: #569CD6", "light_plus": "punctuation.definition.template-expression.begin: #0000FF", @@ -365,7 +365,7 @@ }, { "c": "a", - "t": "string variable", + "t": "string.template.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -379,7 +379,7 @@ }, { "c": "+", - "t": "string keyword.operator.arithmetic", + "t": "string.template.ts keyword.operator.arithmetic.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -393,7 +393,7 @@ }, { "c": "b", - "t": "string variable", + "t": "string.template.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -407,7 +407,7 @@ }, { "c": "}", - "t": "string punctuation punctuation.definition.template-expression.end", + "t": "string.template.ts punctuation.ts punctuation.definition.template-expression.end.ts", "r": { "dark_plus": "punctuation.definition.template-expression.end: #569CD6", "light_plus": "punctuation.definition.template-expression.end: #0000FF", @@ -421,7 +421,7 @@ }, { "c": " world ", - "t": "string", + "t": "string.template.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -435,7 +435,7 @@ }, { "c": "${", - "t": "string punctuation.definition.template-expression.begin", + "t": "string.template.ts punctuation.definition.template-expression.begin.ts", "r": { "dark_plus": "punctuation.definition.template-expression.begin: #569CD6", "light_plus": "punctuation.definition.template-expression.begin: #0000FF", @@ -449,7 +449,7 @@ }, { "c": "a", - "t": "string variable", + "t": "string.template.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -463,7 +463,7 @@ }, { "c": "*", - "t": "string keyword.operator.arithmetic", + "t": "string.template.ts keyword.operator.arithmetic.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -477,7 +477,7 @@ }, { "c": "b", - "t": "string variable", + "t": "string.template.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -491,7 +491,7 @@ }, { "c": "}", - "t": "string punctuation punctuation.definition.template-expression.end", + "t": "string.template.ts punctuation.ts punctuation.definition.template-expression.end.ts", "r": { "dark_plus": "punctuation.definition.template-expression.end: #569CD6", "light_plus": "punctuation.definition.template-expression.end: #0000FF", @@ -505,7 +505,7 @@ }, { "c": "`", - "t": "string", + "t": "string.template.ts punctuation.definition.string.template.end.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -519,7 +519,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", diff --git a/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-this_ts.json b/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-this_ts.json index f496120fdbad..aa495a54f95e 100644 --- a/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-this_ts.json +++ b/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-this_ts.json @@ -1,7 +1,7 @@ [ { "c": "{", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -15,7 +15,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -29,7 +29,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -43,7 +43,7 @@ }, { "c": "foo", - "t": "variable variable", + "t": "variable.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -57,7 +57,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -71,7 +71,7 @@ }, { "c": "9", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -85,7 +85,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -99,7 +99,7 @@ }, { "c": "}", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", diff --git a/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test_ts.json b/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test_ts.json index 926f880f84b7..b31e6daa154a 100644 --- a/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test_ts.json +++ b/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test_ts.json @@ -1,7 +1,7 @@ [ { "c": "/* Game of Life\n * Implemented in TypeScript\n * To learn more about TypeScript, please visit http://www.typescriptlang.org/\n */", - "t": "comment", + "t": "comment.ts", "r": { "dark_plus": "comment: #6A9955", "light_plus": "comment: #008000", @@ -15,21 +15,21 @@ }, { "c": "module", - "t": "", + "t": "storage.type.namespace.ts", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "dark_plus": "storage.type: #569CD6", + "light_plus": "storage.type: #0000FF", + "dark_vs": "storage.type: #569CD6", + "light_vs": "storage.type: #0000FF", + "hc_black": "storage.type: #569CD6", + "dark_modern": "storage.type: #569CD6", + "hc_light": "storage.type: #0F4A85", + "light_modern": "storage.type: #0000FF" } }, { "c": "Conway", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -43,7 +43,7 @@ }, { "c": "{", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -57,7 +57,7 @@ }, { "c": "export", - "t": "keyword.control", + "t": "keyword.control.ts", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -71,7 +71,7 @@ }, { "c": "class", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -85,7 +85,7 @@ }, { "c": "Cell", - "t": "entity.name.type", + "t": "entity.name.type.ts entity.name.type.class.ts", "r": { "dark_plus": "entity.name.type: #4EC9B0", "light_plus": "entity.name.type: #267F99", @@ -99,7 +99,7 @@ }, { "c": "{", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -113,7 +113,7 @@ }, { "c": "public", - "t": "storage.modifier", + "t": "storage.modifier.ts", "r": { "dark_plus": "storage.modifier: #569CD6", "light_plus": "storage.modifier: #0000FF", @@ -127,7 +127,7 @@ }, { "c": "row", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -141,21 +141,21 @@ }, { "c": ":", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "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": "number", - "t": "support.type support.type.primitive", + "t": "support.type.ts support.type.primitive.ts", "r": { "dark_plus": "support.type: #4EC9B0", "light_plus": "support.type: #267F99", @@ -169,7 +169,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -183,7 +183,7 @@ }, { "c": "public", - "t": "storage.modifier", + "t": "storage.modifier.ts", "r": { "dark_plus": "storage.modifier: #569CD6", "light_plus": "storage.modifier: #0000FF", @@ -197,7 +197,7 @@ }, { "c": "col", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -211,21 +211,21 @@ }, { "c": ":", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "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": "number", - "t": "support.type support.type.primitive", + "t": "support.type.ts support.type.primitive.ts", "r": { "dark_plus": "support.type: #4EC9B0", "light_plus": "support.type: #267F99", @@ -239,7 +239,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -253,7 +253,7 @@ }, { "c": "public", - "t": "storage.modifier", + "t": "storage.modifier.ts", "r": { "dark_plus": "storage.modifier: #569CD6", "light_plus": "storage.modifier: #0000FF", @@ -267,7 +267,7 @@ }, { "c": "live", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -281,21 +281,21 @@ }, { "c": ":", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "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": "boolean", - "t": "support.type support.type.primitive", + "t": "support.type.ts support.type.primitive.ts", "r": { "dark_plus": "support.type: #4EC9B0", "light_plus": "support.type: #267F99", @@ -309,7 +309,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -323,7 +323,7 @@ }, { "c": "constructor", - "t": "variable meta.definition.method storage.type", + "t": "variable.ts meta.definition.method.ts storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -337,7 +337,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -351,7 +351,7 @@ }, { "c": "row", - "t": "variable variable.parameter", + "t": "variable.ts variable.parameter.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -365,21 +365,21 @@ }, { "c": ":", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "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": "number", - "t": "support.type support.type.primitive", + "t": "support.type.ts support.type.primitive.ts", "r": { "dark_plus": "support.type: #4EC9B0", "light_plus": "support.type: #267F99", @@ -393,7 +393,7 @@ }, { "c": ",", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -407,7 +407,7 @@ }, { "c": "col", - "t": "variable variable.parameter", + "t": "variable.ts variable.parameter.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -421,21 +421,21 @@ }, { "c": ":", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "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": "number", - "t": "support.type support.type.primitive", + "t": "support.type.ts support.type.primitive.ts", "r": { "dark_plus": "support.type: #4EC9B0", "light_plus": "support.type: #267F99", @@ -449,7 +449,7 @@ }, { "c": ",", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -463,7 +463,7 @@ }, { "c": "live", - "t": "variable variable.parameter", + "t": "variable.ts variable.parameter.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -477,21 +477,21 @@ }, { "c": ":", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "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": "boolean", - "t": "support.type support.type.primitive", + "t": "support.type.ts support.type.primitive.ts", "r": { "dark_plus": "support.type: #4EC9B0", "light_plus": "support.type: #267F99", @@ -505,7 +505,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -519,7 +519,7 @@ }, { "c": "{", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -533,7 +533,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -547,7 +547,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -561,7 +561,7 @@ }, { "c": "row", - "t": "variable variable", + "t": "variable.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -575,7 +575,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -589,7 +589,7 @@ }, { "c": "row", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -603,7 +603,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -617,7 +617,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -631,7 +631,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -645,7 +645,7 @@ }, { "c": "col", - "t": "variable variable", + "t": "variable.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -659,7 +659,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -673,7 +673,7 @@ }, { "c": "col", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -687,7 +687,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -701,7 +701,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -715,7 +715,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -729,7 +729,7 @@ }, { "c": "live", - "t": "variable variable", + "t": "variable.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -743,7 +743,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -757,7 +757,7 @@ }, { "c": "live", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -771,7 +771,7 @@ }, { "c": "}", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -785,7 +785,7 @@ }, { "c": "}", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -799,7 +799,7 @@ }, { "c": "export", - "t": "keyword.control", + "t": "keyword.control.ts", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -813,7 +813,7 @@ }, { "c": "class", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -827,7 +827,7 @@ }, { "c": "GameOfLife", - "t": "entity.name.type", + "t": "entity.name.type.ts entity.name.type.class.ts", "r": { "dark_plus": "entity.name.type: #4EC9B0", "light_plus": "entity.name.type: #267F99", @@ -841,7 +841,7 @@ }, { "c": "{", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -855,7 +855,7 @@ }, { "c": "private", - "t": "storage.modifier", + "t": "storage.modifier.ts", "r": { "dark_plus": "storage.modifier: #569CD6", "light_plus": "storage.modifier: #0000FF", @@ -869,7 +869,7 @@ }, { "c": "gridSize", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -883,21 +883,21 @@ }, { "c": ":", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "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": "number", - "t": "support.type support.type.primitive", + "t": "support.type.ts support.type.primitive.ts", "r": { "dark_plus": "support.type: #4EC9B0", "light_plus": "support.type: #267F99", @@ -911,7 +911,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -925,7 +925,7 @@ }, { "c": "private", - "t": "storage.modifier", + "t": "storage.modifier.ts", "r": { "dark_plus": "storage.modifier: #569CD6", "light_plus": "storage.modifier: #0000FF", @@ -939,7 +939,7 @@ }, { "c": "canvasSize", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -953,21 +953,21 @@ }, { "c": ":", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "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": "number", - "t": "support.type support.type.primitive", + "t": "support.type.ts support.type.primitive.ts", "r": { "dark_plus": "support.type: #4EC9B0", "light_plus": "support.type: #267F99", @@ -981,7 +981,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -995,7 +995,7 @@ }, { "c": "private", - "t": "storage.modifier", + "t": "storage.modifier.ts", "r": { "dark_plus": "storage.modifier: #569CD6", "light_plus": "storage.modifier: #0000FF", @@ -1009,7 +1009,7 @@ }, { "c": "lineColor", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -1023,21 +1023,21 @@ }, { "c": ":", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "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": "string", - "t": "support.type support.type.primitive", + "t": "support.type.ts support.type.primitive.ts", "r": { "dark_plus": "support.type: #4EC9B0", "light_plus": "support.type: #267F99", @@ -1051,7 +1051,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1065,7 +1065,7 @@ }, { "c": "private", - "t": "storage.modifier", + "t": "storage.modifier.ts", "r": { "dark_plus": "storage.modifier: #569CD6", "light_plus": "storage.modifier: #0000FF", @@ -1079,7 +1079,7 @@ }, { "c": "liveColor", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -1093,21 +1093,21 @@ }, { "c": ":", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "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": "string", - "t": "support.type support.type.primitive", + "t": "support.type.ts support.type.primitive.ts", "r": { "dark_plus": "support.type: #4EC9B0", "light_plus": "support.type: #267F99", @@ -1121,7 +1121,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1135,7 +1135,7 @@ }, { "c": "private", - "t": "storage.modifier", + "t": "storage.modifier.ts", "r": { "dark_plus": "storage.modifier: #569CD6", "light_plus": "storage.modifier: #0000FF", @@ -1149,7 +1149,7 @@ }, { "c": "deadColor", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -1163,21 +1163,21 @@ }, { "c": ":", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "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": "string", - "t": "support.type support.type.primitive", + "t": "support.type.ts support.type.primitive.ts", "r": { "dark_plus": "support.type: #4EC9B0", "light_plus": "support.type: #267F99", @@ -1191,7 +1191,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1205,7 +1205,7 @@ }, { "c": "private", - "t": "storage.modifier", + "t": "storage.modifier.ts", "r": { "dark_plus": "storage.modifier: #569CD6", "light_plus": "storage.modifier: #0000FF", @@ -1219,7 +1219,7 @@ }, { "c": "initialLifeProbability", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -1233,21 +1233,21 @@ }, { "c": ":", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "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": "number", - "t": "support.type support.type.primitive", + "t": "support.type.ts support.type.primitive.ts", "r": { "dark_plus": "support.type: #4EC9B0", "light_plus": "support.type: #267F99", @@ -1261,7 +1261,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1275,7 +1275,7 @@ }, { "c": "private", - "t": "storage.modifier", + "t": "storage.modifier.ts", "r": { "dark_plus": "storage.modifier: #569CD6", "light_plus": "storage.modifier: #0000FF", @@ -1289,7 +1289,7 @@ }, { "c": "animationRate", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -1303,21 +1303,21 @@ }, { "c": ":", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "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": "number", - "t": "support.type support.type.primitive", + "t": "support.type.ts support.type.primitive.ts", "r": { "dark_plus": "support.type: #4EC9B0", "light_plus": "support.type: #267F99", @@ -1331,7 +1331,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1345,7 +1345,7 @@ }, { "c": "private", - "t": "storage.modifier", + "t": "storage.modifier.ts", "r": { "dark_plus": "storage.modifier: #569CD6", "light_plus": "storage.modifier: #0000FF", @@ -1359,7 +1359,7 @@ }, { "c": "cellSize", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -1373,21 +1373,21 @@ }, { "c": ":", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "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": "number", - "t": "support.type support.type.primitive", + "t": "support.type.ts support.type.primitive.ts", "r": { "dark_plus": "support.type: #4EC9B0", "light_plus": "support.type: #267F99", @@ -1401,7 +1401,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1415,7 +1415,7 @@ }, { "c": "private", - "t": "storage.modifier", + "t": "storage.modifier.ts", "r": { "dark_plus": "storage.modifier: #569CD6", "light_plus": "storage.modifier: #0000FF", @@ -1429,7 +1429,7 @@ }, { "c": "world", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -1443,7 +1443,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1457,7 +1457,7 @@ }, { "c": "constructor", - "t": "variable meta.definition.method storage.type", + "t": "variable.ts meta.definition.method.ts storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -1471,7 +1471,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1485,7 +1485,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1499,7 +1499,7 @@ }, { "c": "{", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1513,7 +1513,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -1527,7 +1527,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1541,7 +1541,7 @@ }, { "c": "gridSize", - "t": "variable variable", + "t": "variable.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -1555,7 +1555,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1569,7 +1569,7 @@ }, { "c": "50", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -1583,7 +1583,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1597,7 +1597,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -1611,7 +1611,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1625,7 +1625,7 @@ }, { "c": "canvasSize", - "t": "variable variable", + "t": "variable.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -1639,7 +1639,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1653,7 +1653,7 @@ }, { "c": "600", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -1667,7 +1667,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1681,7 +1681,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -1695,7 +1695,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1709,7 +1709,7 @@ }, { "c": "lineColor", - "t": "variable variable", + "t": "variable.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -1723,7 +1723,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1737,7 +1737,7 @@ }, { "c": "'", - "t": "string", + "t": "string.quoted.single.ts punctuation.definition.string.begin.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1751,7 +1751,7 @@ }, { "c": "#cdcdcd", - "t": "string", + "t": "string.quoted.single.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1765,7 +1765,7 @@ }, { "c": "'", - "t": "string", + "t": "string.quoted.single.ts punctuation.definition.string.end.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1779,7 +1779,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1793,7 +1793,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -1807,7 +1807,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1821,7 +1821,7 @@ }, { "c": "liveColor", - "t": "variable variable", + "t": "variable.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -1835,7 +1835,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1849,7 +1849,7 @@ }, { "c": "'", - "t": "string", + "t": "string.quoted.single.ts punctuation.definition.string.begin.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1863,7 +1863,7 @@ }, { "c": "#666", - "t": "string", + "t": "string.quoted.single.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1877,7 +1877,7 @@ }, { "c": "'", - "t": "string", + "t": "string.quoted.single.ts punctuation.definition.string.end.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1891,7 +1891,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1905,7 +1905,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -1919,7 +1919,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1933,7 +1933,7 @@ }, { "c": "deadColor", - "t": "variable variable", + "t": "variable.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -1947,7 +1947,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1961,7 +1961,7 @@ }, { "c": "'", - "t": "string", + "t": "string.quoted.single.ts punctuation.definition.string.begin.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1975,7 +1975,7 @@ }, { "c": "#eee", - "t": "string", + "t": "string.quoted.single.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1989,7 +1989,7 @@ }, { "c": "'", - "t": "string", + "t": "string.quoted.single.ts punctuation.definition.string.end.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -2003,7 +2003,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2017,7 +2017,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -2031,7 +2031,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2045,7 +2045,7 @@ }, { "c": "initialLifeProbability", - "t": "variable variable", + "t": "variable.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -2059,7 +2059,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -2073,7 +2073,7 @@ }, { "c": "0.5", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -2087,7 +2087,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2101,7 +2101,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -2115,7 +2115,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2129,7 +2129,7 @@ }, { "c": "animationRate", - "t": "variable variable", + "t": "variable.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -2143,7 +2143,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -2157,7 +2157,7 @@ }, { "c": "60", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -2171,7 +2171,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2185,7 +2185,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -2199,7 +2199,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2213,7 +2213,7 @@ }, { "c": "cellSize", - "t": "variable variable", + "t": "variable.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -2227,7 +2227,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -2241,7 +2241,7 @@ }, { "c": "0", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -2255,7 +2255,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2269,7 +2269,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -2283,7 +2283,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2297,7 +2297,7 @@ }, { "c": "world", - "t": "variable variable", + "t": "variable.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -2311,7 +2311,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -2325,7 +2325,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -2339,7 +2339,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2353,7 +2353,7 @@ }, { "c": "createWorld", - "t": "variable variable entity.name.function", + "t": "variable.ts variable.ts entity.name.function.ts", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -2367,7 +2367,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2381,7 +2381,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2395,7 +2395,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2409,7 +2409,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -2423,7 +2423,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2437,7 +2437,7 @@ }, { "c": "circleOfLife", - "t": "variable variable entity.name.function", + "t": "variable.ts variable.ts entity.name.function.ts", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -2451,7 +2451,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2465,7 +2465,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2479,7 +2479,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2493,7 +2493,7 @@ }, { "c": "}", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2507,7 +2507,7 @@ }, { "c": "public", - "t": "storage.modifier", + "t": "storage.modifier.ts", "r": { "dark_plus": "storage.modifier: #569CD6", "light_plus": "storage.modifier: #0000FF", @@ -2521,7 +2521,7 @@ }, { "c": "createWorld", - "t": "variable meta.definition.method entity.name.function", + "t": "variable.ts meta.definition.method.ts entity.name.function.ts", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -2535,7 +2535,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2549,7 +2549,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2563,7 +2563,7 @@ }, { "c": "{", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2577,7 +2577,7 @@ }, { "c": "return", - "t": "keyword.control", + "t": "keyword.control.ts", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -2591,7 +2591,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -2605,7 +2605,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2619,7 +2619,7 @@ }, { "c": "travelWorld", - "t": "variable variable entity.name.function", + "t": "variable.ts variable.ts entity.name.function.ts", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -2633,7 +2633,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2647,7 +2647,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2661,7 +2661,7 @@ }, { "c": "cell", - "t": "variable variable.parameter", + "t": "variable.ts variable.parameter.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -2675,21 +2675,21 @@ }, { "c": ":", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "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": "Cell", - "t": "entity.name.type", + "t": "entity.name.type.ts meta.type.annotation.ts entity.name.type.ts", "r": { "dark_plus": "entity.name.type: #4EC9B0", "light_plus": "entity.name.type: #267F99", @@ -2703,7 +2703,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2717,7 +2717,7 @@ }, { "c": "=>", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -2731,7 +2731,7 @@ }, { "c": "{", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2745,7 +2745,7 @@ }, { "c": "cell", - "t": "variable", + "t": "variable.ts variable.other.object.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -2759,7 +2759,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2773,7 +2773,7 @@ }, { "c": "live", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -2787,7 +2787,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -2801,7 +2801,7 @@ }, { "c": "Math", - "t": "variable", + "t": "variable.ts variable.other.object.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -2815,7 +2815,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2829,7 +2829,7 @@ }, { "c": "random", - "t": "variable entity.name.function", + "t": "variable.ts entity.name.function.ts", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -2843,7 +2843,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2857,7 +2857,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2871,7 +2871,7 @@ }, { "c": "<", - "t": "keyword.operator.relational", + "t": "keyword.operator.relational.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -2885,7 +2885,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -2899,7 +2899,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2913,7 +2913,7 @@ }, { "c": "initialLifeProbability", - "t": "variable variable", + "t": "variable.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -2927,7 +2927,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2941,7 +2941,7 @@ }, { "c": "return", - "t": "keyword.control", + "t": "keyword.control.ts", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -2955,7 +2955,7 @@ }, { "c": "cell", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -2969,7 +2969,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2983,7 +2983,7 @@ }, { "c": "}", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2997,7 +2997,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -3011,7 +3011,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -3025,7 +3025,7 @@ }, { "c": "}", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -3039,7 +3039,7 @@ }, { "c": "public", - "t": "storage.modifier", + "t": "storage.modifier.ts", "r": { "dark_plus": "storage.modifier: #569CD6", "light_plus": "storage.modifier: #0000FF", @@ -3053,7 +3053,7 @@ }, { "c": "circleOfLife", - "t": "variable meta.definition.method entity.name.function", + "t": "variable.ts meta.definition.method.ts entity.name.function.ts", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -3067,7 +3067,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -3081,7 +3081,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -3095,21 +3095,21 @@ }, { "c": ":", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "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": "void", - "t": "support.type support.type.primitive", + "t": "support.type.ts support.type.primitive.ts", "r": { "dark_plus": "support.type: #4EC9B0", "light_plus": "support.type: #267F99", @@ -3123,7 +3123,7 @@ }, { "c": "{", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -3137,7 +3137,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -3151,7 +3151,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -3165,7 +3165,7 @@ }, { "c": "world", - "t": "variable variable", + "t": "variable.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -3179,7 +3179,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -3193,7 +3193,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -3207,7 +3207,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -3221,7 +3221,7 @@ }, { "c": "travelWorld", - "t": "variable variable entity.name.function", + "t": "variable.ts variable.ts entity.name.function.ts", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -3235,7 +3235,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -3249,7 +3249,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -3263,7 +3263,7 @@ }, { "c": "cell", - "t": "variable variable.parameter", + "t": "variable.ts variable.parameter.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -3277,21 +3277,21 @@ }, { "c": ":", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "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": "Cell", - "t": "entity.name.type", + "t": "entity.name.type.ts meta.type.annotation.ts entity.name.type.ts", "r": { "dark_plus": "entity.name.type: #4EC9B0", "light_plus": "entity.name.type: #267F99", @@ -3305,7 +3305,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -3319,7 +3319,7 @@ }, { "c": "=>", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -3333,7 +3333,7 @@ }, { "c": "{", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -3347,7 +3347,7 @@ }, { "c": "cell", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -3361,7 +3361,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -3375,7 +3375,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -3389,7 +3389,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -3403,7 +3403,7 @@ }, { "c": "world", - "t": "variable variable", + "t": "variable.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -3417,7 +3417,7 @@ }, { "c": "[", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -3431,7 +3431,7 @@ }, { "c": "cell", - "t": "variable", + "t": "variable.ts variable.other.object.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -3445,7 +3445,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -3459,7 +3459,7 @@ }, { "c": "row", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -3473,7 +3473,7 @@ }, { "c": "]", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -3487,7 +3487,7 @@ }, { "c": "[", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -3501,7 +3501,7 @@ }, { "c": "cell", - "t": "variable", + "t": "variable.ts variable.other.object.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -3515,7 +3515,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -3529,7 +3529,7 @@ }, { "c": "col", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -3543,7 +3543,7 @@ }, { "c": "]", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -3557,7 +3557,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -3571,7 +3571,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -3585,7 +3585,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -3599,7 +3599,7 @@ }, { "c": "draw", - "t": "variable variable entity.name.function", + "t": "variable.ts variable.ts entity.name.function.ts", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -3613,7 +3613,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -3627,7 +3627,7 @@ }, { "c": "cell", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -3641,7 +3641,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -3655,7 +3655,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -3669,7 +3669,7 @@ }, { "c": "return", - "t": "keyword.control", + "t": "keyword.control.ts", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -3683,7 +3683,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -3697,7 +3697,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -3711,7 +3711,7 @@ }, { "c": "resolveNextGeneration", - "t": "variable variable entity.name.function", + "t": "variable.ts variable.ts entity.name.function.ts", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -3725,7 +3725,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -3739,7 +3739,7 @@ }, { "c": "cell", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -3753,7 +3753,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -3767,7 +3767,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -3781,7 +3781,7 @@ }, { "c": "}", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -3795,7 +3795,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -3809,7 +3809,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -3823,7 +3823,7 @@ }, { "c": "setTimeout", - "t": "variable entity.name.function", + "t": "variable.ts entity.name.function.ts", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -3837,7 +3837,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -3851,7 +3851,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -3865,7 +3865,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -3879,7 +3879,7 @@ }, { "c": "=>", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -3893,7 +3893,7 @@ }, { "c": "{", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -3907,7 +3907,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -3921,7 +3921,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -3935,7 +3935,7 @@ }, { "c": "circleOfLife", - "t": "variable variable entity.name.function", + "t": "variable.ts variable.ts entity.name.function.ts", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -3949,7 +3949,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -3963,7 +3963,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -3977,7 +3977,7 @@ }, { "c": "}", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -3991,7 +3991,7 @@ }, { "c": ",", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -4005,7 +4005,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -4019,7 +4019,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -4033,7 +4033,7 @@ }, { "c": "animationRate", - "t": "variable variable", + "t": "variable.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -4047,7 +4047,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -4061,7 +4061,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -4075,7 +4075,7 @@ }, { "c": "}", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -4089,7 +4089,7 @@ }, { "c": "public", - "t": "storage.modifier", + "t": "storage.modifier.ts", "r": { "dark_plus": "storage.modifier: #569CD6", "light_plus": "storage.modifier: #0000FF", @@ -4103,7 +4103,7 @@ }, { "c": "resolveNextGeneration", - "t": "variable meta.definition.method entity.name.function", + "t": "variable.ts meta.definition.method.ts entity.name.function.ts", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -4117,7 +4117,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -4131,7 +4131,7 @@ }, { "c": "cell", - "t": "variable variable.parameter", + "t": "variable.ts variable.parameter.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -4145,21 +4145,21 @@ }, { "c": ":", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "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": "Cell", - "t": "entity.name.type", + "t": "entity.name.type.ts meta.type.annotation.ts entity.name.type.ts", "r": { "dark_plus": "entity.name.type: #4EC9B0", "light_plus": "entity.name.type: #267F99", @@ -4173,7 +4173,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -4187,7 +4187,7 @@ }, { "c": "{", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -4201,7 +4201,7 @@ }, { "c": "var", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -4215,7 +4215,7 @@ }, { "c": "count", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -4229,7 +4229,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -4243,7 +4243,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -4257,7 +4257,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -4271,7 +4271,7 @@ }, { "c": "countNeighbors", - "t": "variable variable entity.name.function", + "t": "variable.ts variable.ts entity.name.function.ts", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -4285,7 +4285,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -4299,7 +4299,7 @@ }, { "c": "cell", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -4313,7 +4313,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -4327,7 +4327,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -4341,7 +4341,7 @@ }, { "c": "var", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -4355,7 +4355,7 @@ }, { "c": "newCell", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -4369,7 +4369,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -4383,7 +4383,7 @@ }, { "c": "new", - "t": "new.expr keyword.operator.new", + "t": "new.expr.ts keyword.operator.new.ts", "r": { "dark_plus": "keyword.operator.new: #569CD6", "light_plus": "keyword.operator.new: #0000FF", @@ -4397,7 +4397,7 @@ }, { "c": "Cell", - "t": "new.expr variable entity.name.function", + "t": "new.expr.ts variable.ts entity.name.function.ts", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -4411,7 +4411,7 @@ }, { "c": "(", - "t": "new.expr punctuation", + "t": "new.expr.ts punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -4425,7 +4425,7 @@ }, { "c": "cell", - "t": "new.expr variable", + "t": "new.expr.ts variable.ts variable.other.object.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -4439,7 +4439,7 @@ }, { "c": ".", - "t": "new.expr punctuation.delimiter", + "t": "new.expr.ts punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -4453,7 +4453,7 @@ }, { "c": "row", - "t": "new.expr variable", + "t": "new.expr.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -4467,7 +4467,7 @@ }, { "c": ",", - "t": "new.expr punctuation.delimiter", + "t": "new.expr.ts punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -4481,7 +4481,7 @@ }, { "c": "cell", - "t": "new.expr variable", + "t": "new.expr.ts variable.ts variable.other.object.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -4495,7 +4495,7 @@ }, { "c": ".", - "t": "new.expr punctuation.delimiter", + "t": "new.expr.ts punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -4509,7 +4509,7 @@ }, { "c": "col", - "t": "new.expr variable", + "t": "new.expr.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -4523,7 +4523,7 @@ }, { "c": ",", - "t": "new.expr punctuation.delimiter", + "t": "new.expr.ts punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -4537,7 +4537,7 @@ }, { "c": "cell", - "t": "new.expr variable", + "t": "new.expr.ts variable.ts variable.other.object.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -4551,7 +4551,7 @@ }, { "c": ".", - "t": "new.expr punctuation.delimiter", + "t": "new.expr.ts punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -4565,7 +4565,7 @@ }, { "c": "live", - "t": "new.expr variable", + "t": "new.expr.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -4579,7 +4579,7 @@ }, { "c": ")", - "t": "new.expr punctuation", + "t": "new.expr.ts punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -4593,7 +4593,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -4607,7 +4607,7 @@ }, { "c": "if", - "t": "keyword.control", + "t": "keyword.control.ts", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -4621,7 +4621,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -4635,7 +4635,7 @@ }, { "c": "count", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -4649,7 +4649,7 @@ }, { "c": "<", - "t": "keyword.operator.relational", + "t": "keyword.operator.relational.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -4663,7 +4663,7 @@ }, { "c": "2", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -4677,7 +4677,7 @@ }, { "c": "||", - "t": "keyword.operator.logical", + "t": "keyword.operator.logical.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -4691,7 +4691,7 @@ }, { "c": "count", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -4705,7 +4705,7 @@ }, { "c": ">", - "t": "keyword.operator.relational", + "t": "keyword.operator.relational.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -4719,7 +4719,7 @@ }, { "c": "3", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -4733,7 +4733,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -4747,7 +4747,7 @@ }, { "c": "newCell", - "t": "variable", + "t": "variable.ts variable.other.object.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -4761,7 +4761,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -4775,7 +4775,7 @@ }, { "c": "live", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -4789,7 +4789,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -4803,7 +4803,7 @@ }, { "c": "false", - "t": "constant.language.boolean.false", + "t": "constant.language.boolean.false.ts", "r": { "dark_plus": "constant.language: #569CD6", "light_plus": "constant.language: #0000FF", @@ -4817,7 +4817,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -4831,7 +4831,7 @@ }, { "c": "else", - "t": "keyword.control", + "t": "keyword.control.ts", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -4845,7 +4845,7 @@ }, { "c": "if", - "t": "keyword.control", + "t": "keyword.control.ts", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -4859,7 +4859,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -4873,7 +4873,7 @@ }, { "c": "count", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -4887,7 +4887,7 @@ }, { "c": "==", - "t": "keyword.operator", + "t": "keyword.operator.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -4901,7 +4901,7 @@ }, { "c": "3", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -4915,7 +4915,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -4929,7 +4929,7 @@ }, { "c": "newCell", - "t": "variable", + "t": "variable.ts variable.other.object.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -4943,7 +4943,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -4957,7 +4957,7 @@ }, { "c": "live", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -4971,7 +4971,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -4985,7 +4985,7 @@ }, { "c": "true", - "t": "constant.language.boolean.true", + "t": "constant.language.boolean.true.ts", "r": { "dark_plus": "constant.language: #569CD6", "light_plus": "constant.language: #0000FF", @@ -4999,7 +4999,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -5013,7 +5013,7 @@ }, { "c": "return", - "t": "keyword.control", + "t": "keyword.control.ts", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -5027,7 +5027,7 @@ }, { "c": "newCell", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -5041,7 +5041,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -5055,7 +5055,7 @@ }, { "c": "}", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -5069,7 +5069,7 @@ }, { "c": "public", - "t": "storage.modifier", + "t": "storage.modifier.ts", "r": { "dark_plus": "storage.modifier: #569CD6", "light_plus": "storage.modifier: #0000FF", @@ -5083,7 +5083,7 @@ }, { "c": "countNeighbors", - "t": "variable meta.definition.method entity.name.function", + "t": "variable.ts meta.definition.method.ts entity.name.function.ts", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -5097,7 +5097,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -5111,7 +5111,7 @@ }, { "c": "cell", - "t": "variable variable.parameter", + "t": "variable.ts variable.parameter.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -5125,21 +5125,21 @@ }, { "c": ":", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "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": "Cell", - "t": "entity.name.type", + "t": "entity.name.type.ts meta.type.annotation.ts entity.name.type.ts", "r": { "dark_plus": "entity.name.type: #4EC9B0", "light_plus": "entity.name.type: #267F99", @@ -5153,7 +5153,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -5167,7 +5167,7 @@ }, { "c": "{", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -5181,7 +5181,7 @@ }, { "c": "var", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -5195,7 +5195,7 @@ }, { "c": "neighbors", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -5209,7 +5209,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -5223,7 +5223,7 @@ }, { "c": "0", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -5237,7 +5237,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -5251,7 +5251,7 @@ }, { "c": "for", - "t": "keyword.control", + "t": "keyword.control.ts", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -5265,7 +5265,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -5279,7 +5279,7 @@ }, { "c": "var", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -5293,7 +5293,7 @@ }, { "c": "row", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -5307,7 +5307,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -5321,21 +5321,21 @@ }, { "c": "-", - "t": "", + "t": "keyword.operator.arithmetic.ts", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "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": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -5349,7 +5349,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -5363,7 +5363,7 @@ }, { "c": "row", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -5377,7 +5377,7 @@ }, { "c": "<=", - "t": "keyword.operator.relational", + "t": "keyword.operator.relational.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -5391,7 +5391,7 @@ }, { "c": "1", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -5405,7 +5405,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -5419,7 +5419,7 @@ }, { "c": "row", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -5433,7 +5433,7 @@ }, { "c": "++", - "t": "keyword.operator.increment", + "t": "keyword.operator.increment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -5447,7 +5447,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -5461,7 +5461,7 @@ }, { "c": "{", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -5475,7 +5475,7 @@ }, { "c": "for", - "t": "keyword.control", + "t": "keyword.control.ts", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -5489,7 +5489,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -5503,7 +5503,7 @@ }, { "c": "var", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -5517,7 +5517,7 @@ }, { "c": "col", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -5531,7 +5531,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -5545,21 +5545,21 @@ }, { "c": "-", - "t": "", + "t": "keyword.operator.arithmetic.ts", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "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": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -5573,7 +5573,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -5587,7 +5587,7 @@ }, { "c": "col", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -5601,7 +5601,7 @@ }, { "c": "<=", - "t": "keyword.operator.relational", + "t": "keyword.operator.relational.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -5615,7 +5615,7 @@ }, { "c": "1", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -5629,7 +5629,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -5643,7 +5643,7 @@ }, { "c": "col", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -5657,7 +5657,7 @@ }, { "c": "++", - "t": "keyword.operator.increment", + "t": "keyword.operator.increment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -5671,7 +5671,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -5685,7 +5685,7 @@ }, { "c": "{", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -5699,7 +5699,7 @@ }, { "c": "if", - "t": "keyword.control", + "t": "keyword.control.ts", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -5713,7 +5713,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -5727,7 +5727,7 @@ }, { "c": "row", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -5741,7 +5741,7 @@ }, { "c": "==", - "t": "keyword.operator", + "t": "keyword.operator.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -5755,7 +5755,7 @@ }, { "c": "0", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -5769,7 +5769,7 @@ }, { "c": "&&", - "t": "keyword.operator.logical", + "t": "keyword.operator.logical.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -5783,7 +5783,7 @@ }, { "c": "col", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -5797,7 +5797,7 @@ }, { "c": "==", - "t": "keyword.operator", + "t": "keyword.operator.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -5811,7 +5811,7 @@ }, { "c": "0", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -5825,7 +5825,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -5839,7 +5839,7 @@ }, { "c": "continue", - "t": "keyword.control", + "t": "keyword.control.ts", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -5853,7 +5853,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -5867,7 +5867,7 @@ }, { "c": "if", - "t": "keyword.control", + "t": "keyword.control.ts", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -5881,7 +5881,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -5895,7 +5895,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -5909,7 +5909,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -5923,7 +5923,7 @@ }, { "c": "isAlive", - "t": "variable variable entity.name.function", + "t": "variable.ts variable.ts entity.name.function.ts", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -5937,7 +5937,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -5951,7 +5951,7 @@ }, { "c": "cell", - "t": "variable", + "t": "variable.ts variable.other.object.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -5965,7 +5965,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -5979,7 +5979,7 @@ }, { "c": "row", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -5993,7 +5993,7 @@ }, { "c": "+", - "t": "keyword.operator.arithmetic", + "t": "keyword.operator.arithmetic.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -6007,7 +6007,7 @@ }, { "c": "row", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -6021,7 +6021,7 @@ }, { "c": ",", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -6035,7 +6035,7 @@ }, { "c": "cell", - "t": "variable", + "t": "variable.ts variable.other.object.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -6049,7 +6049,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -6063,7 +6063,7 @@ }, { "c": "col", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -6077,7 +6077,7 @@ }, { "c": "+", - "t": "keyword.operator.arithmetic", + "t": "keyword.operator.arithmetic.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -6091,7 +6091,7 @@ }, { "c": "col", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -6105,7 +6105,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -6119,7 +6119,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -6133,7 +6133,7 @@ }, { "c": "{", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -6147,7 +6147,7 @@ }, { "c": "neighbors", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -6161,7 +6161,7 @@ }, { "c": "++", - "t": "keyword.operator.increment", + "t": "keyword.operator.increment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -6175,7 +6175,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -6189,7 +6189,7 @@ }, { "c": "}", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -6203,7 +6203,7 @@ }, { "c": "}", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -6217,7 +6217,7 @@ }, { "c": "}", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -6231,7 +6231,7 @@ }, { "c": "return", - "t": "keyword.control", + "t": "keyword.control.ts", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -6245,7 +6245,7 @@ }, { "c": "neighbors", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -6259,7 +6259,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -6273,7 +6273,7 @@ }, { "c": "}", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -6287,7 +6287,7 @@ }, { "c": "public", - "t": "storage.modifier", + "t": "storage.modifier.ts", "r": { "dark_plus": "storage.modifier: #569CD6", "light_plus": "storage.modifier: #0000FF", @@ -6301,7 +6301,7 @@ }, { "c": "isAlive", - "t": "variable meta.definition.method entity.name.function", + "t": "variable.ts meta.definition.method.ts entity.name.function.ts", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -6315,7 +6315,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -6329,7 +6329,7 @@ }, { "c": "row", - "t": "variable variable.parameter", + "t": "variable.ts variable.parameter.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -6343,21 +6343,21 @@ }, { "c": ":", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "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": "number", - "t": "support.type support.type.primitive", + "t": "support.type.ts support.type.primitive.ts", "r": { "dark_plus": "support.type: #4EC9B0", "light_plus": "support.type: #267F99", @@ -6371,7 +6371,7 @@ }, { "c": ",", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -6385,7 +6385,7 @@ }, { "c": "col", - "t": "variable variable.parameter", + "t": "variable.ts variable.parameter.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -6399,21 +6399,21 @@ }, { "c": ":", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "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": "number", - "t": "support.type support.type.primitive", + "t": "support.type.ts support.type.primitive.ts", "r": { "dark_plus": "support.type: #4EC9B0", "light_plus": "support.type: #267F99", @@ -6427,7 +6427,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -6441,7 +6441,7 @@ }, { "c": "{", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -6455,7 +6455,7 @@ }, { "c": "if", - "t": "keyword.control", + "t": "keyword.control.ts", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -6469,7 +6469,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -6483,7 +6483,7 @@ }, { "c": "row", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -6497,7 +6497,7 @@ }, { "c": "<", - "t": "keyword.operator.relational", + "t": "keyword.operator.relational.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -6511,7 +6511,7 @@ }, { "c": "0", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -6525,7 +6525,7 @@ }, { "c": "||", - "t": "keyword.operator.logical", + "t": "keyword.operator.logical.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -6539,7 +6539,7 @@ }, { "c": "col", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -6553,7 +6553,7 @@ }, { "c": "<", - "t": "keyword.operator.relational", + "t": "keyword.operator.relational.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -6567,7 +6567,7 @@ }, { "c": "0", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -6581,7 +6581,7 @@ }, { "c": "||", - "t": "keyword.operator.logical", + "t": "keyword.operator.logical.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -6595,7 +6595,7 @@ }, { "c": "row", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -6609,7 +6609,7 @@ }, { "c": ">=", - "t": "keyword.operator.relational", + "t": "keyword.operator.relational.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -6623,7 +6623,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -6637,7 +6637,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -6651,7 +6651,7 @@ }, { "c": "gridSize", - "t": "variable variable", + "t": "variable.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -6665,7 +6665,7 @@ }, { "c": "||", - "t": "keyword.operator.logical", + "t": "keyword.operator.logical.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -6679,7 +6679,7 @@ }, { "c": "col", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -6693,7 +6693,7 @@ }, { "c": ">=", - "t": "keyword.operator.relational", + "t": "keyword.operator.relational.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -6707,7 +6707,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -6721,7 +6721,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -6735,7 +6735,7 @@ }, { "c": "gridSize", - "t": "variable variable", + "t": "variable.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -6749,7 +6749,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -6763,7 +6763,7 @@ }, { "c": "return", - "t": "keyword.control", + "t": "keyword.control.ts", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -6777,7 +6777,7 @@ }, { "c": "false", - "t": "constant.language.boolean.false", + "t": "constant.language.boolean.false.ts", "r": { "dark_plus": "constant.language: #569CD6", "light_plus": "constant.language: #0000FF", @@ -6791,7 +6791,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -6805,7 +6805,7 @@ }, { "c": "return", - "t": "keyword.control", + "t": "keyword.control.ts", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -6819,7 +6819,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -6833,7 +6833,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -6847,7 +6847,7 @@ }, { "c": "world", - "t": "variable variable", + "t": "variable.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -6861,7 +6861,7 @@ }, { "c": "[", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -6875,7 +6875,7 @@ }, { "c": "row", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -6889,7 +6889,7 @@ }, { "c": "]", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -6903,7 +6903,7 @@ }, { "c": "[", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -6917,7 +6917,7 @@ }, { "c": "col", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -6931,7 +6931,7 @@ }, { "c": "]", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -6945,7 +6945,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -6959,7 +6959,7 @@ }, { "c": "live", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -6973,7 +6973,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -6987,7 +6987,7 @@ }, { "c": "}", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -7001,7 +7001,7 @@ }, { "c": "public", - "t": "storage.modifier", + "t": "storage.modifier.ts", "r": { "dark_plus": "storage.modifier: #569CD6", "light_plus": "storage.modifier: #0000FF", @@ -7015,7 +7015,7 @@ }, { "c": "travelWorld", - "t": "variable meta.definition.method entity.name.function", + "t": "variable.ts meta.definition.method.ts entity.name.function.ts", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -7029,7 +7029,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -7043,7 +7043,7 @@ }, { "c": "callback", - "t": "variable variable.parameter", + "t": "variable.ts variable.parameter.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -7057,7 +7057,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -7071,7 +7071,7 @@ }, { "c": "{", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -7085,7 +7085,7 @@ }, { "c": "var", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -7099,7 +7099,7 @@ }, { "c": "result", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -7113,7 +7113,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -7127,7 +7127,7 @@ }, { "c": "[", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -7141,7 +7141,7 @@ }, { "c": "]", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -7155,7 +7155,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -7169,7 +7169,7 @@ }, { "c": "for", - "t": "keyword.control", + "t": "keyword.control.ts", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -7183,7 +7183,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -7197,7 +7197,7 @@ }, { "c": "var", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -7211,7 +7211,7 @@ }, { "c": "row", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -7225,7 +7225,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -7239,7 +7239,7 @@ }, { "c": "0", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -7253,7 +7253,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -7267,7 +7267,7 @@ }, { "c": "row", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -7281,7 +7281,7 @@ }, { "c": "<", - "t": "keyword.operator.relational", + "t": "keyword.operator.relational.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -7295,7 +7295,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -7309,7 +7309,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -7323,7 +7323,7 @@ }, { "c": "gridSize", - "t": "variable variable", + "t": "variable.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -7337,7 +7337,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -7351,7 +7351,7 @@ }, { "c": "row", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -7365,7 +7365,7 @@ }, { "c": "++", - "t": "keyword.operator.increment", + "t": "keyword.operator.increment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -7379,7 +7379,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -7393,7 +7393,7 @@ }, { "c": "{", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -7407,7 +7407,7 @@ }, { "c": "var", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -7421,7 +7421,7 @@ }, { "c": "rowData", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -7435,7 +7435,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -7449,7 +7449,7 @@ }, { "c": "[", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -7463,7 +7463,7 @@ }, { "c": "]", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -7477,7 +7477,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -7491,7 +7491,7 @@ }, { "c": "for", - "t": "keyword.control", + "t": "keyword.control.ts", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -7505,7 +7505,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -7519,7 +7519,7 @@ }, { "c": "var", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -7533,7 +7533,7 @@ }, { "c": "col", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -7547,7 +7547,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -7561,7 +7561,7 @@ }, { "c": "0", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -7575,7 +7575,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -7589,7 +7589,7 @@ }, { "c": "col", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -7603,7 +7603,7 @@ }, { "c": "<", - "t": "keyword.operator.relational", + "t": "keyword.operator.relational.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -7617,7 +7617,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -7631,7 +7631,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -7645,7 +7645,7 @@ }, { "c": "gridSize", - "t": "variable variable", + "t": "variable.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -7659,7 +7659,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -7673,7 +7673,7 @@ }, { "c": "col", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -7687,7 +7687,7 @@ }, { "c": "++", - "t": "keyword.operator.increment", + "t": "keyword.operator.increment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -7701,7 +7701,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -7715,7 +7715,7 @@ }, { "c": "{", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -7729,7 +7729,7 @@ }, { "c": "rowData", - "t": "variable", + "t": "variable.ts variable.other.object.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -7743,7 +7743,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -7757,7 +7757,7 @@ }, { "c": "push", - "t": "variable entity.name.function", + "t": "variable.ts entity.name.function.ts", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -7771,7 +7771,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -7785,7 +7785,7 @@ }, { "c": "callback", - "t": "variable entity.name.function", + "t": "variable.ts entity.name.function.ts", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -7799,7 +7799,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -7813,7 +7813,7 @@ }, { "c": "new", - "t": "new.expr keyword.operator.new", + "t": "new.expr.ts keyword.operator.new.ts", "r": { "dark_plus": "keyword.operator.new: #569CD6", "light_plus": "keyword.operator.new: #0000FF", @@ -7827,7 +7827,7 @@ }, { "c": "Cell", - "t": "new.expr variable entity.name.function", + "t": "new.expr.ts variable.ts entity.name.function.ts", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -7841,7 +7841,7 @@ }, { "c": "(", - "t": "new.expr punctuation", + "t": "new.expr.ts punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -7855,7 +7855,7 @@ }, { "c": "row", - "t": "new.expr variable", + "t": "new.expr.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -7869,7 +7869,7 @@ }, { "c": ",", - "t": "new.expr punctuation.delimiter", + "t": "new.expr.ts punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -7883,7 +7883,7 @@ }, { "c": "col", - "t": "new.expr variable", + "t": "new.expr.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -7897,7 +7897,7 @@ }, { "c": ",", - "t": "new.expr punctuation.delimiter", + "t": "new.expr.ts punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -7911,7 +7911,7 @@ }, { "c": "false", - "t": "new.expr constant.language.boolean.false", + "t": "new.expr.ts constant.language.boolean.false.ts", "r": { "dark_plus": "constant.language: #569CD6", "light_plus": "constant.language: #0000FF", @@ -7925,7 +7925,7 @@ }, { "c": ")", - "t": "new.expr punctuation", + "t": "new.expr.ts punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -7939,7 +7939,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -7953,7 +7953,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -7967,7 +7967,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -7981,7 +7981,7 @@ }, { "c": "}", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -7995,7 +7995,7 @@ }, { "c": "result", - "t": "variable", + "t": "variable.ts variable.other.object.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -8009,7 +8009,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -8023,7 +8023,7 @@ }, { "c": "push", - "t": "variable entity.name.function", + "t": "variable.ts entity.name.function.ts", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -8037,7 +8037,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -8051,7 +8051,7 @@ }, { "c": "rowData", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -8065,7 +8065,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -8079,7 +8079,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -8093,7 +8093,7 @@ }, { "c": "}", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -8107,7 +8107,7 @@ }, { "c": "return", - "t": "keyword.control", + "t": "keyword.control.ts", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -8121,7 +8121,7 @@ }, { "c": "result", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -8135,7 +8135,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -8149,7 +8149,7 @@ }, { "c": "}", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -8163,7 +8163,7 @@ }, { "c": "public", - "t": "storage.modifier", + "t": "storage.modifier.ts", "r": { "dark_plus": "storage.modifier: #569CD6", "light_plus": "storage.modifier: #0000FF", @@ -8177,7 +8177,7 @@ }, { "c": "draw", - "t": "variable meta.definition.method entity.name.function", + "t": "variable.ts meta.definition.method.ts entity.name.function.ts", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -8191,7 +8191,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -8205,7 +8205,7 @@ }, { "c": "cell", - "t": "variable variable.parameter", + "t": "variable.ts variable.parameter.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -8219,21 +8219,21 @@ }, { "c": ":", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { - "dark_plus": "default: #D4D4D4", - "light_plus": "default: #000000", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", - "hc_black": "default: #FFFFFF", - "dark_modern": "default: #CCCCCC", - "hc_light": "default: #292929", - "light_modern": "default: #3B3B3B" + "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": "Cell", - "t": "entity.name.type", + "t": "entity.name.type.ts meta.type.annotation.ts entity.name.type.ts", "r": { "dark_plus": "entity.name.type: #4EC9B0", "light_plus": "entity.name.type: #267F99", @@ -8247,7 +8247,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -8261,7 +8261,7 @@ }, { "c": "{", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -8275,7 +8275,7 @@ }, { "c": "if", - "t": "keyword.control", + "t": "keyword.control.ts", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -8289,7 +8289,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -8303,7 +8303,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -8317,7 +8317,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -8331,7 +8331,7 @@ }, { "c": "cellSize", - "t": "variable variable", + "t": "variable.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -8345,7 +8345,7 @@ }, { "c": "==", - "t": "keyword.operator", + "t": "keyword.operator.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -8359,7 +8359,7 @@ }, { "c": "0", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -8373,7 +8373,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -8387,7 +8387,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -8401,7 +8401,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -8415,7 +8415,7 @@ }, { "c": "cellSize", - "t": "variable variable", + "t": "variable.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -8429,7 +8429,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -8443,7 +8443,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -8457,7 +8457,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -8471,7 +8471,7 @@ }, { "c": "canvasSize", - "t": "variable variable", + "t": "variable.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -8485,7 +8485,7 @@ }, { "c": "/", - "t": "keyword.operator.arithmetic", + "t": "keyword.operator.arithmetic.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -8499,7 +8499,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -8513,7 +8513,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -8527,7 +8527,7 @@ }, { "c": "gridSize", - "t": "variable variable", + "t": "variable.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -8541,7 +8541,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -8555,7 +8555,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -8569,7 +8569,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -8583,7 +8583,7 @@ }, { "c": "context", - "t": "variable variable", + "t": "variable.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -8597,7 +8597,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -8611,7 +8611,7 @@ }, { "c": "strokeStyle", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -8625,7 +8625,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -8639,7 +8639,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -8653,7 +8653,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -8667,7 +8667,7 @@ }, { "c": "lineColor", - "t": "variable variable", + "t": "variable.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -8681,7 +8681,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -8695,7 +8695,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -8709,7 +8709,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -8723,7 +8723,7 @@ }, { "c": "context", - "t": "variable variable", + "t": "variable.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -8737,7 +8737,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -8751,7 +8751,7 @@ }, { "c": "strokeRect", - "t": "variable entity.name.function", + "t": "variable.ts entity.name.function.ts", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -8765,7 +8765,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -8779,7 +8779,7 @@ }, { "c": "cell", - "t": "variable", + "t": "variable.ts variable.other.object.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -8793,7 +8793,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -8807,7 +8807,7 @@ }, { "c": "row", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -8821,7 +8821,7 @@ }, { "c": "*", - "t": "keyword.operator.arithmetic", + "t": "keyword.operator.arithmetic.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -8835,7 +8835,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -8849,7 +8849,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -8863,7 +8863,7 @@ }, { "c": "cellSize", - "t": "variable variable", + "t": "variable.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -8877,7 +8877,7 @@ }, { "c": ",", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -8891,7 +8891,7 @@ }, { "c": "cell", - "t": "variable", + "t": "variable.ts variable.other.object.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -8905,7 +8905,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -8919,7 +8919,7 @@ }, { "c": "col", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -8933,7 +8933,7 @@ }, { "c": "*", - "t": "keyword.operator.arithmetic", + "t": "keyword.operator.arithmetic.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -8947,7 +8947,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -8961,7 +8961,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -8975,7 +8975,7 @@ }, { "c": "cellSize", - "t": "variable variable", + "t": "variable.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -8989,7 +8989,7 @@ }, { "c": ",", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -9003,7 +9003,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -9017,7 +9017,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -9031,7 +9031,7 @@ }, { "c": "cellSize", - "t": "variable variable", + "t": "variable.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -9045,7 +9045,7 @@ }, { "c": ",", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -9059,7 +9059,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -9073,7 +9073,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -9087,7 +9087,7 @@ }, { "c": "cellSize", - "t": "variable variable", + "t": "variable.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -9101,7 +9101,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -9115,7 +9115,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -9129,7 +9129,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -9143,7 +9143,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -9157,7 +9157,7 @@ }, { "c": "context", - "t": "variable variable", + "t": "variable.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -9171,7 +9171,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -9185,7 +9185,7 @@ }, { "c": "fillStyle", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -9199,7 +9199,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -9213,7 +9213,7 @@ }, { "c": "cell", - "t": "variable", + "t": "variable.ts variable.other.object.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -9227,7 +9227,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -9241,7 +9241,7 @@ }, { "c": "live", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -9255,7 +9255,7 @@ }, { "c": "?", - "t": "punctuation.delimiter keyword.operator.ternary", + "t": "punctuation.delimiter.ts keyword.operator.ternary.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -9269,7 +9269,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -9283,7 +9283,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -9297,7 +9297,7 @@ }, { "c": "liveColor", - "t": "variable variable", + "t": "variable.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -9311,7 +9311,7 @@ }, { "c": ":", - "t": "punctuation.delimiter keyword.operator.ternary", + "t": "punctuation.delimiter.ts keyword.operator.ternary.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -9325,7 +9325,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -9339,7 +9339,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -9353,7 +9353,7 @@ }, { "c": "deadColor", - "t": "variable variable", + "t": "variable.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -9367,7 +9367,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -9381,7 +9381,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -9395,7 +9395,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -9409,7 +9409,7 @@ }, { "c": "context", - "t": "variable variable", + "t": "variable.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -9423,7 +9423,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -9437,7 +9437,7 @@ }, { "c": "fillRect", - "t": "variable entity.name.function", + "t": "variable.ts entity.name.function.ts", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -9451,7 +9451,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -9465,7 +9465,7 @@ }, { "c": "cell", - "t": "variable", + "t": "variable.ts variable.other.object.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -9479,7 +9479,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -9493,7 +9493,7 @@ }, { "c": "row", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -9507,7 +9507,7 @@ }, { "c": "*", - "t": "keyword.operator.arithmetic", + "t": "keyword.operator.arithmetic.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -9521,7 +9521,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -9535,7 +9535,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -9549,7 +9549,7 @@ }, { "c": "cellSize", - "t": "variable variable", + "t": "variable.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -9563,7 +9563,7 @@ }, { "c": ",", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -9577,7 +9577,7 @@ }, { "c": "cell", - "t": "variable", + "t": "variable.ts variable.other.object.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -9591,7 +9591,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -9605,7 +9605,7 @@ }, { "c": "col", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -9619,7 +9619,7 @@ }, { "c": "*", - "t": "keyword.operator.arithmetic", + "t": "keyword.operator.arithmetic.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -9633,7 +9633,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -9647,7 +9647,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -9661,7 +9661,7 @@ }, { "c": "cellSize", - "t": "variable variable", + "t": "variable.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -9675,7 +9675,7 @@ }, { "c": ",", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -9689,7 +9689,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -9703,7 +9703,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -9717,7 +9717,7 @@ }, { "c": "cellSize", - "t": "variable variable", + "t": "variable.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -9731,7 +9731,7 @@ }, { "c": ",", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -9745,7 +9745,7 @@ }, { "c": "this", - "t": "variable.language.this", + "t": "variable.language.this.ts", "r": { "dark_plus": "variable.language: #569CD6", "light_plus": "variable.language: #0000FF", @@ -9759,7 +9759,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -9773,7 +9773,7 @@ }, { "c": "cellSize", - "t": "variable variable", + "t": "variable.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -9787,7 +9787,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -9801,7 +9801,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -9815,7 +9815,7 @@ }, { "c": "}", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -9829,7 +9829,7 @@ }, { "c": "}", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -9843,7 +9843,7 @@ }, { "c": "}", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -9857,7 +9857,7 @@ }, { "c": "var", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -9871,7 +9871,7 @@ }, { "c": "game", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -9885,7 +9885,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -9899,7 +9899,7 @@ }, { "c": "new", - "t": "new.expr keyword.operator.new", + "t": "new.expr.ts keyword.operator.new.ts", "r": { "dark_plus": "keyword.operator.new: #569CD6", "light_plus": "keyword.operator.new: #0000FF", @@ -9913,7 +9913,7 @@ }, { "c": "Conway", - "t": "new.expr variable", + "t": "new.expr.ts variable.ts variable.other.object.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -9927,7 +9927,7 @@ }, { "c": ".", - "t": "new.expr punctuation.delimiter", + "t": "new.expr.ts punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -9941,7 +9941,7 @@ }, { "c": "GameOfLife", - "t": "new.expr variable", + "t": "new.expr.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -9955,7 +9955,7 @@ }, { "c": "(", - "t": "new.expr punctuation", + "t": "new.expr.ts punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -9969,7 +9969,7 @@ }, { "c": ")", - "t": "new.expr punctuation", + "t": "new.expr.ts punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -9983,7 +9983,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", diff --git a/extensions/vscode-test-resolver/package-lock.json b/extensions/vscode-test-resolver/package-lock.json index 367c4dca2e0e..45b8a4309ef9 100644 --- a/extensions/vscode-test-resolver/package-lock.json +++ b/extensions/vscode-test-resolver/package-lock.json @@ -9,26 +9,28 @@ "version": "0.0.1", "license": "MIT", "devDependencies": { - "@types/node": "20.x" + "@types/node": "25.x" }, "engines": { "vscode": "^1.25.0" } }, "node_modules/@types/node": { - "version": "20.11.24", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.11.24.tgz", - "integrity": "sha512-Kza43ewS3xoLgCEpQrsT+xRo/EJej1y0kVYGiLFE1NEODXGzTfwiC6tXTLMQskn1X4/Rjlh0MQUvx9W+L9long==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", + "dev": true, + "license": "MIT" } } } diff --git a/extensions/vscode-test-resolver/package.json b/extensions/vscode-test-resolver/package.json index 8ab2171ddaaa..d70deef0dba6 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": "20.x" + "@types/node": "25.x" }, "capabilities": { "untrustedWorkspaces": { diff --git a/package-lock.json b/package-lock.json index 5b8eca420eba..9040f4fcd65a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,185 +1,173 @@ { "name": "code-oss-dev", - "version": "1.98.0", + "version": "1.99.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "code-oss-dev", - "version": "1.98.0", + "version": "1.99.0", "hasInstallScript": true, "license": "MIT", "dependencies": { + "@c4312/eventsource-umd": "^3.0.5", "@microsoft/1ds-core-js": "^3.2.13", - "@microsoft/1ds-post-js": "^3.2.13", + "@microsoft/1ds-post-js": "^4.3.11", "@parcel/watcher": "2.5.1", "@types/semver": "^7.5.8", "@vscode/deviceid": "^0.1.1", "@vscode/iconv-lite-umd": "0.7.0", - "@vscode/policy-watcher": "^1.1.10", - "@vscode/proxy-agent": "^0.31.0", - "@vscode/ripgrep": "^1.15.10", - "@vscode/spdlog": "^0.15.0", + "@vscode/policy-watcher": "^1.3.0", + "@vscode/proxy-agent": "^0.32.0", + "@vscode/ripgrep": "^1.17.0", + "@vscode/spdlog": "^0.15.7", "@vscode/sqlite3": "5.1.8-vscode", "@vscode/sudo-prompt": "9.3.1", - "@vscode/tree-sitter-wasm": "^0.1.2", - "@vscode/vscode-languagedetection": "1.0.21", - "@vscode/windows-mutex": "^0.5.0", - "@vscode/windows-process-tree": "^0.6.0", + "@vscode/tree-sitter-wasm": "^0.1.3", + "@vscode/vscode-languagedetection": "1.0.23", + "@vscode/windows-mutex": "^0.5.3", + "@vscode/windows-process-tree": "^0.6.3", "@vscode/windows-registry": "^1.1.0", - "@xterm/addon-clipboard": "^0.2.0-beta.81", - "@xterm/addon-image": "^0.9.0-beta.98", - "@xterm/addon-ligatures": "^0.10.0-beta.98", - "@xterm/addon-progress": "^0.2.0-beta.4", - "@xterm/addon-search": "^0.16.0-beta.98", - "@xterm/addon-serialize": "^0.14.0-beta.98", - "@xterm/addon-unicode11": "^0.9.0-beta.98", - "@xterm/addon-webgl": "^0.19.0-beta.98", - "@xterm/headless": "^5.6.0-beta.98", - "@xterm/xterm": "^5.6.0-beta.98", + "@xterm/addon-clipboard": "^0.2.0-beta.82", + "@xterm/addon-image": "^0.9.0-beta.99", + "@xterm/addon-ligatures": "^0.11.0-beta.107", + "@xterm/addon-progress": "^0.2.0-beta.5", + "@xterm/addon-search": "^0.17.0-beta.105", + "@xterm/addon-serialize": "^0.14.0-beta.99", + "@xterm/addon-unicode11": "^0.10.0-beta.107", + "@xterm/addon-webgl": "^0.19.0-beta.99", + "@xterm/headless": "^5.6.0-beta.99", + "@xterm/xterm": "^6.0.0", "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.6", "jschardet": "3.1.4", "kerberos": "2.1.1", "minimist": "^1.2.6", "native-is-elevated": "0.7.0", "native-keymap": "^3.3.5", "native-watchdog": "^1.4.1", - "node-pty": "1.1.0-beta30", - "open": "^8.4.2", + "node-pty": "1.1.0-beta31", + "open": "^11.0.0", "tas-client-umd": "0.2.0", "v8-inspect-profiler": "^0.1.1", - "vscode-oniguruma": "1.7.0", + "vscode-oniguruma": "2.0.1", "vscode-regexpp": "^3.1.0", "vscode-textmate": "9.2.0", "yauzl": "^3.0.0", "yazl": "^2.4.3" }, "devDependencies": { - "@playwright/test": "^1.50.0", - "@stylistic/eslint-plugin-ts": "^2.8.0", - "@types/cookie": "^0.3.3", + "@playwright/test": "^1.56.1", + "@stylistic/eslint-plugin-ts": "^4.4.1", + "@types/cookie": "^1.0.0", "@types/debug": "^4.1.5", "@types/eslint": "^9.6.1", - "@types/gulp-svgmin": "^1.2.1", - "@types/http-proxy-agent": "^2.0.1", + "@types/gulp-svgmin": "^1.2.5", + "@types/http-proxy-agent": "^4.0.1", "@types/kerberos": "^1.1.2", - "@types/minimist": "^1.2.1", - "@types/mocha": "^9.1.1", - "@types/node": "20.x", - "@types/sinon": "^10.0.2", + "@types/minimist": "^1.2.5", + "@types/mocha": "^10.0.10", + "@types/node": "25.x", + "@types/sinon": "^21.0.0", "@types/sinon-test": "^2.4.2", - "@types/trusted-types": "^1.0.6", - "@types/vscode-notebook-renderer": "^1.72.0", + "@types/trusted-types": "^2.0.7", + "@types/vscode-notebook-renderer": "^1.72.4", "@types/webpack": "^5.28.5", "@types/wicg-file-system-access": "^2020.9.6", "@types/windows-foreground-love": "^0.3.0", "@types/winreg": "^1.2.30", "@types/yauzl": "^2.10.0", "@types/yazl": "^2.4.2", - "@typescript-eslint/utils": "^8.8.0", - "@vscode/gulp-electron": "^1.36.0", + "@typescript-eslint/utils": "^8.53.1", + "@vscode/gulp-electron": "^1.38.2", "@vscode/l10n-dev": "0.0.35", - "@vscode/telemetry-extractor": "^1.10.2", - "@vscode/test-cli": "^0.0.6", + "@vscode/telemetry-extractor": "^1.18.0", + "@vscode/test-cli": "^0.0.12", "@vscode/test-electron": "^2.4.0", - "@vscode/test-web": "^0.0.62", + "@vscode/test-web": "^0.0.77", "@vscode/v8-heap-parser": "^0.1.0", - "@vscode/vscode-perf": "^0.0.19", - "@webgpu/types": "^0.1.44", + "@vscode/vscode-perf": "^0.0.25", + "@webgpu/types": "^0.1.69", "ansi-colors": "^3.2.3", "asar": "^3.0.3", "chromium-pickle-js": "^0.2.0", - "cookie": "^0.7.2", - "copy-webpack-plugin": "^11.0.0", - "css-loader": "^6.9.1", + "cookie": "^1.1.1", + "copy-webpack-plugin": "^13.0.1", + "css-loader": "^7.1.2", "cssnano": "^6.0.3", - "debounce": "^1.0.0", - "deemon": "^1.8.0", - "electron": "34.1.0", - "eslint": "^9.11.1", - "eslint-formatter-compact": "^8.40.0", + "debounce": "^3.0.0", + "deemon": "^1.13.6", + "electron": "40.0.0", + "eslint": "^9.39.2", + "eslint-formatter-compact": "^9.0.1", "eslint-plugin-header": "3.1.1", - "eslint-plugin-jsdoc": "^50.3.1", + "eslint-plugin-jsdoc": "^61.5.0", "eslint-plugin-local": "^6.0.0", - "event-stream": "3.3.4", - "fancy-log": "^1.3.3", + "event-stream": "4.0.1", + "fancy-log": "^2.0.0", "file-loader": "^6.2.0", - "glob": "^5.0.13", - "gulp": "^4.0.0", + "glob": "^9.0.1", + "gulp": "^5.0.1", "gulp-azure-storage": "^0.12.1", - "gulp-bom": "^3.0.0", + "gulp-bom": "^5.0.0", "gulp-buffer": "0.0.2", "gulp-filter": "^5.1.0", "gulp-flatmap": "^1.0.2", "gulp-gunzip": "^1.0.0", "gulp-gzip": "^1.4.2", - "gulp-json-editor": "^2.5.0", - "gulp-plumber": "^1.2.0", - "gulp-rename": "^1.2.0", - "gulp-replace": "^0.5.4", + "gulp-json-editor": "^2.6.0", + "gulp-plumber": "^1.2.1", + "gulp-rename": "^2.1.0", + "gulp-replace": "^1.1.4", "gulp-sourcemaps": "^3.0.0", "gulp-svgmin": "^4.1.0", - "gulp-untar": "^0.0.7", - "husky": "^0.13.1", + "gulp-untar": "^0.0.8", + "husky": "^9.1.7", "innosetup": "^6.4.1", - "istanbul-lib-coverage": "^3.2.0", - "istanbul-lib-instrument": "^6.0.1", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-instrument": "^6.0.3", "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.1", - "istanbul-reports": "^3.1.5", - "lazy.js": "^0.4.2", + "istanbul-lib-source-maps": "^5.0.6", + "istanbul-reports": "^3.2.0", + "lazy.js": "^0.5.1", "merge-options": "^1.0.1", "mime": "^1.4.1", - "minimatch": "^3.0.4", + "minimatch": "^10.1.1", "minimist": "^1.2.6", - "mocha": "^10.8.2", + "mocha": "^11.7.5", "mocha-junit-reporter": "^2.2.1", "mocha-multi-reporters": "^1.5.1", "npm-run-all": "^4.1.5", "os-browserify": "^0.3.0", - "p-all": "^1.0.0", + "p-all": "^5.0.1", "path-browserify": "^1.0.1", - "postcss": "^8.4.33", + "postcss": "^8.5.3", "postcss-nesting": "^12.0.2", - "pump": "^1.0.1", - "rcedit": "^1.1.0", - "rimraf": "^2.2.8", - "sinon": "^12.0.1", + "pump": "^3.0.3", + "rcedit": "^5.0.2", + "rimraf": "^6.1.2", + "sinon": "^21.0.0", "sinon-test": "^3.1.3", - "source-map": "0.6.1", + "source-map": "0.7.6", "source-map-support": "^0.3.2", "style-loader": "^3.3.2", - "ts-loader": "^9.5.1", - "ts-node": "^10.9.1", - "tsec": "0.2.7", - "tslib": "^2.6.3", - "typescript": "^5.8.0-dev.20250207", + "ts-loader": "^9.5.4", + "ts-node": "^10.9.2", + "tsec": "0.2.9", + "tslib": "^2.8.1", + "typescript": "^5.9.3", "typescript-eslint": "^8.8.0", "util": "^0.12.4", "webpack": "^5.94.0", - "webpack-cli": "^5.1.4", + "webpack-cli": "^6.0.1", "webpack-stream": "^7.0.0", - "xml2js": "^0.5.0", + "xml2js": "^0.6.2", "yaserver": "^0.4.0" }, "optionalDependencies": { "windows-foreground-love": "0.5.0" } }, - "node_modules/@ampproject/remapping": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.2.0.tgz", - "integrity": "sha512-qRmjj8nj9qmLTQXXmaR1cck3UXSRMPrbsLJAasZpF+t3riI71BXed5ebIOYwQntykeZuhjsdweEc9BxH5Jc26w==", - "dev": true, - "dependencies": { - "@jridgewell/gen-mapping": "^0.1.0", - "@jridgewell/trace-mapping": "^0.3.9" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@azure-rest/ai-translation-text": { "version": "1.0.0-beta.1", "resolved": "https://registry.npmjs.org/@azure-rest/ai-translation-text/-/ai-translation-text-1.0.0-beta.1.tgz", @@ -318,15 +306,6 @@ "node": ">=4.0.0" } }, - "node_modules/@azure/core-http/node_modules/xmlbuilder": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", - "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", - "dev": true, - "engines": { - "node": ">=4.0" - } - }, "node_modules/@azure/core-lro": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/@azure/core-lro/-/core-lro-2.2.1.tgz", @@ -481,110 +460,52 @@ } }, "node_modules/@babel/code-frame": { - "version": "7.22.13", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.22.13.tgz", - "integrity": "sha512-XktuhWlJ5g+3TJXc5upd9Ks1HutSArik6jf2eAjYFyIOf4ej3RN+184cZbzDvbPnuTJIUhPKKJE3cIsYTiAT3w==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.28.6.tgz", + "integrity": "sha512-JYgintcMjRiCvS8mMECzaEn+m3PfoQiyqukOMCCVQtoJGYJw8j/8LBJEiqkHLkfwCcs74E3pbAUFNg7d9VNJ+Q==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/highlight": "^7.22.13", - "chalk": "^2.4.2" + "@babel/helper-validator-identifier": "^7.28.5", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/code-frame/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/code-frame/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/code-frame/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/@babel/code-frame/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, - "node_modules/@babel/code-frame/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/@babel/compat-data": { - "version": "7.18.8", - "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.18.8.tgz", - "integrity": "sha512-HSmX4WZPPK3FUxYp7g2T6EyO8j96HlZJlxmKPSh6KAcqwyDrfx7hKjXpAW/0FhFfTJsR0Yt4lAjLI2coMptIHQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.6.tgz", + "integrity": "sha512-2lfu57JtzctfIrcGMz992hyLlByuzgIk58+hhGCxjKZ3rWI82NnVLjXcaTqkI2NvlcvOskZaiZ5kjUALo3Lpxg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { - "version": "7.18.10", - "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.18.10.tgz", - "integrity": "sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw==", - "dev": true, - "dependencies": { - "@ampproject/remapping": "^2.1.0", - "@babel/code-frame": "^7.18.6", - "@babel/generator": "^7.18.10", - "@babel/helper-compilation-targets": "^7.18.9", - "@babel/helper-module-transforms": "^7.18.9", - "@babel/helpers": "^7.18.9", - "@babel/parser": "^7.18.10", - "@babel/template": "^7.18.10", - "@babel/traverse": "^7.18.10", - "@babel/types": "^7.18.10", - "convert-source-map": "^1.7.0", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.6.tgz", + "integrity": "sha512-H3mcG6ZDLTlYfaSNi0iOKkigqMFvkTKlGUYlD8GW7nNOYRrevuA46iTypPyv+06V3fEmvvazfntkBU34L0azAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/code-frame": "^7.28.6", + "@babel/generator": "^7.28.6", + "@babel/helper-compilation-targets": "^7.28.6", + "@babel/helper-module-transforms": "^7.28.6", + "@babel/helpers": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/template": "^7.28.6", + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6", + "@jridgewell/remapping": "^2.3.5", + "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", - "json5": "^2.2.1", - "semver": "^6.3.0" + "json5": "^2.2.3", + "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" @@ -594,60 +515,65 @@ "url": "https://opencollective.com/babel" } }, + "node_modules/@babel/core/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, "node_modules/@babel/core/node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" } }, "node_modules/@babel/generator": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.23.0.tgz", - "integrity": "sha512-lN85QRR+5IbYrMWM6Y4pE/noaQtg4pNiqeNGX60eqOfo6gtEj6uw/JagelB8vVztSd7R6M5n1+PQkDbHbBRU4g==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.6.tgz", + "integrity": "sha512-lOoVRwADj8hjf7al89tvQ2a1lf53Z+7tiXMgpZJL3maQPDxh0DgLMN62B2MKUOFcoodBHLMbDM6WAbKgNy5Suw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.23.0", - "@jridgewell/gen-mapping": "^0.3.2", - "@jridgewell/trace-mapping": "^0.3.17", - "jsesc": "^2.5.1" + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6", + "@jridgewell/gen-mapping": "^0.3.12", + "@jridgewell/trace-mapping": "^0.3.28", + "jsesc": "^3.0.2" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/generator/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.2.tgz", - "integrity": "sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==", + "node_modules/@babel/helper-compilation-targets": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.28.6.tgz", + "integrity": "sha512-JYtls3hqi15fcx5GaSNL7SCTJ2MNmjrkHXg4FSpOA/grxK8KwyZ5bubHsCq8FXCkua6xhuaaBit+3b7+VZRfcA==", "dev": true, + "license": "MIT", "dependencies": { - "@jridgewell/set-array": "^1.0.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.9" + "@babel/compat-data": "^7.28.6", + "@babel/helper-validator-option": "^7.27.1", + "browserslist": "^4.24.0", + "lru-cache": "^5.1.1", + "semver": "^6.3.1" }, "engines": { - "node": ">=6.0.0" + "node": ">=6.9.0" } }, - "node_modules/@babel/helper-compilation-targets": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.18.9.tgz", - "integrity": "sha512-tzLCyVmqUiFlcFoAPLA/gL9TeYrF61VLNtb+hvkuVaB5SUjW7jcfrglBIX1vUIoT7CLP3bBlIMeyEsIl2eFQNg==", + "node_modules/@babel/helper-compilation-targets/node_modules/lru-cache": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", + "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, + "license": "ISC", "dependencies": { - "@babel/compat-data": "^7.18.8", - "@babel/helper-validator-option": "^7.18.6", - "browserslist": "^4.20.2", - "semver": "^6.3.0" - }, - "engines": { - "node": ">=6.9.0" - }, - "peerDependencies": { - "@babel/core": "^7.0.0" + "yallist": "^3.0.2" } }, "node_modules/@babel/helper-compilation-targets/node_modules/semver": { @@ -655,292 +581,190 @@ "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, + "license": "ISC", "bin": { "semver": "bin/semver.js" } }, - "node_modules/@babel/helper-environment-visitor": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz", - "integrity": "sha512-zfedSIzFhat/gFhWfHtgWvlec0nqB9YEIVrpuwjruLlXfUSnA8cJB0miHKwqDnQ7d32aKo2xt88/xZptwxbfhA==", - "dev": true, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-function-name": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.23.0.tgz", - "integrity": "sha512-OErEqsrxjZTJciZ4Oo+eoZqeW9UIiOcuYKRJA4ZAgV9myA+pOXhhmpfNCKjEH/auVfEYVFJ6y1Tc4r0eIApqiw==", + "node_modules/@babel/helper-compilation-targets/node_modules/yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true, - "dependencies": { - "@babel/template": "^7.22.15", - "@babel/types": "^7.23.0" - }, - "engines": { - "node": ">=6.9.0" - } + "license": "ISC" }, - "node_modules/@babel/helper-hoist-variables": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-hoist-variables/-/helper-hoist-variables-7.22.5.tgz", - "integrity": "sha512-wGjk9QZVzvknA6yKIUURb8zY3grXCcOZt+/7Wcy8O2uctxhplmUPkOdlgoNhmdVee2c92JXbf1xpMtVNbfoxRw==", + "node_modules/@babel/helper-globals": { + "version": "7.28.0", + "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", + "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" - }, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.18.6.tgz", - "integrity": "sha512-0NFvs3VkuSYbFi1x2Vd6tKrywq+z/cLeYC/RJNFrIX/30Bf5aiGYbtvGXolEktzJH8o5E5KJ3tT+nkxuuZFVlA==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.28.6.tgz", + "integrity": "sha512-l5XkZK7r7wa9LucGw9LwZyyCUscb4x37JWTPz7swwFE/0FMQAGpiWUZn8u9DzkSBWEcK25jmvubfpw2dnAMdbw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.18.6" + "@babel/traverse": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.18.9.tgz", - "integrity": "sha512-KYNqY0ICwfv19b31XzvmI/mfcylOzbLtowkw+mfvGPAQ3kfCnMLYbED3YecL5tPd8nAYFQFAd6JHp2LxZk/J1g==", - "dev": true, - "dependencies": { - "@babel/helper-environment-visitor": "^7.18.9", - "@babel/helper-module-imports": "^7.18.6", - "@babel/helper-simple-access": "^7.18.6", - "@babel/helper-split-export-declaration": "^7.18.6", - "@babel/helper-validator-identifier": "^7.18.6", - "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-simple-access": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.18.6.tgz", - "integrity": "sha512-iNpIgTgyAvDQpDj76POqg+YEt8fPxx3yaNBg3S30dxNKm2SWfYhD0TGrK/Eu9wHpUW63VQU894TsTg+GLbUa1g==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.28.6.tgz", + "integrity": "sha512-67oXFAYr2cDLDVGLXTEABjdBJZ6drElUSI7WKp70NrpyISso3plG9SAGEF6y7zbha/wOzUByWWTJvEDVNIUGcA==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/types": "^7.18.6" + "@babel/helper-module-imports": "^7.28.6", + "@babel/helper-validator-identifier": "^7.28.5", + "@babel/traverse": "^7.28.6" }, "engines": { "node": ">=6.9.0" - } - }, - "node_modules/@babel/helper-split-export-declaration": { - "version": "7.22.6", - "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz", - "integrity": "sha512-AsUnxuLhRYsisFiaJwvp1QF+I3KjD5FOxut14q/GzovUe6orHLesW2C7d754kRm53h5gqrz6sFl6sxc4BVtE/g==", - "dev": true, - "dependencies": { - "@babel/types": "^7.22.5" }, - "engines": { - "node": ">=6.9.0" + "peerDependencies": { + "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-string-parser": { - "version": "7.22.5", - "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.22.5.tgz", - "integrity": "sha512-mM4COjgZox8U+JcXQwPijIZLElkgEpO5rsERVDJTc2qfCDfERyob6k5WegS14SX18IIjv+XD+GrqNumY5JRCDw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", + "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.22.20.tgz", - "integrity": "sha512-Y4OZ+ytlatR8AI+8KZfKuL5urKp7qey08ha31L8b3BwewJAoJamTzyvxPR/5D+KkdJCGPq/+8TukHBlY10FX9A==", + "version": "7.28.5", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.28.5.tgz", + "integrity": "sha512-qSs4ifwzKJSV39ucNjsvc6WVHs6b7S03sOh2OcHF9UHfVPqWWALUsNUVzhSBiItjRZoLHx7nIarVjqKVusUZ1Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { - "version": "7.18.6", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.18.6.tgz", - "integrity": "sha512-XO7gESt5ouv/LRJdrVjkShckw6STTaB7l9BrpBaAHDeF5YZT+01PCwmR0SJHnkW6i8OwW/EVWRShfi4j2x+KQw==", + "version": "7.27.1", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", + "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { - "version": "7.18.9", - "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.18.9.tgz", - "integrity": "sha512-Jf5a+rbrLoR4eNdUmnFu8cN5eNJT6qdTdOg5IHIzq87WwyRw9PwguLFOWYgktN/60IP4fgDUawJvs7PjQIzELQ==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.28.6.tgz", + "integrity": "sha512-xOBvwq86HHdB7WUDTfKfT/Vuxh7gElQ+Sfti2Cy6yIWNW05P8iUslOVcZ4/sKbE+/jQaukQAdz/gf3724kYdqw==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/template": "^7.18.6", - "@babel/traverse": "^7.18.9", - "@babel/types": "^7.18.9" + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/highlight": { - "version": "7.22.20", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.22.20.tgz", - "integrity": "sha512-dkdMCN3py0+ksCgYmGG8jKeGA/8Tk+gJwSYYlFGxG5lmhfKNoAy004YpLxpS1W2J8m/EK2Ew+yOs9pVRwO89mg==", + "node_modules/@babel/parser": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.6.tgz", + "integrity": "sha512-TeR9zWR18BvbfPmGbLampPMW+uW1NZnJlRuuHso8i87QZNq2JRF9i6RgxRqtEq+wQGsS19NNTWr2duhnE49mfQ==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/helper-validator-identifier": "^7.22.20", - "chalk": "^2.4.2", - "js-tokens": "^4.0.0" + "@babel/types": "^7.28.6" }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/highlight/node_modules/ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", - "dev": true, - "dependencies": { - "color-convert": "^1.9.0" + "bin": { + "parser": "bin/babel-parser.js" }, "engines": { - "node": ">=4" + "node": ">=6.0.0" } }, - "node_modules/@babel/highlight/node_modules/chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", + "node_modules/@babel/template": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.28.6.tgz", + "integrity": "sha512-YA6Ma2KsCdGb+WC6UpBVFJGXL58MDA6oyONbjyF/+5sBgxY/dwkhLogbMT2GXXyU84/IhRw/2D1Os1B/giz+BQ==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" + "@babel/code-frame": "^7.28.6", + "@babel/parser": "^7.28.6", + "@babel/types": "^7.28.6" }, "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/highlight/node_modules/color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "dependencies": { - "color-name": "1.1.3" - } - }, - "node_modules/@babel/highlight/node_modules/color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU= sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", - "dev": true - }, - "node_modules/@babel/highlight/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, - "engines": { - "node": ">=0.8.0" + "node": ">=6.9.0" } }, - "node_modules/@babel/highlight/node_modules/supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", + "node_modules/@babel/traverse": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.6.tgz", + "integrity": "sha512-fgWX62k02qtjqdSNTAGxmKYY/7FSL9WAS1o2Hu5+I5m9T0yxZzr4cnrfXQ/MX0rIifthCSs6FKTlzYbJcPtMNg==", "dev": true, + "license": "MIT", "dependencies": { - "has-flag": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/@babel/parser": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.23.0.tgz", - "integrity": "sha512-vvPKKdMemU85V9WE/l5wZEmImpCtLqbnTvqDS2U1fJ96KrxoW7KrXhNsNCblQlg8Ck4b85yxdTyelsMUgFUXiw==", - "dev": true, - "bin": { - "parser": "bin/babel-parser.js" + "@babel/code-frame": "^7.28.6", + "@babel/generator": "^7.28.6", + "@babel/helper-globals": "^7.28.0", + "@babel/parser": "^7.28.6", + "@babel/template": "^7.28.6", + "@babel/types": "^7.28.6", + "debug": "^4.3.1" }, "engines": { - "node": ">=6.0.0" + "node": ">=6.9.0" } }, - "node_modules/@babel/template": { - "version": "7.22.15", - "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz", - "integrity": "sha512-QPErUVm4uyJa60rkI73qneDacvdvzxshT3kksGqlGWYdOTIUOwJ7RDUL8sGqslY1uXWSL6xMFKEXDS3ox2uF0w==", + "node_modules/@babel/types": { + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.6.tgz", + "integrity": "sha512-0ZrskXVEHSWIqZM/sQZ4EV3jZJXRkio/WCxaqKZP1g//CEWEPSfeZFcms4XeKBCHU0ZKnIkdJeU/kF+eRp5lBg==", "dev": true, + "license": "MIT", "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/parser": "^7.22.15", - "@babel/types": "^7.22.15" - }, - "engines": { - "node": ">=6.9.0" - } - }, - "node_modules/@babel/traverse": { - "version": "7.23.2", - "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.23.2.tgz", - "integrity": "sha512-azpe59SQ48qG6nu2CzcMLbxUudtN+dOM9kDbUqGq3HXUJRlo7i8fvPoxQUzYgLZ4cMVmuZgm8vvBpNeRhd6XSw==", - "dev": true, - "dependencies": { - "@babel/code-frame": "^7.22.13", - "@babel/generator": "^7.23.0", - "@babel/helper-environment-visitor": "^7.22.20", - "@babel/helper-function-name": "^7.23.0", - "@babel/helper-hoist-variables": "^7.22.5", - "@babel/helper-split-export-declaration": "^7.22.6", - "@babel/parser": "^7.23.0", - "@babel/types": "^7.23.0", - "debug": "^4.1.0", - "globals": "^11.1.0" + "@babel/helper-string-parser": "^7.27.1", + "@babel/helper-validator-identifier": "^7.28.5" }, "engines": { "node": ">=6.9.0" } }, - "node_modules/@babel/traverse/node_modules/globals": { - "version": "11.12.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz", - "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==", + "node_modules/@bcoe/v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", + "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", "dev": true, + "license": "MIT", "engines": { - "node": ">=4" + "node": ">=18" } }, - "node_modules/@babel/types": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.23.0.tgz", - "integrity": "sha512-0oIyUfKoI3mSqMvsxBdclDwxXKXAUA8v/apZbc+iSyARYou1o8ZGDxbUYyLFoW2arqS2jDGqJuZvv1d/io1axg==", - "dev": true, + "node_modules/@c4312/eventsource-umd": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/@c4312/eventsource-umd/-/eventsource-umd-3.0.5.tgz", + "integrity": "sha512-0QhLg51eFB+SS/a4Pv5tHaRSnjJBpdFsjT3WN/Vfh6qzeFXqvaE+evVIIToYvr2lRBLg1NIB635ip8ML+/84Sg==", + "license": "MIT", "dependencies": { - "@babel/helper-string-parser": "^7.22.5", - "@babel/helper-validator-identifier": "^7.22.20", - "to-fast-properties": "^2.0.0" + "eventsource-parser": "^3.0.0" }, "engines": { - "node": ">=6.9.0" + "node": ">=18.0.0" } }, - "node_modules/@bcoe/v8-coverage": { - "version": "0.2.3", - "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", - "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", - "dev": true - }, "node_modules/@cspotcode/source-map-support": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/@cspotcode/source-map-support/-/source-map-support-0.8.1.tgz", @@ -986,12 +810,13 @@ } }, "node_modules/@discoveryjs/json-ext": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.5.3.tgz", - "integrity": "sha512-Fxt+AfXgjMoin2maPIYzFZnQjAXjAL0PHscM5pRTtatFqB+vZxAM9tLp2Optnuw3QOQC40jTNeGYFOMvyf7v9g==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@discoveryjs/json-ext/-/json-ext-0.6.3.tgz", + "integrity": "sha512-4B4OijXeVNOPZlYA2oEwWOTkzyltLao+xbotHQeqN++Rv27Y6s818+n2Qkp8q+Fxhn0t/5lA5X1Mxktud8eayQ==", "dev": true, + "license": "MIT", "engines": { - "node": ">=10.0.0" + "node": ">=14.17.0" } }, "node_modules/@electron/get": { @@ -1066,50 +891,83 @@ } }, "node_modules/@es-joy/jsdoccomment": { - "version": "0.48.0", - "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.48.0.tgz", - "integrity": "sha512-G6QUWIcC+KvSwXNsJyDTHvqUdNoAVJPPgkc3+Uk4WBKqZvoXhlvazOgm9aL0HwihJLQf0l+tOE2UFzXBqCqgDw==", + "version": "0.76.0", + "resolved": "https://registry.npmjs.org/@es-joy/jsdoccomment/-/jsdoccomment-0.76.0.tgz", + "integrity": "sha512-g+RihtzFgGTx2WYCuTHbdOXJeAlGnROws0TeALx9ow/ZmOROOZkVg5wp/B44n0WJgI4SQFP1eWM2iRPlU2Y14w==", "dev": true, + "license": "MIT", "dependencies": { + "@types/estree": "^1.0.8", + "@typescript-eslint/types": "^8.46.0", "comment-parser": "1.4.1", "esquery": "^1.6.0", - "jsdoc-type-pratt-parser": "~4.1.0" + "jsdoc-type-pratt-parser": "~6.10.0" }, "engines": { - "node": ">=16" + "node": ">=20.11.0" } }, - "node_modules/@eslint-community/eslint-utils": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", - "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "node_modules/@es-joy/jsdoccomment/node_modules/@typescript-eslint/types": { + "version": "8.51.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.51.0.tgz", + "integrity": "sha512-TizAvWYFM6sSscmEakjY3sPqGwxZRSywSsPEiuZF6d5GmGD9Gvlsv0f6N8FvAAA0CD06l3rIcWNbsN1e5F/9Ag==", "dev": true, - "dependencies": { - "eslint-visitor-keys": "^3.3.0" - }, + "license": "MIT", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, - "peerDependencies": { - "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@eslint-community/regexpp": { - "version": "4.11.1", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.1.tgz", - "integrity": "sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==", + "node_modules/@es-joy/resolve.exports": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@es-joy/resolve.exports/-/resolve.exports-1.2.0.tgz", + "integrity": "sha512-Q9hjxWI5xBM+qW2enxfe8wDKdFWMfd0Z29k5ZJnuBqD/CasY5Zryj09aCA6owbGATWz+39p5uIdaHXpopOcG8g==", "dev": true, + "license": "MIT", "engines": { - "node": "^12.0.0 || ^14.0.0 || >=16.0.0" + "node": ">=10" + } + }, + "node_modules/@eslint-community/eslint-utils": { + "version": "4.9.1", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", + "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } + }, + "node_modules/@eslint-community/regexpp": { + "version": "4.12.2", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", + "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/config-array": { - "version": "0.18.0", - "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.18.0.tgz", - "integrity": "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==", + "version": "0.21.1", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.21.1.tgz", + "integrity": "sha512-aw1gNayWpdI/jSYVgzN5pL0cfzU02GT3NBpeT/DXbx1/1x7ZKxFPd9bwrzygx/qiwIQiJ1sw/zD8qY/kRvlGHA==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "@eslint/object-schema": "^2.1.4", + "@eslint/object-schema": "^2.1.7", "debug": "^4.3.1", "minimatch": "^3.1.2" }, @@ -1117,20 +975,51 @@ "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, + "node_modules/@eslint/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@eslint/config-helpers": { + "version": "0.4.2", + "resolved": "https://registry.npmjs.org/@eslint/config-helpers/-/config-helpers-0.4.2.tgz", + "integrity": "sha512-gBrxN88gOIf3R7ja5K9slwNayVcZgK6SOUORm2uBzTeIEfeVaIhOpCtTox3P6R7o2jLFwLFTLnC7kU/RGcYEgw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/core": "^0.17.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, "node_modules/@eslint/core": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.6.0.tgz", - "integrity": "sha512-8I2Q8ykA4J0x0o7cg67FPVnehcqWTBehu/lmY+bolPFHGjh49YzGBMXTvpqVgEbBdvNCSxj6iFgiIyHzf03lzg==", + "version": "0.17.0", + "resolved": "https://registry.npmjs.org/@eslint/core/-/core-0.17.0.tgz", + "integrity": "sha512-yL/sLrpmtDaFEiUj1osRP4TI2MDz1AddJL+jZ7KSqvBuliN4xqYY54IfdN8qD8Toa6g1iloph1fxQNkjOxrrpQ==", "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@types/json-schema": "^7.0.15" + }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/eslintrc": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", - "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.3.3.tgz", + "integrity": "sha512-Kr+LPIUVKz2qkx1HAMH8q1q6azbqBAsXJUxBl/ODDuVPX45Z9DfwB8tPjTi6nNZ8BuM3nbJxC5zCAg5elnBUTQ==", "dev": true, + "license": "MIT", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", @@ -1138,7 +1027,7 @@ "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^4.1.0", + "js-yaml": "^4.1.1", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" }, @@ -1149,30 +1038,50 @@ "url": "https://opencollective.com/eslint" } }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/@eslint/js": { - "version": "9.11.1", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.11.1.tgz", - "integrity": "sha512-/qu+TWz8WwPWc7/HcIJKi+c+MOm46GdVaSlTTQcaqaL53+GsoA6MxWp5PtTx48qbSP7ylM1Kn7nhvkugfJvRSA==", + "version": "9.39.2", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.39.2.tgz", + "integrity": "sha512-q1mjIoW1VX4IvSocvM/vbTiveKC4k9eLrajNEuSsmjymSDEbpGddtpfOoN7YGAqBK3NG+uqo8ia4PDTt8buCYA==", "dev": true, + "license": "MIT", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://eslint.org/donate" } }, "node_modules/@eslint/object-schema": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", - "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.7.tgz", + "integrity": "sha512-VtAOaymWVfZcmZbp6E2mympDIHvyjXs/12LqWYjVw6qjrfF+VK+fyG33kChz3nnK+SU5/NeHOqrTEHS8sXO3OA==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@eslint/plugin-kit": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.2.0.tgz", - "integrity": "sha512-vH9PiIMMwvhCx31Af3HiGzsVNULDbyVkHXwlemn/B0TFj/00ho3y55efXrUZTfQipxoHC5u4xq6zblww1zm1Ig==", + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.4.1.tgz", + "integrity": "sha512-43/qtrDUokr7LJqoF2c3+RInu/t4zfrpYdoSDfYyhg52rwLV6TnOvdG4fXm7IkSB3wErkcmJS9iEhjVtOSEjjA==", "dev": true, + "license": "Apache-2.0", "dependencies": { + "@eslint/core": "^0.17.0", "levn": "^0.4.1" }, "engines": { @@ -1230,6 +1139,16 @@ "url": "https://opencollective.com/postcss/" } }, + "node_modules/@gulp-sourcemaps/identity-map/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/@gulp-sourcemaps/map-sources": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/@gulp-sourcemaps/map-sources/-/map-sources-1.0.0.tgz", @@ -1280,6 +1199,53 @@ "xtend": "~4.0.1" } }, + "node_modules/@gulpjs/messages": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@gulpjs/messages/-/messages-1.1.0.tgz", + "integrity": "sha512-Ys9sazDatyTgZVb4xPlDufLweJ/Os2uHWOv+Caxvy2O85JcnT4M3vc73bi8pdLWlv3fdWQz3pdI9tVwo8rQQSg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@gulpjs/to-absolute-glob": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/@gulpjs/to-absolute-glob/-/to-absolute-glob-4.0.0.tgz", + "integrity": "sha512-kjotm7XJrJ6v+7knhPaRgaT6q8F8K2jiafwYdNHLzmV0uGLuZY43FK6smNSHUPrhq5kX2slCUy+RGG/xGqmIKA==", + "dev": true, + "license": "MIT", + "dependencies": { + "is-negated-glob": "^1.0.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/@humanfs/core": { + "version": "0.19.1", + "resolved": "https://registry.npmjs.org/@humanfs/core/-/core-0.19.1.tgz", + "integrity": "sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18.0" + } + }, + "node_modules/@humanfs/node": { + "version": "0.16.7", + "resolved": "https://registry.npmjs.org/@humanfs/node/-/node-0.16.7.tgz", + "integrity": "sha512-/zUx+yOsIrG4Y43Eh2peDeKCxlRt/gET6aHfaKpuq267qXdYDFViVHfMaLyygZOnl0kGWxFIgsBy8QFuTLUXEQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanfs/core": "^0.19.1", + "@humanwhocodes/retry": "^0.4.0" + }, + "engines": { + "node": ">=18.18.0" + } + }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", @@ -1294,10 +1260,11 @@ } }, "node_modules/@humanwhocodes/retry": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.0.tgz", - "integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.4.3.tgz", + "integrity": "sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=18.18" }, @@ -1306,6 +1273,29 @@ "url": "https://github.com/sponsors/nzakas" } }, + "node_modules/@isaacs/balanced-match": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@isaacs/balanced-match/-/balanced-match-4.0.1.tgz", + "integrity": "sha512-yzMTt9lEb8Gv7zRioUilSglI0c0smZ9k5D65677DLWLtWJaXIS3CqcGyUFByYKlnUj6TkjLVs54fBl6+TiGQDQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/@isaacs/brace-expansion": { + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/@isaacs/brace-expansion/-/brace-expansion-5.0.0.tgz", + "integrity": "sha512-ZT55BDLV0yv0RBm2czMiZ+SqCGO7AvmOM3G/w2xhVPH+te0aKgFjmBvGlL1dH+ql2tgGO3MVrbb3jCKyvpgnxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@isaacs/balanced-match": "^4.0.1" + }, + "engines": { + "node": "20 || >=22" + } + }, "node_modules/@isaacs/cliui": { "version": "8.0.2", "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", @@ -1412,16 +1402,25 @@ } }, "node_modules/@jridgewell/gen-mapping": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.1.1.tgz", - "integrity": "sha512-sQXCasFk+U8lWYEe66WxRDOE9PjVz4vSM51fTu3Hw+ClTpUSQb718772vH3pyS5pShp6lvQM7SxgIDXXXmOX7w==", + "version": "0.3.13", + "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.13.tgz", + "integrity": "sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==", "dev": true, + "license": "MIT", "dependencies": { - "@jridgewell/set-array": "^1.0.0", - "@jridgewell/sourcemap-codec": "^1.4.10" - }, - "engines": { - "node": ">=6.0.0" + "@jridgewell/sourcemap-codec": "^1.5.0", + "@jridgewell/trace-mapping": "^0.3.24" + } + }, + "node_modules/@jridgewell/remapping": { + "version": "2.3.5", + "resolved": "https://registry.npmjs.org/@jridgewell/remapping/-/remapping-2.3.5.tgz", + "integrity": "sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/gen-mapping": "^0.3.5", + "@jridgewell/trace-mapping": "^0.3.24" } }, "node_modules/@jridgewell/resolve-uri": { @@ -1433,15 +1432,6 @@ "node": ">=6.0.0" } }, - "node_modules/@jridgewell/set-array": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@jridgewell/set-array/-/set-array-1.2.1.tgz", - "integrity": "sha512-R8gLRTZeyp03ymzP/6Lil/28tGeGEzhx1q2k703KGWRAI1VdvPIXdG70VJc2pAMw3NA6JKL5hhFu1sJX0Mnn/A==", - "dev": true, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@jridgewell/source-map": { "version": "0.3.6", "resolved": "https://registry.npmjs.org/@jridgewell/source-map/-/source-map-0.3.6.tgz", @@ -1452,36 +1442,31 @@ "@jridgewell/trace-mapping": "^0.3.25" } }, - "node_modules/@jridgewell/source-map/node_modules/@jridgewell/gen-mapping": { - "version": "0.3.5", - "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.5.tgz", - "integrity": "sha512-IzL8ZoEDIBRWEzlCcRhOaCupYyN5gdIK+Q6fbFdPDg6HqX6jpkItn7DFIpW9LQzXG6Df9sA7+OKnq0qlz/GaQg==", - "dev": true, - "dependencies": { - "@jridgewell/set-array": "^1.2.1", - "@jridgewell/sourcemap-codec": "^1.4.10", - "@jridgewell/trace-mapping": "^0.3.24" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/@jridgewell/sourcemap-codec": { - "version": "1.4.14", - "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.14.tgz", - "integrity": "sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==", - "dev": true + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" }, "node_modules/@jridgewell/trace-mapping": { - "version": "0.3.25", - "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz", - "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==", + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", "dev": true, + "license": "MIT", "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@keyv/serialize": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@keyv/serialize/-/serialize-1.1.1.tgz", + "integrity": "sha512-dXn3FZhPv0US+7dtJsIi2R+c7qWYiReoEh5zUntWCf4oSpMNib8FDhSoed6m3QyZdx5hK7iLFkYk3rNxwt8vTA==", + "dev": true, + "license": "MIT" + }, "node_modules/@koa/cors": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/@koa/cors/-/cors-5.0.0.tgz", @@ -1495,47 +1480,130 @@ } }, "node_modules/@koa/router": { - "version": "13.1.0", - "resolved": "https://registry.npmjs.org/@koa/router/-/router-13.1.0.tgz", - "integrity": "sha512-mNVu1nvkpSd8Q8gMebGbCkDWJ51ODetrFvLKYusej+V0ByD4btqHYnPIzTBLXnQMVUlm/oxVwqmWBY3zQfZilw==", + "version": "15.1.1", + "resolved": "https://registry.npmjs.org/@koa/router/-/router-15.1.1.tgz", + "integrity": "sha512-trYxL4VOx8r92f8luqpN83xkN0DMTsp/HBJIxoDZH/a2I1Hxvoe+jjjhyJRQUQIHmsNQjCM+Xj6nCqSvnDnlCw==", "dev": true, "license": "MIT", "dependencies": { - "http-errors": "^2.0.0", + "debug": "^4.4.3", + "http-errors": "^2.0.1", "koa-compose": "^4.1.0", - "path-to-regexp": "^6.3.0" + "path-to-regexp": "^8.3.0" }, "engines": { - "node": ">= 18" + "node": ">= 20" + }, + "peerDependencies": { + "koa": "^2.0.0 || ^3.0.0" + }, + "peerDependenciesMeta": { + "koa": { + "optional": false + } + } + }, + "node_modules/@malept/cross-spawn-promise": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/@malept/cross-spawn-promise/-/cross-spawn-promise-1.1.1.tgz", + "integrity": "sha512-RTBGWL5FWQcg9orDOCcp4LvItNzUPcyEU9bwaeJX0rJ1IQxzucC48Y0/sQLp/g6t99IQgAlGIaesJS+gTn7tVQ==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/malept" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/subscription/pkg/npm-.malept-cross-spawn-promise?utm_medium=referral&utm_source=npm_fund" + } + ], + "license": "Apache-2.0", + "dependencies": { + "cross-spawn": "^7.0.1" + }, + "engines": { + "node": ">= 10" } }, "node_modules/@microsoft/1ds-core-js": { - "version": "3.2.13", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-3.2.13.tgz", - "integrity": "sha512-CluYTRWcEk0ObG5EWFNWhs87e2qchJUn0p2D21ZUa3PWojPZfPSBs4//WIE0MYV8Qg1Hdif2ZTwlM7TbYUjfAg==", + "version": "3.2.18", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-3.2.18.tgz", + "integrity": "sha512-ytlFv3dfb8OGqvbZP8tSIlNvn3QNYxdsF0k6ikRMWSr6CmBxBi1sliaxc2Q5KuYOuaeWkd8WRm25Rx/UtHcyMg==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "2.8.15", + "@microsoft/applicationinsights-core-js": "2.8.18", "@microsoft/applicationinsights-shims": "^2.0.2", - "@microsoft/dynamicproto-js": "^1.1.7" + "@microsoft/dynamicproto-js": "^1.1.11" } }, "node_modules/@microsoft/1ds-post-js": { - "version": "3.2.13", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-3.2.13.tgz", - "integrity": "sha512-HgS574fdD19Bo2vPguyznL4eDw7Pcm1cVNpvbvBLWiW3x4e1FCQ3VMXChWnAxCae8Hb0XqlA2sz332ZobBavTA==", + "version": "4.3.11", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.11.tgz", + "integrity": "sha512-V0ZeeALy/Pj8HWgNHDsK+yDeCYnJ9bCgTWhcrna/ZiAT+sGfWs6mDBjAVcG03uP7TDjdWLf8w79lgbXJ3+s3DA==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "3.2.13", - "@microsoft/applicationinsights-shims": "^2.0.2", - "@microsoft/dynamicproto-js": "^1.1.7" + "@microsoft/1ds-core-js": "4.3.11", + "@microsoft/applicationinsights-shims": "3.0.1", + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" + } + }, + "node_modules/@microsoft/1ds-post-js/node_modules/@microsoft/1ds-core-js": { + "version": "4.3.11", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.11.tgz", + "integrity": "sha512-QyQE/YzFYB+31WEpX9hvDoXZOIXA7308Z5uuL1mSsyDSkNPl24hBWz9O3vZL+/p9shy756eKLI2nFLwwIAhXyw==", + "license": "MIT", + "dependencies": { + "@microsoft/applicationinsights-core-js": "3.3.11", + "@microsoft/applicationinsights-shims": "3.0.1", + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" + } + }, + "node_modules/@microsoft/1ds-post-js/node_modules/@microsoft/applicationinsights-core-js": { + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.11.tgz", + "integrity": "sha512-WlBY1sKDNL62T++NifgFCyDuOoNUNrVILfnHubOzgU/od7MFEQYWU8EZyDcBC/+Z8e3TD6jfixurYtWoUC+6Eg==", + "license": "MIT", + "dependencies": { + "@microsoft/applicationinsights-shims": "3.0.1", + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" + }, + "peerDependencies": { + "tslib": ">= 1.0.0" + } + }, + "node_modules/@microsoft/1ds-post-js/node_modules/@microsoft/applicationinsights-shims": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-shims/-/applicationinsights-shims-3.0.1.tgz", + "integrity": "sha512-DKwboF47H1nb33rSUfjqI6ryX29v+2QWcTrRvcQDA32AZr5Ilkr7whOOSsD1aBzwqX0RJEIP1Z81jfE3NBm/Lg==", + "license": "MIT", + "dependencies": { + "@nevware21/ts-utils": ">= 0.9.4 < 2.x" + } + }, + "node_modules/@microsoft/1ds-post-js/node_modules/@microsoft/dynamicproto-js": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.3.tgz", + "integrity": "sha512-JTWTU80rMy3mdxOjjpaiDQsTLZ6YSGGqsjURsY6AUQtIj0udlF/jYmhdLZu8693ZIC0T1IwYnFa0+QeiMnziBA==", + "license": "MIT", + "dependencies": { + "@nevware21/ts-utils": ">= 0.10.4 < 2.x" } }, "node_modules/@microsoft/applicationinsights-core-js": { - "version": "2.8.15", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.15.tgz", - "integrity": "sha512-yYAs9MyjGr2YijQdUSN9mVgT1ijI1FPMgcffpaPmYbHAVbQmF7bXudrBWHxmLzJlwl5rfep+Zgjli2e67lwUqQ==", + "version": "2.8.18", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.18.tgz", + "integrity": "sha512-yPHRZFLpnEO0uSgFPM1BLMRRwjoten9YBbn4pJRbCT4PigLnj748knmWsMwXIdcehtkRTYz78kPYa/LWP7nvmA==", + "license": "MIT", "dependencies": { "@microsoft/applicationinsights-shims": "2.0.2", - "@microsoft/dynamicproto-js": "^1.1.9" + "@microsoft/dynamicproto-js": "^1.1.11" }, "peerDependencies": { "tslib": "*" @@ -1547,9 +1615,25 @@ "integrity": "sha512-PoHEgsnmcqruLNHZ/amACqdJ6YYQpED0KSRe6J7gIJTtpZC1FfFU9b1fmDKDKtFoUSrPzEh1qzO3kmRZP0betg==" }, "node_modules/@microsoft/dynamicproto-js": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.9.tgz", - "integrity": "sha512-n1VPsljTSkthsAFYdiWfC+DKzK2WwcRp83Y1YAqdX552BstvsDjft9YXppjUzp11BPsapDoO1LDgrDB0XVsfNQ==" + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.11.tgz", + "integrity": "sha512-gNw9z9LbqLV+WadZ6/MMrWwO3e0LuoUH1wve/1iPsBNbgqeVCiB0EZFNNj2lysxS2gkqoF9hmyVaG3MoM1BkxA==", + "license": "MIT" + }, + "node_modules/@nevware21/ts-async": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@nevware21/ts-async/-/ts-async-0.5.5.tgz", + "integrity": "sha512-vwqaL05iJPjLeh5igPi8MeeAu10i+Aq7xko1fbo9F5Si6MnVN5505qaV7AhSdk5MCBJVT/UYMk3kgInNjDb4Ig==", + "license": "MIT", + "dependencies": { + "@nevware21/ts-utils": ">= 0.12.2 < 2.x" + } + }, + "node_modules/@nevware21/ts-utils": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.12.5.tgz", + "integrity": "sha512-JPQZWPKQJjj7kAftdEZL0XDFfbMgXCGiUAZe0d7EhLC3QlXTlZdSckGqqRIQ2QNl0VTEZyZUvRBw6Ednw089Fw==", + "license": "MIT" }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", @@ -1587,137 +1671,178 @@ } }, "node_modules/@octokit/auth-token": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-2.5.0.tgz", - "integrity": "sha512-r5FVUJCOLl19AxiuZD2VRZ/ORjp/4IN98Of6YJoJOkY75CIBuYfmiNHGrDwXr+aLGG55igl9QrxX3hbiXlLb+g==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-6.0.0.tgz", + "integrity": "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==", "dev": true, - "dependencies": { - "@octokit/types": "^6.0.3" + "license": "MIT", + "engines": { + "node": ">= 20" } }, "node_modules/@octokit/core": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-3.6.0.tgz", - "integrity": "sha512-7RKRKuA4xTjMhY+eG3jthb3hlZCsOwg3rztWh75Xc+ShDWOfDDATWbeZpAHBNRpm4Tv9WgBMOy1zEJYXG6NJ7Q==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/@octokit/core/-/core-7.0.6.tgz", + "integrity": "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q==", "dev": true, + "license": "MIT", "dependencies": { - "@octokit/auth-token": "^2.4.4", - "@octokit/graphql": "^4.5.8", - "@octokit/request": "^5.6.3", - "@octokit/request-error": "^2.0.5", - "@octokit/types": "^6.0.3", - "before-after-hook": "^2.2.0", - "universal-user-agent": "^6.0.0" + "@octokit/auth-token": "^6.0.0", + "@octokit/graphql": "^9.0.3", + "@octokit/request": "^10.0.6", + "@octokit/request-error": "^7.0.2", + "@octokit/types": "^16.0.0", + "before-after-hook": "^4.0.0", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 20" } }, "node_modules/@octokit/endpoint": { - "version": "6.0.12", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-6.0.12.tgz", - "integrity": "sha512-lF3puPwkQWGfkMClXb4k/eUT/nZKQfxinRWJrdZaJO85Dqwo/G0yOC434Jr2ojwafWJMYqFGFa5ms4jJUgujdA==", + "version": "11.0.2", + "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-11.0.2.tgz", + "integrity": "sha512-4zCpzP1fWc7QlqunZ5bSEjxc6yLAlRTnDwKtgXfcI/FxxGoqedDG8V2+xJ60bV2kODqcGB+nATdtap/XYq2NZQ==", "dev": true, + "license": "MIT", "dependencies": { - "@octokit/types": "^6.0.3", - "is-plain-object": "^5.0.0", - "universal-user-agent": "^6.0.0" + "@octokit/types": "^16.0.0", + "universal-user-agent": "^7.0.2" + }, + "engines": { + "node": ">= 20" } }, "node_modules/@octokit/graphql": { - "version": "4.8.0", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-4.8.0.tgz", - "integrity": "sha512-0gv+qLSBLKF0z8TKaSKTsS39scVKF9dbMxJpj3U0vC7wjNWFuIpL/z76Qe2fiuCbDRcJSavkXsVtMS6/dtQQsg==", + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-9.0.3.tgz", + "integrity": "sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA==", "dev": true, + "license": "MIT", "dependencies": { - "@octokit/request": "^5.6.0", - "@octokit/types": "^6.0.3", - "universal-user-agent": "^6.0.0" + "@octokit/request": "^10.0.6", + "@octokit/types": "^16.0.0", + "universal-user-agent": "^7.0.0" + }, + "engines": { + "node": ">= 20" } }, "node_modules/@octokit/openapi-types": { - "version": "11.2.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-11.2.0.tgz", - "integrity": "sha512-PBsVO+15KSlGmiI8QAzaqvsNlZlrDlyAJYcrXBCvVUxCp7VnXjkwPoFHgjEJXx3WF9BAwkA6nfCUA7i9sODzKA==", - "dev": true + "version": "27.0.0", + "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-27.0.0.tgz", + "integrity": "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA==", + "dev": true, + "license": "MIT" }, "node_modules/@octokit/plugin-paginate-rest": { - "version": "2.17.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-2.17.0.tgz", - "integrity": "sha512-tzMbrbnam2Mt4AhuyCHvpRkS0oZ5MvwwcQPYGtMv4tUa5kkzG58SVB0fcsLulOZQeRnOgdkZWkRUiyBlh0Bkyw==", + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-14.0.0.tgz", + "integrity": "sha512-fNVRE7ufJiAA3XUrha2omTA39M6IXIc6GIZLvlbsm8QOQCYvpq/LkMNGyFlB1d8hTDzsAXa3OKtybdMAYsV/fw==", "dev": true, + "license": "MIT", "dependencies": { - "@octokit/types": "^6.34.0" + "@octokit/types": "^16.0.0" + }, + "engines": { + "node": ">= 20" }, "peerDependencies": { - "@octokit/core": ">=2" + "@octokit/core": ">=6" } }, "node_modules/@octokit/plugin-request-log": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-1.0.4.tgz", - "integrity": "sha512-mLUsMkgP7K/cnFEw07kWqXGF5LKrOkD+lhCrKvPHXWDywAwuDUeDwWBpc69XK3pNX0uKiVt8g5z96PJ6z9xCFA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-request-log/-/plugin-request-log-6.0.0.tgz", + "integrity": "sha512-UkOzeEN3W91/eBq9sPZNQ7sUBvYCqYbrrD8gTbBuGtHEuycE4/awMXcYvx6sVYo7LypPhmQwwpUe4Yyu4QZN5Q==", "dev": true, + "license": "MIT", + "engines": { + "node": ">= 20" + }, "peerDependencies": { - "@octokit/core": ">=3" + "@octokit/core": ">=6" } }, "node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "5.13.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-5.13.0.tgz", - "integrity": "sha512-uJjMTkN1KaOIgNtUPMtIXDOjx6dGYysdIFhgA52x4xSadQCz3b/zJexvITDVpANnfKPW/+E0xkOvLntqMYpviA==", + "version": "17.0.0", + "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-17.0.0.tgz", + "integrity": "sha512-B5yCyIlOJFPqUUeiD0cnBJwWJO8lkJs5d8+ze9QDP6SvfiXSz1BF+91+0MeI1d2yxgOhU/O+CvtiZ9jSkHhFAw==", "dev": true, + "license": "MIT", "dependencies": { - "@octokit/types": "^6.34.0", - "deprecation": "^2.3.1" + "@octokit/types": "^16.0.0" + }, + "engines": { + "node": ">= 20" }, "peerDependencies": { - "@octokit/core": ">=3" + "@octokit/core": ">=6" } }, "node_modules/@octokit/request": { - "version": "5.6.3", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-5.6.3.tgz", - "integrity": "sha512-bFJl0I1KVc9jYTe9tdGGpAMPy32dLBXXo1dS/YwSCTL/2nd9XeHsY616RE3HPXDVk+a+dBuzyz5YdlXwcDTr2A==", + "version": "10.0.7", + "resolved": "https://registry.npmjs.org/@octokit/request/-/request-10.0.7.tgz", + "integrity": "sha512-v93h0i1yu4idj8qFPZwjehoJx4j3Ntn+JhXsdJrG9pYaX6j/XRz2RmasMUHtNgQD39nrv/VwTWSqK0RNXR8upA==", "dev": true, + "license": "MIT", "dependencies": { - "@octokit/endpoint": "^6.0.1", - "@octokit/request-error": "^2.1.0", - "@octokit/types": "^6.16.1", - "is-plain-object": "^5.0.0", - "node-fetch": "^2.6.7", - "universal-user-agent": "^6.0.0" + "@octokit/endpoint": "^11.0.2", + "@octokit/request-error": "^7.0.2", + "@octokit/types": "^16.0.0", + "fast-content-type-parse": "^3.0.0", + "universal-user-agent": "^7.0.2" + }, + "engines": { + "node": ">= 20" } }, "node_modules/@octokit/request-error": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-2.1.0.tgz", - "integrity": "sha512-1VIvgXxs9WHSjicsRwq8PlR2LR2x6DwsJAaFgzdi0JfJoGSO8mYI/cHJQ+9FbN21aa+DrgNLnwObmyeSC8Rmpg==", + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-7.1.0.tgz", + "integrity": "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw==", "dev": true, + "license": "MIT", "dependencies": { - "@octokit/types": "^6.0.3", - "deprecation": "^2.0.0", - "once": "^1.4.0" + "@octokit/types": "^16.0.0" + }, + "engines": { + "node": ">= 20" } }, "node_modules/@octokit/rest": { - "version": "18.12.0", - "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-18.12.0.tgz", - "integrity": "sha512-gDPiOHlyGavxr72y0guQEhLsemgVjwRePayJ+FcKc2SJqKUbxbkvf5kAZEWA/MKvsfYlQAMVzNJE3ezQcxMJ2Q==", + "version": "22.0.1", + "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-22.0.1.tgz", + "integrity": "sha512-Jzbhzl3CEexhnivb1iQ0KJ7s5vvjMWcmRtq5aUsKmKDrRW6z3r84ngmiFKFvpZjpiU/9/S6ITPFRpn5s/3uQJw==", "dev": true, + "license": "MIT", "dependencies": { - "@octokit/core": "^3.5.1", - "@octokit/plugin-paginate-rest": "^2.16.8", - "@octokit/plugin-request-log": "^1.0.4", - "@octokit/plugin-rest-endpoint-methods": "^5.12.0" + "@octokit/core": "^7.0.6", + "@octokit/plugin-paginate-rest": "^14.0.0", + "@octokit/plugin-request-log": "^6.0.0", + "@octokit/plugin-rest-endpoint-methods": "^17.0.0" + }, + "engines": { + "node": ">= 20" } }, "node_modules/@octokit/types": { - "version": "6.34.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-6.34.0.tgz", - "integrity": "sha512-s1zLBjWhdEI2zwaoSgyOFoKSl109CUcVBCc7biPJ3aAf6LGLU6szDvi31JPU7bxfla2lqfhjbbg/5DdFNxOwHw==", + "version": "16.0.0", + "resolved": "https://registry.npmjs.org/@octokit/types/-/types-16.0.0.tgz", + "integrity": "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg==", "dev": true, + "license": "MIT", "dependencies": { - "@octokit/openapi-types": "^11.2.0" + "@octokit/openapi-types": "^27.0.0" } }, + "node_modules/@one-ini/wasm": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/@one-ini/wasm/-/wasm-0.1.1.tgz", + "integrity": "sha512-XuySG1E38YScSJoMlqovLru4KTUNSjgVTIjyh7qMX6aNN5HY5Ct5LhRJdxO79JtTzKfzV/bnWpz+zquYrISsvw==", + "dev": true, + "license": "MIT" + }, "node_modules/@opentelemetry/api": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.0.3.tgz", @@ -2044,40 +2169,28 @@ "node": ">=14" } }, - "node_modules/@pkgr/core": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@pkgr/core/-/core-0.1.1.tgz", - "integrity": "sha512-cq8o4cWH0ibXh9VGi5P20Tu9XF/0fFXl9EUinr9QfTM7a7p0oTA4iJRCQWppXR1Pg8dSM0UCItCkPwsk9qWWYA==", - "dev": true, - "engines": { - "node": "^12.20.0 || ^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/unts" - } - }, "node_modules/@playwright/browser-chromium": { - "version": "1.47.2", - "resolved": "https://registry.npmjs.org/@playwright/browser-chromium/-/browser-chromium-1.47.2.tgz", - "integrity": "sha512-tsk9bLcGzIu4k4xI2ixlwDrdJhMqCalUCsSj7TRI8VuvK7cLiJIa5SR0dprKbX+wkku/JMR4EN6g9DMHvfna+Q==", + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/@playwright/browser-chromium/-/browser-chromium-1.57.0.tgz", + "integrity": "sha512-pUg+2p6HwewLp8KCD9G6VYaS2iewdkNkyqMcSIxXBXOlp1ojTxLF6/bwyR4ixLMy6tyv75jhE8PzzMZiX5KzwQ==", "dev": true, "hasInstallScript": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.47.2" + "playwright-core": "1.57.0" }, "engines": { "node": ">=18" } }, "node_modules/@playwright/test": { - "version": "1.50.0", - "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.50.0.tgz", - "integrity": "sha512-ZGNXbt+d65EGjBORQHuYKj+XhCewlwpnSd/EDuLPZGSiEWmgOJB5RmMCCYGy5aMfTs9wx61RivfDKi8H/hcMvw==", + "version": "1.56.1", + "resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.56.1.tgz", + "integrity": "sha512-vSMYtL/zOcFpvJCW71Q/OEGQb7KYBPAdKh35WNSkaZA75JlAO8ED8UN6GUNTm3drWomcbcqRPFqQbLae8yBTdg==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright": "1.50.0" + "playwright": "1.56.1" }, "bin": { "playwright": "cli.js" @@ -2086,6 +2199,26 @@ "node": ">=18" } }, + "node_modules/@sec-ant/readable-stream": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/@sec-ant/readable-stream/-/readable-stream-0.4.1.tgz", + "integrity": "sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==", + "dev": true, + "license": "MIT" + }, + "node_modules/@sindresorhus/base62": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/base62/-/base62-1.0.0.tgz", + "integrity": "sha512-TeheYy0ILzBEI/CO55CP6zJCSdSWeRtGnHy8U8dWSUH4I68iqTsy7HkMktR4xakThc9jotkPQUXT4ITdbV7cHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/@sindresorhus/is": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.6.0.tgz", @@ -2099,62 +2232,70 @@ } }, "node_modules/@sinonjs/commons": { - "version": "1.8.3", - "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.3.tgz", - "integrity": "sha512-xkNcLAn/wZaX14RPlwizcKicDk9G3F8m2nU3L7Ukm5zBgTwiT0wsoFAHx9Jq56fJA1z/7uKGtCRu16sOUCLIHQ==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", + "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "type-detect": "4.0.8" } }, "node_modules/@sinonjs/fake-timers": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-7.1.2.tgz", - "integrity": "sha512-iQADsW4LBMISqZ6Ci1dupJL9pprqwcVFTcOsEmQOEhW+KLCVn/Y4Jrvg2k19fIHCp+iFprriYPTdRcQR8NbUPg==", + "version": "13.0.5", + "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-13.0.5.tgz", + "integrity": "sha512-36/hTbH2uaWuGVERyC6da9YwGWnzUZXuPro/F2LfsdOsLnCojz/iSH8MxUt/FD2S5XBSVPhmArFUXcpCQ2Hkiw==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@sinonjs/commons": "^1.7.0" + "@sinonjs/commons": "^3.0.1" } }, "node_modules/@sinonjs/samsam": { - "version": "6.0.2", - "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-6.0.2.tgz", - "integrity": "sha512-jxPRPp9n93ci7b8hMfJOFDPRLFYadN6FSpeROFTR4UNF4i5b+EK6m4QXPO46BDhFgRy1JuS87zAnFOzCUwMJcQ==", + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/@sinonjs/samsam/-/samsam-8.0.3.tgz", + "integrity": "sha512-hw6HbX+GyVZzmaYNh82Ecj1vdGZrqVIn/keDTg63IgAwiQPO+xCz99uG6Woqgb4tM0mUiFENKZ4cqd7IX94AXQ==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@sinonjs/commons": "^1.6.0", - "lodash.get": "^4.4.2", - "type-detect": "^4.0.8" + "@sinonjs/commons": "^3.0.1", + "type-detect": "^4.1.0" } }, - "node_modules/@sinonjs/text-encoding": { - "version": "0.7.1", - "resolved": "https://registry.npmjs.org/@sinonjs/text-encoding/-/text-encoding-0.7.1.tgz", - "integrity": "sha512-+iTbntw2IZPb/anVDbypzfQa+ay64MW0Zo8aJ8gZPWMMK6/OubMVb6lUPMagqjOPnmtauXnFCACVl3O7ogjeqQ==", - "dev": true + "node_modules/@sinonjs/samsam/node_modules/type-detect": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.1.0.tgz", + "integrity": "sha512-Acylog8/luQ8L7il+geoSxhEkazvkslg7PSNKOX59mbB9cOveP5aq9h74Y7YU8yDpJwetzQQrfIwtf4Wp4LKcw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } }, "node_modules/@stylistic/eslint-plugin-ts": { - "version": "2.8.0", - "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-ts/-/eslint-plugin-ts-2.8.0.tgz", - "integrity": "sha512-VukJqkRlC2psLKoIHJ+4R3ZxLJfWeizGGX+X5ZxunjXo4MbxRNtwu5UvXuerABg4s2RV6Z3LFTdm0WvI4+RAMQ==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/@stylistic/eslint-plugin-ts/-/eslint-plugin-ts-4.4.1.tgz", + "integrity": "sha512-2r6cLcmdF6til66lx8esBYvBvsn7xCmLT50gw/n1rGGlTq/OxeNjBIh4c3VEaDGMa/5TybrZTia6sQUHdIWx1w==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/utils": "^8.4.0", - "eslint-visitor-keys": "^4.0.0", - "espree": "^10.1.0" + "@typescript-eslint/utils": "^8.32.1", + "eslint-visitor-keys": "^4.2.0", + "espree": "^10.3.0" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "peerDependencies": { - "eslint": ">=8.40.0" + "eslint": ">=9.0.0" } }, "node_modules/@stylistic/eslint-plugin-ts/node_modules/eslint-visitor-keys": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.1.0.tgz", - "integrity": "sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -2204,51 +2345,38 @@ } }, "node_modules/@ts-morph/common": { - "version": "0.20.0", - "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.20.0.tgz", - "integrity": "sha512-7uKjByfbPpwuzkstL3L5MQyuXPSKdoNG93Fmi2JoDcTf3pEP731JdRFAduRVkOs8oqxPsXKA+ScrWkdQ8t/I+Q==", + "version": "0.26.1", + "resolved": "https://registry.npmjs.org/@ts-morph/common/-/common-0.26.1.tgz", + "integrity": "sha512-Sn28TGl/4cFpcM+jwsH1wLncYq3FtN/BIpem+HOygfBWPT5pAeS5dB4VFVzV8FbnOKHpDLZmvAl4AjPEev5idA==", "dev": true, + "license": "MIT", "dependencies": { - "fast-glob": "^3.2.12", - "minimatch": "^7.4.3", - "mkdirp": "^2.1.6", + "fast-glob": "^3.3.2", + "minimatch": "^9.0.4", "path-browserify": "^1.0.1" } }, "node_modules/@ts-morph/common/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } }, "node_modules/@ts-morph/common/node_modules/minimatch": { - "version": "7.4.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-7.4.6.tgz", - "integrity": "sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, + "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@ts-morph/common/node_modules/mkdirp": { - "version": "2.1.6", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-2.1.6.tgz", - "integrity": "sha512-+hEnITedc8LAtIP9u3HJDFIdcLV2vXP33sqLLIzkv1Db1zO/1OxbvYf0Y1OC/S/Qo5dxHXepofhmxL02PsKe+A==", - "dev": true, - "bin": { - "mkdirp": "dist/cjs/src/bin.js" - }, - "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.17" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -2297,10 +2425,15 @@ "dev": true }, "node_modules/@types/cookie": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.3.3.tgz", - "integrity": "sha512-LKVP3cgXBT9RYj+t+9FDKwS5tdI+rPBXaNSkma7hvqy35lc7mAokC2zsqWJH0LaqIt3B962nuYI77hsJoT1gow==", - "dev": true + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-1.0.0.tgz", + "integrity": "sha512-mGFXbkDQJ6kAXByHS7QAggRXgols0mAdP4MuXgloGY1tXokvzaFFM4SMqWvf7AH0oafI7zlFJwoGWzmhDqTZ9w==", + "deprecated": "This is a stub types definition. cookie provides its own type definitions, so you do not need this installed.", + "dev": true, + "license": "MIT", + "dependencies": { + "cookie": "*" + } }, "node_modules/@types/debug": { "version": "4.1.9", @@ -2322,10 +2455,11 @@ } }, "node_modules/@types/estree": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz", - "integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==", - "dev": true + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.8.tgz", + "integrity": "sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==", + "dev": true, + "license": "MIT" }, "node_modules/@types/expect": { "version": "1.20.4", @@ -2345,36 +2479,41 @@ } }, "node_modules/@types/gulp-svgmin": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@types/gulp-svgmin/-/gulp-svgmin-1.2.1.tgz", - "integrity": "sha512-qT/Y+C2uWJZoGw4oAjuJGZk+ImmTrx+QZbMGSzf8a1absW3wztrmMPvCF64pdogATDVUSPQDLzPWAFeIxylJTA==", + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@types/gulp-svgmin/-/gulp-svgmin-1.2.5.tgz", + "integrity": "sha512-Vp5rfl/6xG2H9ipXdJrox+H3ymWKrG0iuyxdYTfJpEizzFsCGLD9VygWZ/UfYvPb3KuS787ZYhD3EveIQgmXuw==", "dev": true, + "license": "MIT", "dependencies": { "@types/node": "*", - "@types/svgo": "^1", + "@types/svgo": "1", "@types/vinyl": "*" } }, "node_modules/@types/http-cache-semantics": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.1.tgz", - "integrity": "sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==", - "dev": true + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.4.tgz", + "integrity": "sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==", + "dev": true, + "license": "MIT" }, "node_modules/@types/http-proxy-agent": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/@types/http-proxy-agent/-/http-proxy-agent-2.0.1.tgz", - "integrity": "sha512-dgsgbsgI3t+ZkdzF9H19uBaLsurIZJJjJsVpj4mCLp8B6YghQ7jVwyqhaL0PcVtuC3nOi0ZBhAi2Dd9jCUwdFA==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", + "integrity": "sha512-2XJTByP9C14Kcq5ClvPRfOgFJxklhuru7xmuSPcNr2CPwRGdpCgIdUUNc0MDb25+bzaSolVN0u4RKH7M+CMimg==", + "deprecated": "This is a stub types definition. http-proxy-agent provides its own type definitions, so you do not need this installed.", "dev": true, + "license": "MIT", "dependencies": { - "@types/node": "*" + "http-proxy-agent": "*" } }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/json-schema": { "version": "7.0.15", @@ -2405,16 +2544,18 @@ "optional": true }, "node_modules/@types/minimist": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.1.tgz", - "integrity": "sha512-fZQQafSREFyuZcdWFAExYjBiCL7AUCdgsk80iO0q4yihYYdcIiH28CcuPTGFgLOCC8RlW49GSQxdHwZP+I7CNg==", - "dev": true + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==", + "dev": true, + "license": "MIT" }, "node_modules/@types/mocha": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-9.1.1.tgz", - "integrity": "sha512-Z61JK7DKDtdKTWwLeElSEBcWGRLY8g95ic5FoQqI9CMx0ns/Ghep3B4DfcEimiKMvtamNVULVNKEsiwV3aQmXw==", - "dev": true + "version": "10.0.10", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.10.tgz", + "integrity": "sha512-xPyYSz1cMPnJQhl0CLMH68j3gprKZaTjG3s5Vi+fDgx+uhG9NOXwbVt52eFS8ECyXhyKcjDLCBEqBExKuiZb7Q==", + "dev": true, + "license": "MIT" }, "node_modules/@types/ms": { "version": "0.7.32", @@ -2423,12 +2564,13 @@ "dev": true }, "node_modules/@types/node": { - "version": "20.14.13", - "resolved": "https://registry.npmjs.org/@types/node/-/node-20.14.13.tgz", - "integrity": "sha512-+bHoGiZb8UiQ0+WEtmph2IWQCjIqg8MDZMAV+ppRRhUZnquF5mQkP/9vpSwJClEiSM/C7fZZExPzfU0vJTyp8w==", + "version": "25.0.10", + "resolved": "https://registry.npmjs.org/@types/node/-/node-25.0.10.tgz", + "integrity": "sha512-zWW5KPngR/yvakJgGOmZ5vTBemDoSqF3AcV/LrO5u5wTWyEAVVh+IT39G4gtyAkh3CtTZs8aX/yRM82OfzHJRg==", "dev": true, + "license": "MIT", "dependencies": { - "undici-types": "~5.26.4" + "undici-types": "~7.16.0" } }, "node_modules/@types/node-fetch": { @@ -2442,14 +2584,17 @@ } }, "node_modules/@types/node-fetch/node_modules/form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.4.tgz", + "integrity": "sha512-f0cRzm6dkyVYV3nPoooP8XlccPQukegwhAnpoLcXy+X+A8KfpGOoXwDr9FLZd3wzgLaBGQBE3lY93Zm/i1JvIQ==", "dev": true, + "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", - "mime-types": "^2.1.12" + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", + "mime-types": "^2.1.35" }, "engines": { "node": ">= 6" @@ -2465,17 +2610,19 @@ } }, "node_modules/@types/semver": { - "version": "7.5.8", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", - "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==" + "version": "7.7.1", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.1.tgz", + "integrity": "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==", + "license": "MIT" }, "node_modules/@types/sinon": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-10.0.2.tgz", - "integrity": "sha512-BHn8Bpkapj8Wdfxvh2jWIUoaYB/9/XhsL0oOvBfRagJtKlSl9NWPcFOz2lRukI9szwGxFtYZCTejJSqsGDbdmw==", + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/@types/sinon/-/sinon-21.0.0.tgz", + "integrity": "sha512-+oHKZ0lTI+WVLxx1IbJDNmReQaIsQJjN2e7UUrJHEeByG7bFeKJYsv1E75JxTQ9QKJDp21bAa/0W2Xo4srsDnw==", "dev": true, + "license": "MIT", "dependencies": { - "@sinonjs/fake-timers": "^7.1.0" + "@types/sinonjs__fake-timers": "*" } }, "node_modules/@types/sinon-test": { @@ -2487,6 +2634,13 @@ "@types/sinon": "*" } }, + "node_modules/@types/sinonjs__fake-timers": { + "version": "15.0.1", + "resolved": "https://registry.npmjs.org/@types/sinonjs__fake-timers/-/sinonjs__fake-timers-15.0.1.tgz", + "integrity": "sha512-Ko2tjWJq8oozHzHV+reuvS5KYIRAokHnGbDwGh/J64LntgpbuylF74ipEL24HCyRjf9FOlBiBHWBR1RlVKsI1w==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/svgo": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/@types/svgo/-/svgo-1.3.6.tgz", @@ -2494,10 +2648,11 @@ "dev": true }, "node_modules/@types/trusted-types": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-1.0.6.tgz", - "integrity": "sha512-230RC8sFeHoT6sSUlRO6a8cAnclO06eeiq1QDfiv2FGCLWFvvERWgwIQD4FWqD9A69BN7Lzee4OXwoMVnnsWDw==", - "dev": true + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "dev": true, + "license": "MIT" }, "node_modules/@types/tunnel": { "version": "0.0.3", @@ -2519,10 +2674,11 @@ } }, "node_modules/@types/vscode-notebook-renderer": { - "version": "1.72.0", - "resolved": "https://registry.npmjs.org/@types/vscode-notebook-renderer/-/vscode-notebook-renderer-1.72.0.tgz", - "integrity": "sha512-5iTjb39DpLn03ULUwrDR3L2Dy59RV4blSUHy0oLdQuIY11PhgWO4mXIcoFS0VxY1GZQ4IcjSf3ooT2Jrrcahnw==", - "dev": true + "version": "1.72.4", + "resolved": "https://registry.npmjs.org/@types/vscode-notebook-renderer/-/vscode-notebook-renderer-1.72.4.tgz", + "integrity": "sha512-bdKO41c6Dc24pH/O/eM/jqfCwGH4zc76o/g/6Gt1y/eg/bvvqP2/VpbV+Sa5Te2sZekFRcbYnSSFTKo3wcVGUg==", + "dev": true, + "license": "MIT" }, "node_modules/@types/webpack": { "version": "5.28.5", @@ -2571,6 +2727,42 @@ "@types/node": "*" } }, + "node_modules/@typescript-eslint/project-service": { + "version": "8.53.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/project-service/-/project-service-8.53.1.tgz", + "integrity": "sha512-WYC4FB5Ra0xidsmlPb+1SsnaSKPmS3gsjIARwbEkHkoWloQmuzcfypljaJcR78uyLA1h8sHdWWPHSLDI+MtNog==", + "dev": true, + "license": "MIT", + "dependencies": { + "@typescript-eslint/tsconfig-utils": "^8.53.1", + "@typescript-eslint/types": "^8.53.1", + "debug": "^4.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, + "node_modules/@typescript-eslint/project-service/node_modules/@typescript-eslint/types": { + "version": "8.53.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.53.1.tgz", + "integrity": "sha512-jr/swrr2aRmUAUjW5/zQHbMaui//vQlsZcJKijZf3M26bnmLj8LyZUpj8/Rd6uzaek06OWsqdofN/Thenm5O8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, "node_modules/@typescript-eslint/scope-manager": { "version": "8.8.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.8.0.tgz", @@ -2588,6 +2780,23 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@typescript-eslint/tsconfig-utils": { + "version": "8.53.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/tsconfig-utils/-/tsconfig-utils-8.53.1.tgz", + "integrity": "sha512-qfvLXS6F6b1y43pnf0pPbXJ+YoXIC7HKg0UGZ27uMIemKMKA6XH2DTxsEDdpdN29D+vHV07x/pnlPNVLhdhWiA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" + } + }, "node_modules/@typescript-eslint/types": { "version": "8.8.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.8.0.tgz", @@ -2630,10 +2839,11 @@ } }, "node_modules/@typescript-eslint/typescript-estree/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } @@ -2654,15 +2864,16 @@ } }, "node_modules/@typescript-eslint/utils": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.8.0.tgz", - "integrity": "sha512-QE2MgfOTem00qrlPgyByaCHay9yb1+9BjnMFnSFkUKQfu7adBXDTnCAivURnuPPAG/qiB+kzKkZKmKfaMT0zVg==", + "version": "8.53.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.53.1.tgz", + "integrity": "sha512-c4bMvGVWW4hv6JmDUEG7fSYlWOl3II2I4ylt0NM+seinYQlZMQIaKaXIIVJWt9Ofh6whrpM+EdDQXKXjNovvrg==", "dev": true, + "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "8.8.0", - "@typescript-eslint/types": "8.8.0", - "@typescript-eslint/typescript-estree": "8.8.0" + "@eslint-community/eslint-utils": "^4.9.1", + "@typescript-eslint/scope-manager": "8.53.1", + "@typescript-eslint/types": "8.53.1", + "@typescript-eslint/typescript-estree": "8.53.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2672,17 +2883,19 @@ "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.57.0 || ^9.0.0" + "eslint": "^8.57.0 || ^9.0.0", + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@typescript-eslint/visitor-keys": { - "version": "8.8.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.8.0.tgz", - "integrity": "sha512-8mq51Lx6Hpmd7HnA2fcHQo3YgfX1qbccxQOgZcb4tvasu//zXRaA1j5ZRFeCw/VRAdFi4mRM9DnZw0Nu0Q2d1g==", + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/scope-manager": { + "version": "8.53.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.53.1.tgz", + "integrity": "sha512-Lu23yw1uJMFY8cUeq7JlrizAgeQvWugNQzJp8C3x8Eo5Jw5Q2ykMdiiTB9vBVOOUBysMzmRRmUfwFrZuI2C4SQ==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "8.8.0", - "eslint-visitor-keys": "^3.4.3" + "@typescript-eslint/types": "8.53.1", + "@typescript-eslint/visitor-keys": "8.53.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -2692,75 +2905,568 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@vscode/deviceid": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@vscode/deviceid/-/deviceid-0.1.1.tgz", - "integrity": "sha512-ErpoMeKKNYAkR1IT3zxB5RtiTqEECdh8fxggupWvzuxpTAX77hwOI2NdJ7um+vupnXRBZVx4ugo0+dVHJWUkag==", - "hasInstallScript": true, - "dependencies": { - "fs-extra": "^11.2.0", - "uuid": "^9.0.1" + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/types": { + "version": "8.53.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.53.1.tgz", + "integrity": "sha512-jr/swrr2aRmUAUjW5/zQHbMaui//vQlsZcJKijZf3M26bnmLj8LyZUpj8/Rd6uzaek06OWsqdofN/Thenm5O8A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@vscode/gulp-electron": { - "version": "1.36.0", - "resolved": "https://registry.npmjs.org/@vscode/gulp-electron/-/gulp-electron-1.36.0.tgz", - "integrity": "sha512-zGNnxN2mccAa0fgyjUn0+HUX4sv4syUfoLQru3/QAQYki1oeqnkPpCXZxhismFmWpXwTwsf5sXeh1AOs8uyU0g==", + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/typescript-estree": { + "version": "8.53.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.53.1.tgz", + "integrity": "sha512-RGlVipGhQAG4GxV1s34O91cxQ/vWiHJTDHbXRr0li2q/BGg3RR/7NM8QDWgkEgrwQYCvmJV9ichIwyoKCQ+DTg==", "dev": true, + "license": "MIT", "dependencies": { - "@electron/get": "^2.0.2", - "@octokit/rest": "^18.0.14", - "event-stream": "3.3.4", - "gulp-filter": "^5.1.0", - "gulp-rename": "1.2.2", - "gulp-symdest": "^1.2.0", - "gulp-vinyl-zip": "^2.1.2", - "mkdirp": "^0.5.1", - "plist": "^3.0.1", - "progress": "^1.1.8", - "rcedit": "^0.3.0", - "rimraf": "^2.4.2", - "semver": "^4.3.4", - "sumchecker": "^3.0.1", - "temp": "^0.8.3", - "vinyl": "^3.0.0", - "vinyl-fs": "^3.0.3" + "@typescript-eslint/project-service": "8.53.1", + "@typescript-eslint/tsconfig-utils": "8.53.1", + "@typescript-eslint/types": "8.53.1", + "@typescript-eslint/visitor-keys": "8.53.1", + "debug": "^4.4.3", + "minimatch": "^9.0.5", + "semver": "^7.7.3", + "tinyglobby": "^0.2.15", + "ts-api-utils": "^2.4.0" }, "engines": { - "node": ">=10" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "typescript": ">=4.8.4 <6.0.0" } }, - "node_modules/@vscode/gulp-electron/node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "node_modules/@typescript-eslint/utils/node_modules/@typescript-eslint/visitor-keys": { + "version": "8.53.1", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.53.1.tgz", + "integrity": "sha512-oy+wV7xDKFPRyNggmXuZQSBzvoLnpmJs+GhzRhPjrxl2b/jIlyjVokzm47CZCDUdXKr2zd7ZLodPfOBpOPyPlg==", "dev": true, + "license": "MIT", "dependencies": { - "minimist": "^1.2.5" + "@typescript-eslint/types": "8.53.1", + "eslint-visitor-keys": "^4.2.1" }, - "bin": { - "mkdirp": "bin/cmd.js" + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@vscode/gulp-electron/node_modules/rcedit": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/rcedit/-/rcedit-0.3.0.tgz", - "integrity": "sha1-y17uGF5Ub57aWXwkjJkGGG+pa84= sha512-6cFo8dgNFf92BPdBNEKtwCl321wSVW6HYn3+QRQMGf0sclBMerCyMAreQDdIchfR8ECG0pAYD33NIODaJEnv4w==", - "dev": true - }, - "node_modules/@vscode/gulp-electron/node_modules/semver": { - "version": "4.3.6", - "resolved": "https://registry.npmjs.org/semver/-/semver-4.3.6.tgz", - "integrity": "sha512-IrpJ+yoG4EOH8DFWuVg+8H1kW1Oaof0Wxe7cPcXW3x9BjkN/eVo54F15LyqemnDIUYskQWr9qvl/RihmSy6+xQ==", + "node_modules/@typescript-eslint/utils/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, - "bin": { - "semver": "bin/semver" + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/eslint-visitor-keys": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@typescript-eslint/utils/node_modules/ts-api-utils": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-2.4.0.tgz", + "integrity": "sha512-3TaVTaAv2gTiMB35i3FiGJaRfwb3Pyn/j3m/bfAvGe8FB7CF6u+LMYqYlDh7reQf7UNvoTvdfAqHGmPGOSsPmA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18.12" + }, + "peerDependencies": { + "typescript": ">=4.8.4" + } + }, + "node_modules/@typescript-eslint/visitor-keys": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.8.0.tgz", + "integrity": "sha512-8mq51Lx6Hpmd7HnA2fcHQo3YgfX1qbccxQOgZcb4tvasu//zXRaA1j5ZRFeCw/VRAdFi4mRM9DnZw0Nu0Q2d1g==", + "dev": true, + "dependencies": { + "@typescript-eslint/types": "8.8.0", + "eslint-visitor-keys": "^3.4.3" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } + }, + "node_modules/@vscode/deviceid": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@vscode/deviceid/-/deviceid-0.1.4.tgz", + "integrity": "sha512-3u705VptsQhKMcHvUMJzaOn9fBrKEQHsl7iibRRVQ8kUNV+cptki7bQXACPNsGtJ5Dh4/7A7W1uKtP3z39GUQg==", + "hasInstallScript": true, + "license": "MIT", + "dependencies": { + "fs-extra": "^11.2.0", + "uuid": "^9.0.1" + } + }, + "node_modules/@vscode/gulp-electron": { + "version": "1.38.2", + "resolved": "https://registry.npmjs.org/@vscode/gulp-electron/-/gulp-electron-1.38.2.tgz", + "integrity": "sha512-uFMp6Utz2kf62NMXVIht09FfIcuAFLuw7b9xhJNm2iGaaAI3b2BBHP05cKG3LYIPGvkWoC7UNk4EjyQDO7T/ZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@electron/get": "^4.0.1", + "@octokit/rest": "^22.0.0", + "event-stream": "3.3.4", + "gulp-filter": "^5.1.0", + "gulp-rename": "1.2.2", + "gulp-symdest": "^1.2.0", + "gulp-vinyl-zip": "^2.1.2", + "mkdirp": "^0.5.1", + "plist": "^3.0.1", + "progress": "^1.1.8", + "rcedit": "^4.0.1", + "rimraf": "^2.4.2", + "semver": "^7.7.2", + "sumchecker": "^3.0.1", + "temp": "^0.8.3", + "vinyl": "^3.0.0", + "vinyl-fs": "^3.0.3" + }, + "engines": { + "node": ">=22" + } + }, + "node_modules/@vscode/gulp-electron/node_modules/@electron/get": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/@electron/get/-/get-4.0.2.tgz", + "integrity": "sha512-n9fRt/nzzOOZdDtTP3kT6GVdo0ro9FgMKCoS520kQMIiKBhpGmPny6yK/lER3tOCKr+wLYW1O25D9oI6ZinwCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "debug": "^4.1.1", + "env-paths": "^3.0.0", + "got": "^14.4.5", + "graceful-fs": "^4.2.11", + "progress": "^2.0.3", + "semver": "^7.6.3", + "sumchecker": "^3.0.1" + }, + "engines": { + "node": ">=22.12.0" + }, + "optionalDependencies": { + "global-agent": "^3.0.0" + } + }, + "node_modules/@vscode/gulp-electron/node_modules/@electron/get/node_modules/progress": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", + "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/@vscode/gulp-electron/node_modules/@sindresorhus/is": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-7.1.1.tgz", + "integrity": "sha512-rO92VvpgMc3kfiTjGT52LEtJ8Yc5kCWhZjLQ3LwlA4pSgPpQO7bVpYXParOD8Jwf+cVQECJo3yP/4I8aZtUQTQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sindresorhus/is?sponsor=1" + } + }, + "node_modules/@vscode/gulp-electron/node_modules/cacheable-lookup": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/cacheable-lookup/-/cacheable-lookup-7.0.0.tgz", + "integrity": "sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + } + }, + "node_modules/@vscode/gulp-electron/node_modules/cacheable-request": { + "version": "13.0.16", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-13.0.16.tgz", + "integrity": "sha512-qwRRfUu3bPhPEgKoLAXpYJfm1E2VuHT4DCzl579k2ODyFG+43jv3iMi2wbHlreIZR3+9Sp1lp4fsaXZ9VIlmzA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/http-cache-semantics": "4.0.4", + "get-stream": "9.0.1", + "http-cache-semantics": "4.2.0", + "keyv": "5.5.4", + "mimic-response": "4.0.0", + "normalize-url": "8.1.0", + "responselike": "4.0.2" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/@vscode/gulp-electron/node_modules/cacheable-request/node_modules/keyv": { + "version": "5.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.5.4.tgz", + "integrity": "sha512-eohl3hKTiVyD1ilYdw9T0OiB4hnjef89e3dMYKz+mVKDzj+5IteTseASUsOB+EU9Tf6VNTCjDePcP6wkDGmLKQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@keyv/serialize": "^1.1.1" + } + }, + "node_modules/@vscode/gulp-electron/node_modules/decompress-response": { + "version": "10.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-10.0.0.tgz", + "integrity": "sha512-oj7KWToJuuxlPr7VV0vabvxEIiqNMo+q0NueIiL3XhtwC6FVOX7Hr1c0C4eD0bmf7Zr+S/dSf2xvkH3Ad6sU3Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "mimic-response": "^4.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@vscode/gulp-electron/node_modules/env-paths": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-3.0.0.tgz", + "integrity": "sha512-dtJUTepzMW3Lm/NPxRf3wP4642UWhjL2sQxc+ym2YMj1m/H2zDNQOlezafzkHwn6sMstjHTwG6iQQsctDW/b1A==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@vscode/gulp-electron/node_modules/event-stream": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "integrity": "sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==", + "dev": true, + "license": "MIT", + "dependencies": { + "duplexer": "~0.1.1", + "from": "~0", + "map-stream": "~0.1.0", + "pause-stream": "0.0.11", + "split": "0.3", + "stream-combiner": "~0.0.4", + "through": "~2.3.1" + } + }, + "node_modules/@vscode/gulp-electron/node_modules/get-stream": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-9.0.1.tgz", + "integrity": "sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sec-ant/readable-stream": "^0.4.1", + "is-stream": "^4.0.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@vscode/gulp-electron/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/@vscode/gulp-electron/node_modules/got": { + "version": "14.6.5", + "resolved": "https://registry.npmjs.org/got/-/got-14.6.5.tgz", + "integrity": "sha512-Su87c0NNeg97de1sO02gy9I8EmE7DCJ1gzcFLcgGpYeq2PnLg4xz73MWrp6HjqbSsjb6Glf4UBDW6JNyZA6uSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/is": "^7.0.1", + "byte-counter": "^0.1.0", + "cacheable-lookup": "^7.0.0", + "cacheable-request": "^13.0.12", + "decompress-response": "^10.0.0", + "form-data-encoder": "^4.0.2", + "http2-wrapper": "^2.2.1", + "keyv": "^5.5.3", + "lowercase-keys": "^3.0.0", + "p-cancelable": "^4.0.1", + "responselike": "^4.0.2", + "type-fest": "^4.26.1" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sindresorhus/got?sponsor=1" + } + }, + "node_modules/@vscode/gulp-electron/node_modules/gulp-rename": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/gulp-rename/-/gulp-rename-1.2.2.tgz", + "integrity": "sha512-qhfUlYwq5zIP4cpRjx+np2vZVnr0xCRQrF3RsGel8uqL47Gu3yjmllSfnvJyl/39zYuxS68e1nnxImbm7388vw==", + "dev": true, + "engines": { + "node": ">=0.10.0", + "npm": ">=1.2.10" + } + }, + "node_modules/@vscode/gulp-electron/node_modules/http2-wrapper": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/http2-wrapper/-/http2-wrapper-2.2.1.tgz", + "integrity": "sha512-V5nVw1PAOgfI3Lmeaj2Exmeg7fenjhRUgz1lPSezy1CuhPYbgQtbQj4jZfEAEMlaL+vupsvhjqCyjzob0yxsmQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "quick-lru": "^5.1.1", + "resolve-alpn": "^1.2.0" + }, + "engines": { + "node": ">=10.19.0" + } + }, + "node_modules/@vscode/gulp-electron/node_modules/is-stream": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-4.0.1.tgz", + "integrity": "sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@vscode/gulp-electron/node_modules/keyv": { + "version": "5.5.5", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-5.5.5.tgz", + "integrity": "sha512-FA5LmZVF1VziNc0bIdCSA1IoSVnDCqE8HJIZZv2/W8YmoAM50+tnUgJR/gQZwEeIMleuIOnRnHA/UaZRNeV4iQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@keyv/serialize": "^1.1.1" + } + }, + "node_modules/@vscode/gulp-electron/node_modules/lowercase-keys": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-3.0.0.tgz", + "integrity": "sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@vscode/gulp-electron/node_modules/map-stream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", + "integrity": "sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==", + "dev": true + }, + "node_modules/@vscode/gulp-electron/node_modules/mimic-response": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-4.0.0.tgz", + "integrity": "sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@vscode/gulp-electron/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@vscode/gulp-electron/node_modules/mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "dev": true, + "dependencies": { + "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" + } + }, + "node_modules/@vscode/gulp-electron/node_modules/normalize-url": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-8.1.0.tgz", + "integrity": "sha512-X06Mfd/5aKsRHc0O0J5CUedwnPmnDtLF2+nq+KN9KSDlJHkPuh0JUviWjEWMe0SW/9TDdSLVPuk7L5gGTIA1/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@vscode/gulp-electron/node_modules/p-cancelable": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-4.0.1.tgz", + "integrity": "sha512-wBowNApzd45EIKdO1LaU+LrMBwAcjfPaYtVzV3lmfM3gf8Z4CHZsiIqlM8TZZ8okYvh5A1cP6gTfCRQtwUpaUg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + } + }, + "node_modules/@vscode/gulp-electron/node_modules/rcedit": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/rcedit/-/rcedit-4.0.1.tgz", + "integrity": "sha512-bZdaQi34krFWhrDn+O53ccBDw0MkAT2Vhu75SqhtvhQu4OPyFM4RoVheyYiVQYdjhUi6EJMVWQ0tR6bCIYVkUg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn-windows-exe": "^1.1.0" + }, + "engines": { + "node": ">= 14.0.0" + } + }, + "node_modules/@vscode/gulp-electron/node_modules/responselike": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-4.0.2.tgz", + "integrity": "sha512-cGk8IbWEAnaCpdAt1BHzJ3Ahz5ewDJa0KseTsE3qIRMJ3C698W8psM7byCeWVpd/Ha7FUYzuRVzXoKoM6nRUbA==", + "dev": true, + "license": "MIT", + "dependencies": { + "lowercase-keys": "^3.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@vscode/gulp-electron/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, + "node_modules/@vscode/gulp-electron/node_modules/split": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", + "integrity": "sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "through": "2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@vscode/gulp-electron/node_modules/stream-combiner": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "integrity": "sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "duplexer": "~0.1.1" } }, "node_modules/@vscode/iconv-lite-umd": { "version": "0.7.0", "resolved": "https://registry.npmjs.org/@vscode/iconv-lite-umd/-/iconv-lite-umd-0.7.0.tgz", - "integrity": "sha512-bRRFxLfg5dtAyl5XyiVWz/ZBPahpOpPrNYnnHpOpUZvam4tKH35wdhP4Kj6PbM0+KdliOsPzbGWpkxcdpNB/sg==" + "integrity": "sha512-bRRFxLfg5dtAyl5XyiVWz/ZBPahpOpPrNYnnHpOpUZvam4tKH35wdhP4Kj6PbM0+KdliOsPzbGWpkxcdpNB/sg==", + "license": "MIT" }, "node_modules/@vscode/l10n-dev": { "version": "0.0.35", @@ -2784,10 +3490,11 @@ } }, "node_modules/@vscode/l10n-dev/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } @@ -2829,10 +3536,24 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/@vscode/l10n-dev/node_modules/xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, "node_modules/@vscode/policy-watcher": { - "version": "1.1.10", - "resolved": "https://registry.npmjs.org/@vscode/policy-watcher/-/policy-watcher-1.1.10.tgz", - "integrity": "sha512-erRJiryjhP//MnRZo+j0LxjVSFa4eZMR9HeAGxIuxlZCQrnvrIG5nv/4qBxiMH0+uE4Z74YY/Ct0wus6l9U/xg==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/@vscode/policy-watcher/-/policy-watcher-1.3.0.tgz", + "integrity": "sha512-a8pPxlZlMJWOOj2NZ/2ceXgHdDU/NXo+8Pn/InV/sPBfbvTnf/MpMc4pscm9pdU4UIrTGR5+OduQW7mTK8DK7Q==", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -2850,9 +3571,9 @@ } }, "node_modules/@vscode/proxy-agent": { - "version": "0.31.0", - "resolved": "https://registry.npmjs.org/@vscode/proxy-agent/-/proxy-agent-0.31.0.tgz", - "integrity": "sha512-HY6UaJ9mKsBxHGnbj9EKigH5vczvB+9wTa8csNqPIjE8i2XjinKcGTBa+ky2+Eb+LGQNvyV/v56+XF6P11xRaQ==", + "version": "0.32.0", + "resolved": "https://registry.npmjs.org/@vscode/proxy-agent/-/proxy-agent-0.32.0.tgz", + "integrity": "sha512-n6h2+WVMJ3ByfGUakDbBNpR25J2JpLQabofiTKHIcLpXfxhT5TQSEH4OcjesZZfqw1zDpd7oBgcgqToWIiaBrQ==", "license": "MIT", "dependencies": { "@tootallnate/once": "^3.0.0", @@ -2868,9 +3589,9 @@ } }, "node_modules/@vscode/ripgrep": { - "version": "1.15.10", - "resolved": "https://registry.npmjs.org/@vscode/ripgrep/-/ripgrep-1.15.10.tgz", - "integrity": "sha512-83Q6qFrELpFgf88bPOcwSWDegfY2r/cb6bIfdLTSZvN73Dg1wviSfO+1v6lTFMd0mAvUYYcTUu+Mn5xMroZMxA==", + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/@vscode/ripgrep/-/ripgrep-1.17.0.tgz", + "integrity": "sha512-mBRKm+ASPkUcw4o9aAgfbusIu6H4Sdhw09bjeP1YOBFTJEZAnrnk6WZwzv8NEjgC82f7ILvhmb1WIElSugea6g==", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -2889,9 +3610,9 @@ } }, "node_modules/@vscode/spdlog": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/@vscode/spdlog/-/spdlog-0.15.1.tgz", - "integrity": "sha512-tVPeXmyz3/4NKqtNfNQxqcrBSSEZVIbF4lVDuDh9Nik5xhuHVceCU6cTpwmJ6yVBs+jv51SGF622txOQv95sZA==", + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/@vscode/spdlog/-/spdlog-0.15.7.tgz", + "integrity": "sha512-xpHAtw0IESD6wmjqLr6LbpYAmr8ZYm8AT7hGE7oM7AojNeOBngXLOqmzpXbTNTAvXBq1KHy8PwbMmY24uYR/oQ==", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -2926,50 +3647,50 @@ "integrity": "sha512-9ORTwwS74VaTn38tNbQhsA5U44zkJfcb0BdTSyyG6frP4e8KMtHuTXYmwefe5dpL8XB1aGSIVTaLjD3BbWb5iA==" }, "node_modules/@vscode/telemetry-extractor": { - "version": "1.10.2", - "resolved": "https://registry.npmjs.org/@vscode/telemetry-extractor/-/telemetry-extractor-1.10.2.tgz", - "integrity": "sha512-hn+KDSwIRj7LzDSFd9HALkc80UY1g16nQgWztHml+nxAZU3Hw/EoWEEDxOncvDYq9YcV+tX/cVHrVjbNL2Dg0g==", + "version": "1.18.0", + "resolved": "https://registry.npmjs.org/@vscode/telemetry-extractor/-/telemetry-extractor-1.18.0.tgz", + "integrity": "sha512-bj/+tFpdgZHCynotGY/4FxF9rqmLL1geuLyitLkRfiS2BN89B7WQ14g2Q+B7ZOg5G8ZcLIOjnF0p5JNO2HE4hQ==", "dev": true, + "license": "MIT", "dependencies": { - "@vscode/ripgrep": "^1.15.9", - "command-line-args": "^5.2.1", - "ts-morph": "^19.0.0" + "@vscode/ripgrep": "^1.15.10", + "command-line-args": "^6.0.1", + "ts-morph": "^25.0.1" }, "bin": { "vscode-telemetry-extractor": "out/extractor.js" } }, "node_modules/@vscode/test-cli": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/@vscode/test-cli/-/test-cli-0.0.6.tgz", - "integrity": "sha512-4i61OUv5PQr3GxhHOuUgHdgBDfIO/kXTPCsEyFiMaY4SOqQTgkTmyZLagHehjOgCfsXdcrJa3zgQ7zoc+Dh6hQ==", + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/@vscode/test-cli/-/test-cli-0.0.12.tgz", + "integrity": "sha512-iYN0fDg29+a2Xelle/Y56Xvv7Nc8Thzq4VwpzAF/SIE6918rDicqfsQxV6w1ttr2+SOm+10laGuY9FG2ptEKsQ==", "dev": true, + "license": "MIT", "dependencies": { - "@types/mocha": "^10.0.2", - "c8": "^9.1.0", - "chokidar": "^3.5.3", - "enhanced-resolve": "^5.15.0", + "@types/mocha": "^10.0.10", + "c8": "^10.1.3", + "chokidar": "^3.6.0", + "enhanced-resolve": "^5.18.3", "glob": "^10.3.10", "minimatch": "^9.0.3", - "mocha": "^10.2.0", - "supports-color": "^9.4.0", + "mocha": "^11.7.4", + "supports-color": "^10.2.2", "yargs": "^17.7.2" }, "bin": { "vscode-test": "out/bin.mjs" + }, + "engines": { + "node": ">=18" } }, - "node_modules/@vscode/test-cli/node_modules/@types/mocha": { - "version": "10.0.2", - "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.2.tgz", - "integrity": "sha512-NaHL0+0lLNhX6d9rs+NSt97WH/gIlRHmszXbQ/8/MV/eVcFNdeJ/GYhrFuUc8K7WuPhRhTSdMkCp8VMzhUq85w==", - "dev": true - }, "node_modules/@vscode/test-cli/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } @@ -3028,138 +3749,58 @@ } }, "node_modules/@vscode/test-web": { - "version": "0.0.62", - "resolved": "https://registry.npmjs.org/@vscode/test-web/-/test-web-0.0.62.tgz", - "integrity": "sha512-Ypug5PvhPOPFbuHVilai7t23tm3Wm5geIpC2DB09Gy9o0jZCduramiSdPf+YN7yhkFy1usFYtN3Eaks1XoBrOQ==", + "version": "0.0.77", + "resolved": "https://registry.npmjs.org/@vscode/test-web/-/test-web-0.0.77.tgz", + "integrity": "sha512-jeHvZn2crMdX6ICX7vZYeDx6DLxyW07b9EU3cV8zT+coEVQpf4C8zIN6uEBV67xFOUmOi4C1bzFZF5oUkDD7+w==", "dev": true, "license": "MIT", "dependencies": { "@koa/cors": "^5.0.0", - "@koa/router": "^13.1.0", - "@playwright/browser-chromium": "^1.47.2", - "glob": "^11.0.0", + "@koa/router": "^15.0.0", + "@playwright/browser-chromium": "^1.57.0", "gunzip-maybe": "^1.4.2", "http-proxy-agent": "^7.0.2", - "https-proxy-agent": "^7.0.5", - "koa": "^2.15.3", + "https-proxy-agent": "^7.0.6", + "koa": "^3.1.1", "koa-morgan": "^1.0.1", - "koa-mount": "^4.0.0", + "koa-mount": "^4.2.0", "koa-static": "^5.0.0", "minimist": "^1.2.8", - "playwright": "^1.47.2", - "tar-fs": "^3.0.6", - "vscode-uri": "^3.0.8" + "playwright": "^1.57.0", + "tar-fs": "^3.1.1", + "tinyglobby": "^0.2.15", + "vscode-uri": "^3.1.0" }, "bin": { "vscode-test-web": "out/server/index.js" }, "engines": { - "node": ">=16" - } - }, - "node_modules/@vscode/test-web/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0" + "node": ">=20" } }, - "node_modules/@vscode/test-web/node_modules/glob": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz", - "integrity": "sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==", + "node_modules/@vscode/test-web/node_modules/playwright": { + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.57.0.tgz", + "integrity": "sha512-ilYQj1s8sr2ppEJ2YVadYBN0Mb3mdo9J0wQ+UuDhzYqURwSoW4n1Xs5vs7ORwgDGmyEh33tRMeS8KhdkMoLXQw==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "foreground-child": "^3.1.0", - "jackspeak": "^4.0.1", - "minimatch": "^10.0.0", - "minipass": "^7.1.2", - "package-json-from-dist": "^1.0.0", - "path-scurry": "^2.0.0" + "playwright-core": "1.57.0" }, "bin": { - "glob": "dist/esm/bin.mjs" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@vscode/test-web/node_modules/jackspeak": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.1.tgz", - "integrity": "sha512-cub8rahkh0Q/bw1+GxP7aeSe29hHHn2V4m29nnDlvCdlgU+3UGxkZp7Z53jLUdpX3jdTO0nJZUDl3xvbWc2Xog==", - "dev": true, - "dependencies": { - "@isaacs/cliui": "^8.0.2" + "playwright": "cli.js" }, "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=18" }, "optionalDependencies": { - "@pkgjs/parseargs": "^0.11.0" - } - }, - "node_modules/@vscode/test-web/node_modules/lru-cache": { - "version": "11.0.1", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.1.tgz", - "integrity": "sha512-CgeuL5uom6j/ZVrg7G/+1IXqRY8JXX4Hghfy5YE0EhoYQWvndP1kufu58cmZLNIDKnRhZrXfdS9urVWx98AipQ==", - "dev": true, - "engines": { - "node": "20 || >=22" - } - }, - "node_modules/@vscode/test-web/node_modules/minimatch": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", - "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", - "dev": true, - "dependencies": { - "brace-expansion": "^2.0.1" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/@vscode/test-web/node_modules/minipass": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", - "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", - "dev": true, - "engines": { - "node": ">=16 || 14 >=14.17" - } - }, - "node_modules/@vscode/test-web/node_modules/path-scurry": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", - "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", - "dev": true, - "dependencies": { - "lru-cache": "^11.0.0", - "minipass": "^7.1.2" - }, - "engines": { - "node": "20 || >=22" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "fsevents": "2.3.2" } }, "node_modules/@vscode/tree-sitter-wasm": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@vscode/tree-sitter-wasm/-/tree-sitter-wasm-0.1.2.tgz", - "integrity": "sha512-nXFrHey/TdhN2v15Qurs+trTSQB2ad0O8I8R7RMAjvrZi9qXAE+H+l9ZHHsA14fjZxKIyGg+nFmNpdEAuQALpw==", + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@vscode/tree-sitter-wasm/-/tree-sitter-wasm-0.1.3.tgz", + "integrity": "sha512-gs0+tlOfriWc6h1kXTCZvmPTuusN+SeDs7yDVZn/kKLGgqlhXFbl/gWKItxaTeryTDalN8N+ikneni6+3UDOag==", "license": "MIT" }, "node_modules/@vscode/v8-heap-parser": { @@ -3169,25 +3810,27 @@ "dev": true }, "node_modules/@vscode/vscode-languagedetection": { - "version": "1.0.21", - "resolved": "https://registry.npmjs.org/@vscode/vscode-languagedetection/-/vscode-languagedetection-1.0.21.tgz", - "integrity": "sha512-zSUH9HYCw5qsCtd7b31yqkpaCU6jhtkKLkvOOA8yTrIRfBSOFb8PPhgmMicD7B/m+t4PwOJXzU1XDtrM9Fd3/g==", + "version": "1.0.23", + "resolved": "https://registry.npmjs.org/@vscode/vscode-languagedetection/-/vscode-languagedetection-1.0.23.tgz", + "integrity": "sha512-Ywk6vXC81nUHvc9WX3uFJG/UHFLXDYJgNeUVirBLJGvmchXHdapsGeAYclNqO1thQLmykmJhSouIZV+JJS8o1A==", + "license": "MIT", "bin": { "vscode-languagedetection": "cli/index.js" } }, "node_modules/@vscode/vscode-perf": { - "version": "0.0.19", - "resolved": "https://registry.npmjs.org/@vscode/vscode-perf/-/vscode-perf-0.0.19.tgz", - "integrity": "sha512-E/I0S+71K3Jo4kiMYbeKM8mUG3K8cHlj5MFVfPYVAvlp7KuIZTM914E7osp+jx8XgMLN6fChxnFmntm1GtVrKA==", + "version": "0.0.25", + "resolved": "https://registry.npmjs.org/@vscode/vscode-perf/-/vscode-perf-0.0.25.tgz", + "integrity": "sha512-h3PpwX0mJeRqVdlKnBJ9n+ngihXpWcbyy1xeI8tLDyK/g7yanawknzVm526Ojv5LRs19LCucqUzia0s4oZjhKA==", "dev": true, + "license": "MIT", "dependencies": { "chalk": "^4.x", "commander": "^9.4.0", "cookie": "^0.7.2", "js-base64": "^3.7.4", "node-fetch": "2.6.8", - "playwright": "^1.29.2" + "playwright": "^1.56.1" }, "bin": { "vscode-perf": "bin/vscode-perf" @@ -3196,6 +3839,16 @@ "node": ">= 16" } }, + "node_modules/@vscode/vscode-perf/node_modules/cookie": { + "version": "0.7.2", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", + "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, "node_modules/@vscode/windows-ca-certs": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@vscode/windows-ca-certs/-/windows-ca-certs-0.3.3.tgz", @@ -3221,20 +3874,22 @@ } }, "node_modules/@vscode/windows-mutex": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@vscode/windows-mutex/-/windows-mutex-0.5.0.tgz", - "integrity": "sha512-iD29L9AUscpn07aAvhP2QuhrXzuKc1iQpPF6u7ybtvRbR+o+RotfbuKqqF1RDlDDrJZkL+3AZTy4D01U4nEe5A==", + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/@vscode/windows-mutex/-/windows-mutex-0.5.3.tgz", + "integrity": "sha512-hWNmD+AzINR57jWuc/iW53kA+BghI4iOuicxhAEeeJLPOeMm9X5IUD0ttDwJFEib+D8H/2T9pT/8FeB/xcqbRw==", "hasInstallScript": true, + "license": "MIT", "dependencies": { "bindings": "^1.5.0", "node-addon-api": "7.1.0" } }, "node_modules/@vscode/windows-process-tree": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@vscode/windows-process-tree/-/windows-process-tree-0.6.0.tgz", - "integrity": "sha512-7/DjBKKUtlmKNiAet2GRbdvfjgMKmfBeWVClIgONv8aqxGnaKca5N85eIDxh6rLMy2hKvFqIIsqgxs1Q26TWwg==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@vscode/windows-process-tree/-/windows-process-tree-0.6.3.tgz", + "integrity": "sha512-mjirLbtgjv7P6fwD8gx7iaY961EfGqUExGvfzsKl3spLfScg57ejlMi+7O1jfJqpM2Zly9DTSxyY4cFsDN6c9Q==", "hasInstallScript": true, + "license": "MIT", "dependencies": { "node-addon-api": "7.1.0" } @@ -3392,48 +4047,52 @@ } }, "node_modules/@webgpu/types": { - "version": "0.1.44", - "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.44.tgz", - "integrity": "sha512-JDpYJN5E/asw84LTYhKyvPpxGnD+bAKPtpW9Ilurf7cZpxaTbxkQcGwOd7jgB9BPBrTYQ+32ufo4HiuomTjHNQ==", - "dev": true + "version": "0.1.69", + "resolved": "https://registry.npmjs.org/@webgpu/types/-/types-0.1.69.tgz", + "integrity": "sha512-RPmm6kgRbI8e98zSD3RVACvnuktIja5+yLgDAkTmxLr90BEwdTXRQWNLF3ETTTyH/8mKhznZuN5AveXYFEsMGQ==", + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/@webpack-cli/configtest": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-2.1.1.tgz", - "integrity": "sha512-wy0mglZpDSiSS0XHrVR+BAdId2+yxPSoJW8fsna3ZpYSlufjvxnP4YbKTCBZnNIcGN4r6ZPXV55X4mYExOfLmw==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/configtest/-/configtest-3.0.1.tgz", + "integrity": "sha512-u8d0pJ5YFgneF/GuvEiDA61Tf1VDomHHYMjv/wc9XzYj7nopltpG96nXN5dJRstxZhcNpV1g+nT6CydO7pHbjA==", "dev": true, + "license": "MIT", "engines": { - "node": ">=14.15.0" + "node": ">=18.12.0" }, "peerDependencies": { - "webpack": "5.x.x", - "webpack-cli": "5.x.x" + "webpack": "^5.82.0", + "webpack-cli": "6.x.x" } }, "node_modules/@webpack-cli/info": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-2.0.2.tgz", - "integrity": "sha512-zLHQdI/Qs1UyT5UBdWNqsARasIA+AaF8t+4u2aS2nEpBQh2mWIVb8qAklq0eUENnC5mOItrIB4LiS9xMtph18A==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/info/-/info-3.0.1.tgz", + "integrity": "sha512-coEmDzc2u/ffMvuW9aCjoRzNSPDl/XLuhPdlFRpT9tZHmJ/039az33CE7uH+8s0uL1j5ZNtfdv0HkfaKRBGJsQ==", "dev": true, + "license": "MIT", "engines": { - "node": ">=14.15.0" + "node": ">=18.12.0" }, "peerDependencies": { - "webpack": "5.x.x", - "webpack-cli": "5.x.x" + "webpack": "^5.82.0", + "webpack-cli": "6.x.x" } }, "node_modules/@webpack-cli/serve": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-2.0.5.tgz", - "integrity": "sha512-lqaoKnRYBdo1UgDX8uF24AfGMifWK19TxPmM5FHc2vAGxrJ/qtyUyFBWoY1tISZdelsQ5fBcOusifo5o5wSJxQ==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@webpack-cli/serve/-/serve-3.0.1.tgz", + "integrity": "sha512-sbgw03xQaCLiT6gcY/6u3qBDn01CWw/nbaXl3gTdTFuJJ75Gffv3E3DBpgvY2fkkrdS1fpjaXNOmJlnbtKauKg==", "dev": true, + "license": "MIT", "engines": { - "node": ">=14.15.0" + "node": ">=18.12.0" }, "peerDependencies": { - "webpack": "5.x.x", - "webpack-cli": "5.x.x" + "webpack": "^5.82.0", + "webpack-cli": "6.x.x" }, "peerDependenciesMeta": { "webpack-dev-server": { @@ -3442,98 +4101,101 @@ } }, "node_modules/@xterm/addon-clipboard": { - "version": "0.2.0-beta.81", - "resolved": "https://registry.npmjs.org/@xterm/addon-clipboard/-/addon-clipboard-0.2.0-beta.81.tgz", - "integrity": "sha512-vDxRyBO9VHzsl+gUQsDlUM9o2ZxSJKzE2eYQtuILKcf5D0EXYI86aMwKT/1iguX41vcMg42WXQBQ0TLWZ2MyTQ==", + "version": "0.2.0-beta.82", + "resolved": "https://registry.npmjs.org/@xterm/addon-clipboard/-/addon-clipboard-0.2.0-beta.82.tgz", + "integrity": "sha512-6PCRV0AHm/+ogeRdz2Txndau3l2Z7X7Buu8v5kpnNB30DKyvMh5p9J35maBPIwKF8XUSBvgywu+AW5x6mVqu9g==", "license": "MIT", "dependencies": { "js-base64": "^3.7.5" }, "peerDependencies": { - "@xterm/xterm": "^5.6.0-beta.98" + "@xterm/xterm": "^5.6.0-beta.99" } }, "node_modules/@xterm/addon-image": { - "version": "0.9.0-beta.98", - "resolved": "https://registry.npmjs.org/@xterm/addon-image/-/addon-image-0.9.0-beta.98.tgz", - "integrity": "sha512-yJaezwUc1Y3QYCmYSpjFW9IzMTLPSqrRCgdPnQ0JbCAVASRVmH6DLRfeikheJCvoXW6VUwMmGkb3fSplTxiV1w==", + "version": "0.9.0-beta.99", + "resolved": "https://registry.npmjs.org/@xterm/addon-image/-/addon-image-0.9.0-beta.99.tgz", + "integrity": "sha512-fU6VsnB3X6RUVo5Y2ZACEnbS/3CSFPhWxkDML6r+fgPz6pV4IwGBFLuyvUPxfyfpYt5+3muh6ChDDwUjxG1Ldg==", "license": "MIT", "peerDependencies": { - "@xterm/xterm": "^5.6.0-beta.98" + "@xterm/xterm": "^5.6.0-beta.99" } }, "node_modules/@xterm/addon-ligatures": { - "version": "0.10.0-beta.98", - "resolved": "https://registry.npmjs.org/@xterm/addon-ligatures/-/addon-ligatures-0.10.0-beta.98.tgz", - "integrity": "sha512-1zYeS9OUBR2ThG7dsxsGKOqeSlUo+DNTd5aaV5ZFbKQsQ1w+sQCaL73ZrXp1kuVK7pBiP5NCo4ffcXfzcztztA==", + "version": "0.11.0-beta.107", + "resolved": "https://registry.npmjs.org/@xterm/addon-ligatures/-/addon-ligatures-0.11.0-beta.107.tgz", + "integrity": "sha512-M+sf5/SvLFRo7LifIzF1hRCtSHr0aL/g1dQGYPpz5ceuiVgGvPJhRM/N9QHFIS9axPx4RCfEGl5wcSp+CTWEnA==", "license": "MIT", "dependencies": { - "font-finder": "^1.1.0", - "font-ligatures": "^1.4.1" + "lru-cache": "^6.0.0", + "opentype.js": "^0.8.0" }, "engines": { "node": ">8.0.0" }, "peerDependencies": { - "@xterm/xterm": "^5.6.0-beta.98" + "@xterm/xterm": "^6.1.0-beta.107" } }, "node_modules/@xterm/addon-progress": { - "version": "0.2.0-beta.4", - "resolved": "https://registry.npmjs.org/@xterm/addon-progress/-/addon-progress-0.2.0-beta.4.tgz", - "integrity": "sha512-7t1nlaANdEjUBVvuTTs5gw6UQgB+unFLwSGGnYXIvdQroYdkXQXSSATSqpYKVCd/6QFhBerzdB2VwPM5L5lxIw==", + "version": "0.2.0-beta.5", + "resolved": "https://registry.npmjs.org/@xterm/addon-progress/-/addon-progress-0.2.0-beta.5.tgz", + "integrity": "sha512-6dfUtCqK/anFiVilv1KNyVWbEql2hJwINlAXnl5YtIyEwR8F/i+zWBuzUj9152gT3rDASTmgXE5HG6mnyaUI9w==", "license": "MIT", "peerDependencies": { - "@xterm/xterm": "^5.6.0-beta.98" + "@xterm/xterm": "^5.6.0-beta.99" } }, "node_modules/@xterm/addon-search": { - "version": "0.16.0-beta.98", - "resolved": "https://registry.npmjs.org/@xterm/addon-search/-/addon-search-0.16.0-beta.98.tgz", - "integrity": "sha512-7gtx7eYvwFLxlb5q2IKxa7jG1KEinVwTlT3ijnSsPawwn7rGi6nR135rGiR8DAjEV0GUO406ICeoYyVbgiFNwQ==", + "version": "0.17.0-beta.105", + "resolved": "https://registry.npmjs.org/@xterm/addon-search/-/addon-search-0.17.0-beta.105.tgz", + "integrity": "sha512-HkjOQiPWKnKnyeBJm1Q4Dt/JexT1DOPlT5yBylg0r8iCmZBAf3BIt5IDXCgjLrS31WF210Ho1rAstN9kwCNttw==", "license": "MIT", "peerDependencies": { - "@xterm/xterm": "^5.6.0-beta.98" + "@xterm/xterm": "^6.1.0-beta.105" } }, "node_modules/@xterm/addon-serialize": { - "version": "0.14.0-beta.98", - "resolved": "https://registry.npmjs.org/@xterm/addon-serialize/-/addon-serialize-0.14.0-beta.98.tgz", - "integrity": "sha512-Tsr8j3wnun2raYR1DgsNClQP/I5a85u/uW/5EiYH+/iPPua6EWJvPlr5Q6TCU/cdIKW1o27Z3L5/mw0pfMUXrQ==", + "version": "0.14.0-beta.99", + "resolved": "https://registry.npmjs.org/@xterm/addon-serialize/-/addon-serialize-0.14.0-beta.99.tgz", + "integrity": "sha512-7ULW4BUUGL1Bv8vGBNylaBTpKFDm7rjMdkOwJt+LVd/oJkyL8RFSGgQSuYb+5DyiEhBSpeahg3bi8bStxufvMQ==", "license": "MIT", "peerDependencies": { - "@xterm/xterm": "^5.6.0-beta.98" + "@xterm/xterm": "^5.6.0-beta.99" } }, "node_modules/@xterm/addon-unicode11": { - "version": "0.9.0-beta.98", - "resolved": "https://registry.npmjs.org/@xterm/addon-unicode11/-/addon-unicode11-0.9.0-beta.98.tgz", - "integrity": "sha512-3GjjEYAPWAEGE1CaTFDjQxKcY5NiHHPmeufTsRp3IL5850ZiaMMq9bIL2WSdoFIbVgfbxh5mRy2cJPdu9m0uRQ==", + "version": "0.10.0-beta.107", + "resolved": "https://registry.npmjs.org/@xterm/addon-unicode11/-/addon-unicode11-0.10.0-beta.107.tgz", + "integrity": "sha512-aWnjjJGGAOUxuWRl+kF7EyD2qQ3TqRdb0u0L1aB1MlUk04qG5Ctv3uwLu8Moh/uAyJ7tAqgU7GQQP6yWDwvnJQ==", "license": "MIT", "peerDependencies": { - "@xterm/xterm": "^5.6.0-beta.98" + "@xterm/xterm": "^6.1.0-beta.107" } }, "node_modules/@xterm/addon-webgl": { - "version": "0.19.0-beta.98", - "resolved": "https://registry.npmjs.org/@xterm/addon-webgl/-/addon-webgl-0.19.0-beta.98.tgz", - "integrity": "sha512-09FbNHgN2ad/8JI+AEyg8C3msyk04ET1FihQIpTeWPfd2LJIAdps7G4St2+qzZbhlFkR6m9Dgrgh/AC2uegh8A==", + "version": "0.19.0-beta.99", + "resolved": "https://registry.npmjs.org/@xterm/addon-webgl/-/addon-webgl-0.19.0-beta.99.tgz", + "integrity": "sha512-t4vTtwDLYWgzcH86s3hlCGaZWJWzTXLXUcgw/2l+Fkq9LFy4cLuQgWTVjOWLB0KOJ0FmT+g0sBWLApUw9bYa2w==", "license": "MIT", "peerDependencies": { - "@xterm/xterm": "^5.6.0-beta.98" + "@xterm/xterm": "^5.6.0-beta.99" } }, "node_modules/@xterm/headless": { - "version": "5.6.0-beta.98", - "resolved": "https://registry.npmjs.org/@xterm/headless/-/headless-5.6.0-beta.98.tgz", - "integrity": "sha512-nRl5NyNHajvxNy1N/h0+vEO9tgBgYU8kN/SApBzezGYjcKhs81MYVr5uO1uMKb2eq5eTXBW8BtcyD3KqGoqkEA==", + "version": "5.6.0-beta.99", + "resolved": "https://registry.npmjs.org/@xterm/headless/-/headless-5.6.0-beta.99.tgz", + "integrity": "sha512-E+TR7Cgdb9tHGYw96cexH9l5ghsQEFfw4LaXKxmdlogs43qk2HPwwI7fR/i7t7Ci9ScBXf2gMP76NPpfeX1hZQ==", "license": "MIT" }, "node_modules/@xterm/xterm": { - "version": "5.6.0-beta.98", - "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.6.0-beta.98.tgz", - "integrity": "sha512-fJexj3XKDAMGsR8KKaiEhGrtJD1eRANbT3094E3KSgvbHRa3524tSFvDCx5+5KRE/hYaECmi0knAUIWJCvSPTg==", - "license": "MIT" + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-6.0.0.tgz", + "integrity": "sha512-TQwDdQGtwwDt+2cgKDLn0IRaSxYu1tSUjgKarSDkUM0ZNiSRXFpjxEsvc/Zgc5kq5omJ+V0a8/kIM2WD3sMOYg==", + "license": "MIT", + "workspaces": [ + "addons/*" + ] }, "node_modules/@xtuc/ieee754": { "version": "1.2.0", @@ -3548,29 +4210,35 @@ "dev": true }, "node_modules/abbrev": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", - "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", - "dev": true + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-2.0.0.tgz", + "integrity": "sha512-6/mh1E2u2YgEsCHdY0Yx5oW+61gZU+1vXaoiHHrpKeuRNNgFvS+/jrwHiQhB5apAf5oB7UB7E19ol2R2LKH8hQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" + } }, "node_modules/accepts": { - "version": "1.3.7", - "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", - "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.8.tgz", + "integrity": "sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==", "dev": true, + "license": "MIT", "dependencies": { - "mime-types": "~2.1.24", - "negotiator": "0.6.2" + "mime-types": "~2.1.34", + "negotiator": "0.6.3" }, "engines": { "node": ">= 0.6" } }, "node_modules/acorn": { - "version": "8.12.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", - "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "version": "8.15.0", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.15.0.tgz", + "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, + "license": "MIT", "bin": { "acorn": "bin/acorn" }, @@ -3606,12 +4274,10 @@ } }, "node_modules/agent-base": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", - "dependencies": { - "debug": "^4.3.4" - }, + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", "engines": { "node": ">= 14" } @@ -3637,6 +4303,7 @@ "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", "dev": true, + "license": "MIT", "dependencies": { "ajv": "^8.0.0" }, @@ -3650,15 +4317,16 @@ } }, "node_modules/ajv-formats/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, + "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.1", + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "require-from-string": "^2.0.2" }, "funding": { "type": "github", @@ -3669,7 +4337,8 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/ajv-keywords": { "version": "3.5.2", @@ -3713,8 +4382,9 @@ "node_modules/ansi-gray": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/ansi-gray/-/ansi-gray-0.1.1.tgz", - "integrity": "sha1-KWLPVOyXksSFEKPetSRDaGHvclE= sha512-HrgGIZUl8h2EHuZaU9hTR/cU5nhKxpVE1V6kdGsQ8e4zirElJ5fvtfc8N7Q1oq1aatO275i8pUFUCpNWCAnVWw==", + "integrity": "sha512-HrgGIZUl8h2EHuZaU9hTR/cU5nhKxpVE1V6kdGsQ8e4zirElJ5fvtfc8N7Q1oq1aatO275i8pUFUCpNWCAnVWw==", "dev": true, + "license": "MIT", "dependencies": { "ansi-wrap": "0.1.0" }, @@ -3775,10 +4445,11 @@ "dev": true }, "node_modules/anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, + "license": "ISC", "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -3799,12 +4470,6 @@ "node": ">=0.10.0" } }, - "node_modules/archy": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/archy/-/archy-1.0.0.tgz", - "integrity": "sha1-+cjBN1fMHde8N5rHeyxipcKGjEA= sha512-Xg+9RwCg/0p32teKdGMPTPnVXKD0w3DfHnFTficozsAgsvq2XenPJq/MYpzzQ/v8zrOyJn6Ds39VA4JIDwFfqw==", - "dev": true - }, "node_modules/are-docs-informative": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/are-docs-informative/-/are-docs-informative-0.0.2.tgz", @@ -3835,18 +4500,6 @@ "node": ">=0.10.0" } }, - "node_modules/arr-filter": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/arr-filter/-/arr-filter-1.1.2.tgz", - "integrity": "sha1-Q/3d0JHo7xGqTEXZzcGOLf8XEe4= sha512-A2BETWCqhsecSvCkWAeVBFLH6sXEUGASuzkpjL3GR1SlL/PWL6M3J8EAAld2Uubmh39tvkJTqC9LeLHCUKmFXA==", - "dev": true, - "dependencies": { - "make-iterator": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/arr-flatten": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/arr-flatten/-/arr-flatten-1.1.0.tgz", @@ -3856,18 +4509,6 @@ "node": ">=0.10.0" } }, - "node_modules/arr-map": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/arr-map/-/arr-map-2.0.2.tgz", - "integrity": "sha1-Onc0X/wc814qkYJWAfnljy4kysQ= sha512-tVqVTHt+Q5Xb09qRkbu+DidW1yYzz5izWS2Xm2yFm7qJnmUfz4HPzNxbHkdRJbz2lrqI7S+z17xNYdFcBBO8Hw==", - "dev": true, - "dependencies": { - "make-iterator": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/arr-union": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/arr-union/-/arr-union-3.1.0.tgz", @@ -3878,12 +4519,13 @@ } }, "node_modules/array-back": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", - "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/array-back/-/array-back-6.2.2.tgz", + "integrity": "sha512-gUAZ7HPyb4SJczXAMUXMGAvI976JoK3qEx9v1FTmeYuJj0IBiaKttG1ydtGKdkfqWkIkouke7nG8ufGy77+Cvw==", "dev": true, + "license": "MIT", "engines": { - "node": ">=6" + "node": ">=12.17" } }, "node_modules/array-differ": { @@ -3898,51 +4540,9 @@ "node_modules/array-each": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/array-each/-/array-each-1.0.1.tgz", - "integrity": "sha1-p5SvDAWrF1KEbudTofIRoFugxE8= sha512-zHjL5SZa68hkKHBFBK6DJCTtr9sfTCPCaph/L7tMSLcTFgy+zX7E+6q5UArbtOtMBCtxdICpfTCspRse+ywyXA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/array-initial": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/array-initial/-/array-initial-1.1.0.tgz", - "integrity": "sha1-L6dLJnOTccOUe9enrcc74zSz15U= sha512-BC4Yl89vneCYfpLrs5JU2aAu9/a+xWbeKhvISg9PT7eWFB9UlRvI+rKEtk6mgxWr3dSkk9gQ8hCrdqt06NXPdw==", - "dev": true, - "dependencies": { - "array-slice": "^1.0.0", - "is-number": "^4.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/array-initial/node_modules/is-number": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/array-last": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/array-last/-/array-last-1.3.0.tgz", - "integrity": "sha512-eOCut5rXlI6aCOS7Z7kCplKRKyiFQ6dHFBem4PwlwKeNFk2/XxTrhRh5T9PyaEWGy/NHTZWbY+nsZlNFJu9rYg==", - "dev": true, - "dependencies": { - "is-number": "^4.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/array-last/node_modules/is-number": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-4.0.0.tgz", - "integrity": "sha512-rSklcAIlf1OmFdyAqbnWTLVelsQ58uvZ66S/ZyawjWqIviTWCjg2PzVGw8WUA+nNuPTqb4wgA+NszrJ+08LlgQ==", + "integrity": "sha512-zHjL5SZa68hkKHBFBK6DJCTtr9sfTCPCaph/L7tMSLcTFgy+zX7E+6q5UArbtOtMBCtxdICpfTCspRse+ywyXA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -3952,20 +4552,7 @@ "resolved": "https://registry.npmjs.org/array-slice/-/array-slice-1.1.0.tgz", "integrity": "sha512-B1qMD3RBP7O8o0H2KbrXDyB0IccejMF15+87Lvlor12ONPRHP6gTjXMNkt/d3ZuOGbAe66hFmaCfECI24Ufp6w==", "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/array-sort": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/array-sort/-/array-sort-1.0.0.tgz", - "integrity": "sha512-ihLeJkonmdiAsD7vpgN3CRcx2J2S0TiYW+IS/5zHBI7mKUq3ySvBdzzBfD236ubDBQFiiyG3SWCPc+msQ9KoYg==", - "dev": true, - "dependencies": { - "default-compare": "^1.0.0", - "get-value": "^2.0.6", - "kind-of": "^5.0.2" - }, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -3979,15 +4566,6 @@ "node": ">=0.10.0" } }, - "node_modules/array-unique": { - "version": "0.3.2", - "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz", - "integrity": "sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg= sha512-SleRWjh9JUud2wH1hPs9rZBZ33H6T9HOiL0uwGnGx9FpE6wKGyfWugmbkEOIs6qWrZhg0LWeLziLrEwQJhs5mQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/arrify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/arrify/-/arrify-1.0.1.tgz", @@ -4049,6 +4627,19 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/asar/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/assign-symbols": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/assign-symbols/-/assign-symbols-1.0.0.tgz", @@ -4059,36 +4650,31 @@ } }, "node_modules/async-done": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/async-done/-/async-done-1.3.2.tgz", - "integrity": "sha512-uYkTP8dw2og1tu1nmza1n1CMW0qb8gWWlwqMmLb7MhBVs4BXrFziT6HXUd+/RlRA/i4H9AkofYloUbs1fwMqlw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/async-done/-/async-done-2.0.0.tgz", + "integrity": "sha512-j0s3bzYq9yKIVLKGE/tWlCpa3PfFLcrDZLTSVdnnCTGagXuXBJO4SsY9Xdk/fQBirCkH4evW5xOeJXqlAQFdsw==", "dev": true, + "license": "MIT", "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.2", - "process-nextick-args": "^2.0.0", - "stream-exhaust": "^1.0.1" + "end-of-stream": "^1.4.4", + "once": "^1.4.0", + "stream-exhaust": "^1.0.2" }, "engines": { - "node": ">= 0.10" + "node": ">= 10.13.0" } }, - "node_modules/async-each": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/async-each/-/async-each-1.0.3.tgz", - "integrity": "sha512-z/WhQ5FPySLdvREByI2vZiTWwCnF0moMJ1hK9YQwDTHKh6I7/uSckMetoRGb5UBZPC1z0jlw+n/XCgjeH7y1AQ==", - "dev": true - }, "node_modules/async-settle": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/async-settle/-/async-settle-1.0.0.tgz", - "integrity": "sha1-HQqRS7Aldb7IqPOnTlCA9yssDGs= sha512-VPXfB4Vk49z1LHHodrEQ6Xf7W4gg1w0dAPROHngx7qgDjqmIQ+fXmwgGXTW/ITLai0YLSvWepJOP9EVpMnEAcw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/async-settle/-/async-settle-2.0.0.tgz", + "integrity": "sha512-Obu/KE8FurfQRN6ODdHN9LuXqwC+JFIM9NRyZqJJ4ZfLJmIYN9Rg0/kb+wF70VV5+fJusTMQlJ1t5rF7J/ETdg==", "dev": true, + "license": "MIT", "dependencies": { - "async-done": "^1.2.2" + "async-done": "^2.0.0" }, "engines": { - "node": ">= 0.10" + "node": ">= 10.13.0" } }, "node_modules/asynckit": { @@ -4128,23 +4714,31 @@ "dev": true }, "node_modules/bach": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/bach/-/bach-1.2.0.tgz", - "integrity": "sha1-Szzpa/JxNPeaG0FKUcFONMO9mIA= sha512-bZOOfCb3gXBXbTFXq3OZtGR88LwGeJvzu6szttaIzymOTS4ZttBNOWSv7aLZja2EMycKtRYV0Oa8SNKH/zkxvg==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/bach/-/bach-2.0.1.tgz", + "integrity": "sha512-A7bvGMGiTOxGMpNupYl9HQTf0FFDNF4VCmks4PJpFyN1AX2pdKuxuwdvUz2Hu388wcgp+OvGFNsumBfFNkR7eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "async-done": "^2.0.0", + "async-settle": "^2.0.0", + "now-and-later": "^3.0.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/bach/node_modules/now-and-later": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-3.0.0.tgz", + "integrity": "sha512-pGO4pzSdaxhWTGkfSfHx3hVzJVslFPwBp2Myq9MYN/ChfJZF87ochMAXnvz6/58RJSf5ik2q9tXprBBrk2cpcg==", "dev": true, + "license": "MIT", "dependencies": { - "arr-filter": "^1.1.1", - "arr-flatten": "^1.0.1", - "arr-map": "^2.0.0", - "array-each": "^1.0.0", - "array-initial": "^1.0.0", - "array-last": "^1.1.1", - "async-done": "^1.2.2", - "async-settle": "^1.0.0", - "now-and-later": "^2.0.0" + "once": "^1.4.0" }, "engines": { - "node": ">= 0.10" + "node": ">= 10.13.0" } }, "node_modules/balanced-match": { @@ -4154,79 +4748,80 @@ "dev": true }, "node_modules/bare-events": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.4.2.tgz", - "integrity": "sha512-qMKFd2qG/36aA4GwvKq8MxnPgCQAmBWmSyLWsJcbn8v03wvIPQ/hG1Ms8bPzndZxMDoHpxez5VOS+gC9Yi24/Q==", + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.6.1.tgz", + "integrity": "sha512-AuTJkq9XmE6Vk0FJVNq5QxETrSA/vKHarWVBG5l/JbdCL1prJemiyJqUS0jrlXO0MftuPq4m3YVYhoNc5+aE/g==", "dev": true, + "license": "Apache-2.0", "optional": true }, "node_modules/bare-fs": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-2.3.1.tgz", - "integrity": "sha512-W/Hfxc/6VehXlsgFtbB5B4xFcsCl+pAh30cYhoFyXErf6oGrwjh8SwiPAdHgpmWonKuYpZgGywN0SXt7dgsADA==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/bare-fs/-/bare-fs-4.2.3.tgz", + "integrity": "sha512-1aGs5pRVLToMQ79elP+7cc0u0s/wXAzfBv/7hDloT7WFggLqECCas5qqPky7WHCFdsBH5WDq6sD4fAoz5sJbtA==", "dev": true, + "license": "Apache-2.0", "optional": true, "dependencies": { - "bare-events": "^2.0.0", - "bare-path": "^2.0.0", - "bare-stream": "^2.0.0" + "bare-events": "^2.5.4", + "bare-path": "^3.0.0", + "bare-stream": "^2.6.4" + }, + "engines": { + "bare": ">=1.16.0" + }, + "peerDependencies": { + "bare-buffer": "*" + }, + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + } } }, "node_modules/bare-os": { - "version": "2.4.0", - "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-2.4.0.tgz", - "integrity": "sha512-v8DTT08AS/G0F9xrhyLtepoo9EJBJ85FRSMbu1pQUlAf6A8T0tEEQGMVObWeqpjhSPXsE0VGlluFBJu2fdoTNg==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/bare-os/-/bare-os-3.6.2.tgz", + "integrity": "sha512-T+V1+1srU2qYNBmJCXZkUY5vQ0B4FSlL3QDROnKQYOqeiQR8UbjNHlPa+TIbM4cuidiN9GaTaOZgSEgsvPbh5A==", "dev": true, - "optional": true + "license": "Apache-2.0", + "optional": true, + "engines": { + "bare": ">=1.14.0" + } }, "node_modules/bare-path": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-2.1.3.tgz", - "integrity": "sha512-lh/eITfU8hrj9Ru5quUp0Io1kJWIk1bTjzo7JH1P5dWmQ2EL4hFUlfI8FonAhSlgIfhn63p84CDY/x+PisgcXA==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/bare-path/-/bare-path-3.0.0.tgz", + "integrity": "sha512-tyfW2cQcB5NN8Saijrhqn0Zh7AnFNsnczRcuWODH0eYAXBsJ5gVxAUuNr7tsHSC6IZ77cA0SitzT+s47kot8Mw==", "dev": true, + "license": "Apache-2.0", "optional": true, "dependencies": { - "bare-os": "^2.1.0" + "bare-os": "^3.0.1" } }, "node_modules/bare-stream": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.1.3.tgz", - "integrity": "sha512-tiDAH9H/kP+tvNO5sczyn9ZAA7utrSMobyDchsnyyXBuUe2FSQWbxhtuHB8jwpHYYevVo2UJpcmvvjrbHboUUQ==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/bare-stream/-/bare-stream-2.7.0.tgz", + "integrity": "sha512-oyXQNicV1y8nc2aKffH+BUHFRXmx6VrPzlnaEvMhram0nPBrKcEdcyBg5r08D0i8VxngHFAiVyn1QKXpSG0B8A==", "dev": true, + "license": "Apache-2.0", "optional": true, "dependencies": { - "streamx": "^2.18.0" - } - }, - "node_modules/base": { - "version": "0.11.2", - "resolved": "https://registry.npmjs.org/base/-/base-0.11.2.tgz", - "integrity": "sha512-5T6P4xPgpp0YDFvSWwEZ4NoE3aM4QBQXDzmVbraCkFj8zHM+mba8SyqB5DbZWyR7mYHo6Y7BdQo3MoA4m0TeQg==", - "dev": true, - "dependencies": { - "cache-base": "^1.0.1", - "class-utils": "^0.3.5", - "component-emitter": "^1.2.1", - "define-property": "^1.0.0", - "isobject": "^3.0.1", - "mixin-deep": "^1.2.0", - "pascalcase": "^0.1.1" + "streamx": "^2.21.0" }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/base/node_modules/define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY= sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", - "dev": true, - "dependencies": { - "is-descriptor": "^1.0.0" + "peerDependencies": { + "bare-buffer": "*", + "bare-events": "*" }, - "engines": { - "node": ">=0.10.0" + "peerDependenciesMeta": { + "bare-buffer": { + "optional": true + }, + "bare-events": { + "optional": true + } } }, "node_modules/base64-js": { @@ -4261,10 +4856,11 @@ } }, "node_modules/before-after-hook": { - "version": "2.2.2", - "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-2.2.2.tgz", - "integrity": "sha512-3pZEU3NT5BFUo/AD5ERPWOgQOCZITni6iavr5AUw5AUwQjMlI0kzu5btnyD39AF0gUEsDPwJT+oY1ORBJijPjQ==", - "dev": true + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-4.0.0.tgz", + "integrity": "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==", + "dev": true, + "license": "Apache-2.0" }, "node_modules/big.js": { "version": "5.2.2", @@ -4285,10 +4881,17 @@ } }, "node_modules/binaryextensions": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-1.0.1.tgz", - "integrity": "sha1-HmN0iLNbWL2l9HdL+WpSEqjJB1U= sha512-xnG0l4K3ghM62rFzDi2jcNEuICl6uQ4NgvGpqQsY7HgW8gPDeAWGOxHI/k+qZfXfMANytzrArGNPXidaCwtbmA==", - "dev": true + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-2.3.0.tgz", + "integrity": "sha512-nAihlQsYGyc5Bwq6+EsubvANYGExeJKHDO3RjnvwU042fawQTQfM3Kxn7IHUXQOz4bzfwsGYYHGSvXyW4zOGLg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + }, + "funding": { + "url": "https://bevry.me/fund" + } }, "node_modules/bindings": { "version": "1.5.0", @@ -4334,10 +4937,11 @@ "optional": true }, "node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "version": "1.1.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", + "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -4448,6 +5052,34 @@ "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, + "node_modules/bundle-name": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", + "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", + "license": "MIT", + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/byte-counter": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/byte-counter/-/byte-counter-0.1.0.tgz", + "integrity": "sha512-jheRLVMeUKrDBjVw2O5+k4EvR4t9wtxHL+bo/LxfkxsVeuGMy3a5SEGgXdAFA4FSzTrU8rQXQIrsZ3oBq5a0pQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/bytes": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.2.tgz", @@ -4458,19 +5090,20 @@ } }, "node_modules/c8": { - "version": "9.1.0", - "resolved": "https://registry.npmjs.org/c8/-/c8-9.1.0.tgz", - "integrity": "sha512-mBWcT5iqNir1zIkzSPyI3NCR9EZCVI3WUD+AVO17MVWTSFNyUueXE82qTeampNtTr+ilN/5Ua3j24LgbCKjDVg==", + "version": "10.1.3", + "resolved": "https://registry.npmjs.org/c8/-/c8-10.1.3.tgz", + "integrity": "sha512-LvcyrOAaOnrrlMpW22n690PUvxiq4Uf9WMhQwNJ9vgagkL/ph1+D4uvjvDA5XCbykrc0sx+ay6pVi9YZ1GnhyA==", "dev": true, + "license": "ISC", "dependencies": { - "@bcoe/v8-coverage": "^0.2.3", + "@bcoe/v8-coverage": "^1.0.1", "@istanbuljs/schema": "^0.1.3", "find-up": "^5.0.0", "foreground-child": "^3.1.1", "istanbul-lib-coverage": "^3.2.0", "istanbul-lib-report": "^3.0.1", "istanbul-reports": "^3.1.6", - "test-exclude": "^6.0.0", + "test-exclude": "^7.0.1", "v8-to-istanbul": "^9.0.0", "yargs": "^17.7.2", "yargs-parser": "^21.1.1" @@ -4479,40 +5112,15 @@ "c8": "bin/c8.js" }, "engines": { - "node": ">=14.14.0" - } - }, - "node_modules/cache-base": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-base/-/cache-base-1.0.1.tgz", - "integrity": "sha512-AKcdTnFSWATd5/GCPRxr2ChwIJ85CeyrEyjRHlKxQ56d4XJMGym0uAiKn0xbLOGOl3+yRpOTi484dVCEc5AUzQ==", - "dev": true, - "dependencies": { - "collection-visit": "^1.0.0", - "component-emitter": "^1.2.1", - "get-value": "^2.0.6", - "has-value": "^1.0.0", - "isobject": "^3.0.1", - "set-value": "^2.0.0", - "to-object-path": "^0.3.0", - "union-value": "^1.0.0", - "unset-value": "^1.0.0" + "node": ">=18" }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/cache-content-type": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/cache-content-type/-/cache-content-type-1.0.1.tgz", - "integrity": "sha512-IKufZ1o4Ut42YUrZSo8+qnMTrFuKkvyoLXUywKz9GJ5BrhOFGhLdkx9sG4KAnVvbY6kEcSFjLQul+DVmBm2bgA==", - "dev": true, - "dependencies": { - "mime-types": "^2.1.18", - "ylru": "^1.2.0" + "peerDependencies": { + "monocart-coverage-reports": "^2" }, - "engines": { - "node": ">= 6.0.0" + "peerDependenciesMeta": { + "monocart-coverage-reports": { + "optional": true + } } }, "node_modules/cacheable-lookup": { @@ -4561,11 +5169,26 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -4663,16 +5286,11 @@ } }, "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], + "license": "MIT", "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -4685,6 +5303,9 @@ "engines": { "node": ">= 8.10.0" }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, "optionalDependencies": { "fsevents": "~2.3.2" } @@ -4758,103 +5379,6 @@ "integrity": "sha1-BKEGZywYsIWrd02YPfo+oTjyIgU= sha512-1R5Fho+jBq0DDydt+/vHWj5KJNJCKdARKOCwZUen84I5BreWoLqRLANH1U87eJy1tiASPtMnGqJJq0ZsLoRPOw==", "dev": true }, - "node_modules/ci-info": { - "version": "1.6.0", - "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-1.6.0.tgz", - "integrity": "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A==", - "dev": true - }, - "node_modules/class-utils": { - "version": "0.3.6", - "resolved": "https://registry.npmjs.org/class-utils/-/class-utils-0.3.6.tgz", - "integrity": "sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==", - "dev": true, - "dependencies": { - "arr-union": "^3.1.0", - "define-property": "^0.2.5", - "isobject": "^3.0.0", - "static-extend": "^0.1.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", - "dev": true, - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", - "deprecated": "Please upgrade to v0.1.7", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/is-accessor-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", - "deprecated": "Please upgrade to v0.1.5", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/is-data-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/class-utils/node_modules/is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/cli-cursor": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-4.0.0.tgz", @@ -4939,6 +5463,7 @@ "resolved": "https://registry.npmjs.org/clone-deep/-/clone-deep-4.0.1.tgz", "integrity": "sha512-neHB9xuzh/wk0dIHweyAXv2aPGZIVk3pLMe+/RNzINf17fe0OG96QroktYAUm7SM1PBnzTabaLboqqxDyMU+SQ==", "dev": true, + "license": "MIT", "dependencies": { "is-plain-object": "^2.0.4", "kind-of": "^6.0.2", @@ -4953,6 +5478,7 @@ "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", "dev": true, + "license": "MIT", "dependencies": { "isobject": "^3.0.1" }, @@ -4960,15 +5486,6 @@ "node": ">=0.10.0" } }, - "node_modules/clone-deep/node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/clone-response": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", @@ -4995,73 +5512,28 @@ "readable-stream": "^2.3.5" } }, - "node_modules/cloneable-readable/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/co": { - "version": "4.6.0", - "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", - "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ= sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", - "dev": true, - "engines": { - "iojs": ">= 1.0.0", - "node": ">= 0.12.0" - } - }, - "node_modules/code-block-writer": { - "version": "12.0.0", - "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-12.0.0.tgz", - "integrity": "sha512-q4dMFMlXtKR3XNBHyMHt/3pwYNA69EDk00lloMOaaUMKPUXBw6lpXtbu3MMVG6/uOihGnRDOlkyqsONEUj60+w==", - "dev": true - }, - "node_modules/code-point-at": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/code-point-at/-/code-point-at-1.1.0.tgz", - "integrity": "sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c= sha512-RpAVKQA5T63xEj6/giIbUEtZwJ4UFIc3ZtvEkiaUERylqe8xb5IvqcgOurZLahv93CLKfxcw5YI+DZcUBRyLXA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/collection-map": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-map/-/collection-map-1.0.0.tgz", - "integrity": "sha1-rqDwb40mx4DCt1SUOFVEsiVa8Yw= sha512-5D2XXSpkOnleOI21TG7p3T0bGAsZ/XknZpKBmGYyluO8pw4zA3K8ZlrBIbC4FXg3m6z/RNFiUFfT2sQK01+UHA==", - "dev": true, - "dependencies": { - "arr-map": "^2.0.2", - "for-own": "^1.0.0", - "make-iterator": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/collection-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz", - "integrity": "sha1-S8A3PBZLwykbTTaMgpzxqApZ3KA= sha512-lNkKvzEeMBBjUGHZ+q6z9pSJla0KWAQPvtzhEV9+iGyQYG+pBpl7xKDhxoNSOZH2hhv0v5k0y2yAM4o4SjoSkw==", + "node_modules/cloneable-readable/node_modules/readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, "dependencies": { - "map-visit": "^1.0.0", - "object-visit": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" } }, + "node_modules/code-block-writer": { + "version": "13.0.3", + "resolved": "https://registry.npmjs.org/code-block-writer/-/code-block-writer-13.0.3.tgz", + "integrity": "sha512-Oofo0pq3IKnsFtuHqSF7TqBfr71aeyZDVJ0HpmqB7FBM2qEigL0iPONSCZSO9pE9dZTAxANe5XHG9Uy0YMv8cg==", + "dev": true, + "license": "MIT" + }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", @@ -5124,18 +5596,27 @@ } }, "node_modules/command-line-args": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.2.1.tgz", - "integrity": "sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-6.0.1.tgz", + "integrity": "sha512-Jr3eByUjqyK0qd8W0SGFW1nZwqCaNCtbXjRo2cRJC1OYxWl3MZ5t1US3jq+cO4sPavqgw4l9BMGX0CBe+trepg==", "dev": true, + "license": "MIT", "dependencies": { - "array-back": "^3.1.0", - "find-replace": "^3.0.0", + "array-back": "^6.2.2", + "find-replace": "^5.0.2", "lodash.camelcase": "^4.3.0", - "typical": "^4.0.0" + "typical": "^7.2.0" }, "engines": { - "node": ">=4.0.0" + "node": ">=12.20" + }, + "peerDependencies": { + "@75lb/nature": "latest" + }, + "peerDependenciesMeta": { + "@75lb/nature": { + "optional": true + } } }, "node_modules/commander": { @@ -5152,57 +5633,24 @@ "resolved": "https://registry.npmjs.org/comment-parser/-/comment-parser-1.4.1.tgz", "integrity": "sha512-buhp5kePrmda3vhc5B9t7pUQXAb2Tnd0qgpkIhPhkHXxJpiPJ11H0ZEU0oBpJ2QztSbzG/ZxMj/CHsYJqRHmyg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 12.0.0" } }, - "node_modules/component-emitter": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/component-emitter/-/component-emitter-1.3.0.tgz", - "integrity": "sha512-Rd3se6QB+sO1TwqZjscQrurpEPIfO0/yYnSin6Q/rD3mOutHvUrCAhJub3r90uNb+SESBuE0QYoB90YdfatsRg==", - "dev": true - }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", - "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s= sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", - "dev": true - }, - "node_modules/concat-stream": { - "version": "1.6.2", - "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", - "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", - "dev": true, - "engines": [ - "node >= 0.8" - ], - "dependencies": { - "buffer-from": "^1.0.0", - "inherits": "^2.0.3", - "readable-stream": "^2.2.2", - "typedarray": "^0.0.6" - } - }, - "node_modules/concat-stream/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } + "license": "MIT" }, "node_modules/config-chain": { - "version": "1.1.12", - "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.12.tgz", - "integrity": "sha512-a1eOIcu8+7lUInge4Rpf/n4Krkf3Dd9lqhljRzII1/Zno/kRtUWnznPO3jOKBmTEktkt3fkxisUcivoj0ebzoA==", + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/config-chain/-/config-chain-1.1.13.tgz", + "integrity": "sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==", "dev": true, + "license": "MIT", "dependencies": { "ini": "^1.3.4", "proto-list": "~1.2.1" @@ -5213,6 +5661,7 @@ "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.4.tgz", "integrity": "sha512-FveZTNuGw04cxlAiWbzi6zTAL/lhehaWbTtgluJh4/E95DqMwTmha3KZN1aAWA8cFIhHzMZUvLevkw5Rqk+tSQ==", "dev": true, + "license": "MIT", "dependencies": { "safe-buffer": "5.2.1" }, @@ -5238,13 +5687,15 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/content-type": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.5.tgz", "integrity": "sha512-nTjqfcBFEipKdXCv4YDQWCfmcLZKm81ldF0pAopTvyrFGVbcR6P/VAAd5G7N+0tTr8QqiU0tFadD6FK4NtJwOA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -5259,12 +5710,17 @@ } }, "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", "dev": true, + "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/cookies": { @@ -5272,6 +5728,7 @@ "resolved": "https://registry.npmjs.org/cookies/-/cookies-0.9.1.tgz", "integrity": "sha512-TG2hpqe4ELx54QER/S3HQ9SRVnQnGBtKUz5bLQWtYAQ+o6GpgMs6sYUvaiJjVxb+UXwhRhAEP3m7LbsIZ77Hmw==", "dev": true, + "license": "MIT", "dependencies": { "depd": "~2.0.0", "keygrip": "~1.1.0" @@ -5280,40 +5737,35 @@ "node": ">= 0.8" } }, - "node_modules/copy-descriptor": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/copy-descriptor/-/copy-descriptor-0.1.1.tgz", - "integrity": "sha1-Z29us8OZl8LuGsOpJP1hJHSPV40= sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/copy-props": { - "version": "2.0.5", - "resolved": "https://registry.npmjs.org/copy-props/-/copy-props-2.0.5.tgz", - "integrity": "sha512-XBlx8HSqrT0ObQwmSzM7WE5k8FxTV75h1DX1Z3n6NhQ/UYYAvInWYmG06vFt7hQZArE2fuO62aihiWIVQwh1sw==", + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/copy-props/-/copy-props-4.0.0.tgz", + "integrity": "sha512-bVWtw1wQLzzKiYROtvNlbJgxgBYt2bMJpkCbKmXM3xyijvcjjWXEk5nyrrT3bgJ7ODb19ZohE2T0Y3FgNPyoTw==", "dev": true, + "license": "MIT", "dependencies": { - "each-props": "^1.3.2", + "each-props": "^3.0.0", "is-plain-object": "^5.0.0" + }, + "engines": { + "node": ">= 10.13.0" } }, "node_modules/copy-webpack-plugin": { - "version": "11.0.0", - "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-11.0.0.tgz", - "integrity": "sha512-fX2MWpamkW0hZxMEg0+mYnA40LTosOSa5TqZ9GYIBzyJa9C3QUaMPSE2xAi/buNr8u89SfD9wHSQVBzrRa/SOQ==", + "version": "13.0.1", + "resolved": "https://registry.npmjs.org/copy-webpack-plugin/-/copy-webpack-plugin-13.0.1.tgz", + "integrity": "sha512-J+YV3WfhY6W/Xf9h+J1znYuqTye2xkBUIGyTPWuBAT27qajBa5mR4f8WBmfDY3YjRftT2kqZZiLi1qf0H+UOFw==", "dev": true, + "license": "MIT", "dependencies": { - "fast-glob": "^3.2.11", "glob-parent": "^6.0.1", - "globby": "^13.1.1", "normalize-path": "^3.0.0", - "schema-utils": "^4.0.0", - "serialize-javascript": "^6.0.0" + "schema-utils": "^4.2.0", + "serialize-javascript": "^6.0.2", + "tinyglobby": "^0.2.12" }, "engines": { - "node": ">= 14.15.0" + "node": ">= 18.12.0" }, "funding": { "type": "opencollective", @@ -5335,37 +5787,6 @@ "node": ">=10.13.0" } }, - "node_modules/copy-webpack-plugin/node_modules/globby": { - "version": "13.1.3", - "resolved": "https://registry.npmjs.org/globby/-/globby-13.1.3.tgz", - "integrity": "sha512-8krCNHXvlCgHDpegPzleMq07yMYTO2sXKASmZmquEYWEmCx6J5UTRbp5RwMJkTJGtcQ44YpiUYUiN0b9mzy8Bw==", - "dev": true, - "dependencies": { - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.11", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^4.0.0" - }, - "engines": { - "node": "^12.20.0 || ^14.13.1 || >=16.0.0" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/copy-webpack-plugin/node_modules/slash": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-4.0.0.tgz", - "integrity": "sha512-3dOsAHXXUkQTpOYcoAxLIorMTp4gIQr5IW3iVb7A7lFIp0VHhnynm9izx6TssdrIcVIESAlVjtnO2K8bg+Coew==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/core-util-is": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", @@ -5392,6 +5813,31 @@ "node": ">= 8" } }, + "node_modules/cross-spawn-windows-exe": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/cross-spawn-windows-exe/-/cross-spawn-windows-exe-1.2.0.tgz", + "integrity": "sha512-mkLtJJcYbDCxEG7Js6eUnUNndWjyUZwJ3H7bErmmtOYU/Zb99DyUkpamuIZE0b3bhmJyZ7D90uS6f+CGxRRjOw==", + "dev": true, + "funding": [ + { + "type": "individual", + "url": "https://github.com/sponsors/malept" + }, + { + "type": "tidelift", + "url": "https://tidelift.com/subscription/pkg/npm-cross-spawn-windows-exe?utm_medium=referral&utm_source=npm_fund" + } + ], + "license": "Apache-2.0", + "dependencies": { + "@malept/cross-spawn-promise": "^1.1.0", + "is-wsl": "^2.2.0", + "which": "^2.0.2" + }, + "engines": { + "node": ">= 10" + } + }, "node_modules/crypt": { "version": "0.0.2", "resolved": "https://registry.npmjs.org/crypt/-/crypt-0.0.2.tgz", @@ -5426,29 +5872,39 @@ } }, "node_modules/css-loader": { - "version": "6.9.1", - "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-6.9.1.tgz", - "integrity": "sha512-OzABOh0+26JKFdMzlK6PY1u5Zx8+Ck7CVRlcGNZoY9qwJjdfu2VWFuprTIpPW+Av5TZTVViYWcFQaEEQURLknQ==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/css-loader/-/css-loader-7.1.2.tgz", + "integrity": "sha512-6WvYYn7l/XEGN8Xu2vWFt9nVzrCn39vKyTEFf/ExEyoksJjjSZV/0/35XPlMbpnr6VGhZIUg5yJrL8tGfes/FA==", "dev": true, + "license": "MIT", "dependencies": { "icss-utils": "^5.1.0", "postcss": "^8.4.33", - "postcss-modules-extract-imports": "^3.0.0", - "postcss-modules-local-by-default": "^4.0.4", - "postcss-modules-scope": "^3.1.1", + "postcss-modules-extract-imports": "^3.1.0", + "postcss-modules-local-by-default": "^4.0.5", + "postcss-modules-scope": "^3.2.0", "postcss-modules-values": "^4.0.0", "postcss-value-parser": "^4.2.0", "semver": "^7.5.4" }, "engines": { - "node": ">= 12.13.0" + "node": ">= 18.12.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/webpack" }, "peerDependencies": { - "webpack": "^5.0.0" + "@rspack/core": "0.x || 1.x", + "webpack": "^5.27.0" + }, + "peerDependenciesMeta": { + "@rspack/core": { + "optional": true + }, + "webpack": { + "optional": true + } } }, "node_modules/css-select": { @@ -5480,6 +5936,16 @@ "node": ">=8.0.0" } }, + "node_modules/css-tree/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/css-what": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.1.0.tgz", @@ -5492,6 +5958,16 @@ "url": "https://github.com/sponsors/fb55" } }, + "node_modules/css/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/cssesc": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/cssesc/-/cssesc-3.0.0.tgz", @@ -5606,15 +6082,22 @@ } }, "node_modules/debounce": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/debounce/-/debounce-1.1.0.tgz", - "integrity": "sha512-ZQVKfRVlwRfD150ndzEK8M90ABT+Y/JQKs4Y7U4MXdpuoUkkrr4DwKbVux3YjylA5bUMUj0Nc3pMxPJX6N2QQQ==", - "dev": true + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/debounce/-/debounce-3.0.0.tgz", + "integrity": "sha512-64byRbF0/AirwbuHqB3/ZpMG9/nckDa6ZA0yd6UnaQNwbbemCOwvz2sL5sjXLHhZHADyiwLm0M5qMhltUUx+TA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, "node_modules/debug": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.0.tgz", - "integrity": "sha512-6WTZ/IxCY/T6BALoZHaE4ctp9xm+Z5kY/pzYaCHRFeyVhojxlrm+46y68HA6hr0TcwEssoxNiDEUJQjfPZ/RYA==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "license": "MIT", "dependencies": { "ms": "^2.1.3" @@ -5695,10 +6178,11 @@ } }, "node_modules/deemon": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/deemon/-/deemon-1.8.0.tgz", - "integrity": "sha512-qcuSMls/W5DdoEKKAF0PiNQrc8+tItFjvszfjNm1YqNv1p5wwEt+6qILA9sws6eM81nmNwD38ducqlgIXzQlsQ==", + "version": "1.13.6", + "resolved": "https://registry.npmjs.org/deemon/-/deemon-1.13.6.tgz", + "integrity": "sha512-+/fizwuGaBl+DwvFgP5I0+cYXCPHrjCBzFegm2xIcVmkC2sPTxK5KRwIVtyY0kIngoqwf9bENDkFEpfjMr0H2g==", "dev": true, + "license": "MIT", "dependencies": { "bl": "^4.0.2", "tree-kill": "^1.2.2" @@ -5707,14 +6191,15 @@ "deemon": "src/deemon.js" }, "engines": { - "node": ">=10" + "node": ">=22" } }, "node_modules/deep-equal": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.0.1.tgz", - "integrity": "sha1-9dJgKStmDghO/0zbyfCK0yR0SLU= sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw==", - "dev": true + "integrity": "sha512-bHtC0iYvWhyaTzvV3CZgPeZQqCOBGyGsVV7v4eevpdkLHfiSrXUdBG+qAuSz4RI70sszvjQ1QSZ98An1yNwpSw==", + "dev": true, + "license": "MIT" }, "node_modules/deep-extend": { "version": "0.6.0", @@ -5731,10 +6216,11 @@ "dev": true }, "node_modules/deepmerge": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-3.1.0.tgz", - "integrity": "sha512-/TnecbwXEdycfbsM2++O3eGiatEFHjjNciHEwJclM+T5Kd94qD1AP+2elP/Mq0L5b9VZJao5znR01Mz6eX8Seg==", + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", + "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -5748,25 +6234,32 @@ "node": ">=4.0.0" } }, - "node_modules/default-compare": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/default-compare/-/default-compare-1.0.0.tgz", - "integrity": "sha512-QWfXlM0EkAbqOCbD/6HjdwT19j7WCkMyiRhWilc4H9/5h/RzTF9gv5LYh1+CmDV5d1rki6KAWLtQale0xt20eQ==", - "dev": true, + "node_modules/default-browser": { + "version": "5.4.0", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.4.0.tgz", + "integrity": "sha512-XDuvSq38Hr1MdN47EDvYtx3U0MTqpCEn+F6ft8z2vYDzMrvQhVp0ui9oQdqW3MvK3vqUETglt1tVGgjLuJ5izg==", + "license": "MIT", "dependencies": { - "kind-of": "^5.0.2" + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/default-resolution": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/default-resolution/-/default-resolution-2.0.0.tgz", - "integrity": "sha1-vLgrqnKtebQmp2cy8aga1t8m1oQ= sha512-2xaP6GiwVwOEbXCGoJ4ufgC76m8cj805jrghScewJC2ZDsb9U0b4BIrba+xt/Uytyd0HvQ6+WymSRTfnYj59GQ==", - "dev": true, + "node_modules/default-browser-id": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", + "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", + "license": "MIT", "engines": { - "node": ">= 0.10" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/defer-to-connect": { @@ -5796,11 +6289,15 @@ } }, "node_modules/define-lazy-prop": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-2.0.0.tgz", - "integrity": "sha512-Ds09qNh8yw3khSjiJjiUInaGX9xlqZDY7JVryGxdxV7NPeuqQfplOpQ66yJFZut3jLa5zOwkXw1g9EI2uKh4Og==", + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/define-properties": { @@ -5815,19 +6312,6 @@ "node": ">= 0.4" } }, - "node_modules/define-property": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-2.0.2.tgz", - "integrity": "sha512-jwK2UV4cnPpbcG7+VRARKTZPUWowwXA8bzH5NP6ud0oeAxyYPuGZUAC7hMugpCdz4BeSZl2Dl9k66CHJ/46ZYQ==", - "dev": true, - "dependencies": { - "is-descriptor": "^1.0.2", - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/delayed-stream": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-0.0.6.tgz", @@ -5840,8 +6324,9 @@ "node_modules/delegates": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/delegates/-/delegates-1.0.0.tgz", - "integrity": "sha1-hMbhWbgZBP3KWaDvRM2HDTElD5o= sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", - "dev": true + "integrity": "sha512-bd2L678uiWATM6m5Z1VzNCErI3jiGzt6HGY8OVICs40JQq/HALfbyNJmp0UDakEY4pMMaN0Ly5om/B1VI/+xfQ==", + "dev": true, + "license": "MIT" }, "node_modules/depd": { "version": "2.0.0", @@ -5852,17 +6337,12 @@ "node": ">= 0.8" } }, - "node_modules/deprecation": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/deprecation/-/deprecation-2.3.1.tgz", - "integrity": "sha512-xmHIy4F3scKVwMsQ4WnVaS8bHOx0DmVwRywosKhaILI0ywMDWPtBSku2HNxRvF7jtwDRsoEwYQSfbxj8b7RlJQ==", - "dev": true - }, "node_modules/destroy": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.2.0.tgz", "integrity": "sha512-2sJGJTaXIIaR1w4iJSNoN0hnMY7Gpc/n8D4qSCJw8QqFWXf7cuAgnEHxBpweaVcPevC2l3KpjYCx3NypQQgaJg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8", "npm": "1.2.8000 || >= 1.4.16" @@ -5871,25 +6351,28 @@ "node_modules/detect-file": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/detect-file/-/detect-file-1.0.0.tgz", - "integrity": "sha1-8NZtA2cqglyxtzvbP+YjEMjlUrc= sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==", + "integrity": "sha512-DtCOLG98P007x7wiiOmfI0fi3eIKyWiLTGJ2MDnVi/E04lWGbf+JzrRHMm0rgIIZJGtHpKpbVgLWHrv8xXpc3Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/detect-indent": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-5.0.0.tgz", - "integrity": "sha1-OHHMCmoALow+Wzz38zYmRnXwa50= sha512-rlpvsxUtM0PQvy9iZe640/IWwWYyBsTApREbA1pHOpmOUIl9MkP/U4z7vTtg4Oaojvqhxt7sdufnT0EzGaR31g==", + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-6.1.0.tgz", + "integrity": "sha512-reYkTUJAZb9gUuZ2RvVCNhVHdg62RHnJ7WJl8ftMi4diZ6NWlciOzQN88pUhSELEwflJht4oQDv0F0BMlwaYtA==", "dev": true, + "license": "MIT", "engines": { - "node": ">=4" + "node": ">=8" } }, "node_modules/detect-libc": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", - "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", "engines": { "node": ">=8" } @@ -5911,27 +6394,15 @@ "optional": true }, "node_modules/diff": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", - "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" } }, - "node_modules/dir-glob": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", - "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", - "dev": true, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/dom-serializer": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", @@ -5987,11 +6458,27 @@ "url": "https://github.com/fb55/domutils?sponsor=1" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/duplexer": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.1.tgz", - "integrity": "sha1-rOb/gIwc5mtX0ev5eXessCM0z8E= sha512-sxNZ+ljy+RA1maXoUReeqBBpBC6RLKmg5ewzV+x+mSETmWNoKdZN6vcQjpFROemza23hGFskJtFNoUWUaQ+R4Q==", - "dev": true + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/duplexer/-/duplexer-0.1.2.tgz", + "integrity": "sha512-jtD6YG370ZCIi/9GTaJKQxWTZD045+4R4hTk/x1UyoqadyJ9x9CgSi1RlVDQF8U2sxLLSnFkCaMihqljHIWgMg==", + "dev": true, + "license": "MIT" }, "node_modules/duplexify": { "version": "3.7.1", @@ -6019,27 +6506,19 @@ "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" } - }, - "node_modules/each-props": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/each-props/-/each-props-1.3.2.tgz", - "integrity": "sha512-vV0Hem3zAGkJAyU7JSjixeU66rwdynTAa1vofCrSA5fEln+m67Az9CcnkVD776/fsN/UjIWmBDoNRS6t6G9RfA==", - "dev": true, - "dependencies": { - "is-plain-object": "^2.0.1", - "object.defaults": "^1.1.0" - } - }, - "node_modules/each-props/node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + }, + "node_modules/each-props": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/each-props/-/each-props-3.0.0.tgz", + "integrity": "sha512-IYf1hpuWrdzse/s/YJOrFmU15lyhSzxelNVAHTEG3DtP4QsLTWZUzcUL3HMXmKQxXpa4EIrBPpwRgj0aehdvAw==", "dev": true, + "license": "MIT", "dependencies": { - "isobject": "^3.0.1" + "is-plain-object": "^5.0.0", + "object.defaults": "^1.1.0" }, "engines": { - "node": ">=0.10.0" + "node": ">= 10.13.0" } }, "node_modules/eastasianwidth": { @@ -6048,48 +6527,72 @@ "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", "dev": true }, + "node_modules/easy-transform-stream": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/easy-transform-stream/-/easy-transform-stream-1.0.1.tgz", + "integrity": "sha512-ktkaa6XR7COAR3oj02CF3IOgz2m1hCaY3SfzvKT4Svt2MhHw9XCt+ncJNWfe2TGz31iqzNGZ8spdKQflj+Rlog==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/editorconfig": { - "version": "0.15.2", - "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-0.15.2.tgz", - "integrity": "sha512-GWjSI19PVJAM9IZRGOS+YKI8LN+/sjkSjNyvxL5ucqP9/IqtYNXBaQ/6c/hkPNYQHyOHra2KoXZI/JVpuqwmcQ==", + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/editorconfig/-/editorconfig-1.0.4.tgz", + "integrity": "sha512-L9Qe08KWTlqYMVvMcTIvMAdl1cDUubzRNYL+WfA4bLDMHe4nemKkpmYzkznE1FwLKu0EEmy6obgQKzMJrg4x9Q==", "dev": true, + "license": "MIT", "dependencies": { - "@types/node": "^10.11.7", - "@types/semver": "^5.5.0", - "commander": "^2.19.0", - "lru-cache": "^4.1.3", - "semver": "^5.6.0", - "sigmund": "^1.0.1" + "@one-ini/wasm": "0.1.1", + "commander": "^10.0.0", + "minimatch": "9.0.1", + "semver": "^7.5.3" }, "bin": { "editorconfig": "bin/editorconfig" + }, + "engines": { + "node": ">=14" } }, - "node_modules/editorconfig/node_modules/@types/node": { - "version": "10.12.21", - "resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.21.tgz", - "integrity": "sha512-CBgLNk4o3XMnqMc0rhb6lc77IwShMEglz05deDcn2lQxyXEZivfwgYJu7SMha9V5XcrP6qZuevTHV/QrN2vjKQ==", - "dev": true - }, - "node_modules/editorconfig/node_modules/@types/semver": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/@types/semver/-/semver-5.5.0.tgz", - "integrity": "sha512-41qEJgBH/TWgo5NFSvBCJ1qkoi3Q6ONSF2avrHq1LVEZfYpdHmj0y9SuTK+u9ZhG1sYQKBL1AWXKyLWP4RaUoQ==", - "dev": true + "node_modules/editorconfig/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } }, "node_modules/editorconfig/node_modules/commander": { - "version": "2.20.3", - "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", - "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", - "dev": true + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", + "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" + } }, - "node_modules/editorconfig/node_modules/semver": { - "version": "5.7.2", - "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", - "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "node_modules/editorconfig/node_modules/minimatch": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.1.tgz", + "integrity": "sha512-0jWhJpD/MdhPXwPuiRkCbfYfSKp2qnn2eOc279qI7f+osl/l+prKSrvhg157zSYvx/1nmgn2NqdT6k2Z7zSH9w==", "dev": true, - "bin": { - "semver": "bin/semver" + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/ee-first": { @@ -6099,15 +6602,15 @@ "dev": true }, "node_modules/electron": { - "version": "34.1.0", - "resolved": "https://registry.npmjs.org/electron/-/electron-34.1.0.tgz", - "integrity": "sha512-ZUid8XrGPA0dfes97PPADc8ecWOUX/qYRNp1glze9coZLEYc+PsMvgjVDCHSvjfHfiI+V3unwngSVpBouX71YQ==", + "version": "40.0.0", + "resolved": "https://registry.npmjs.org/electron/-/electron-40.0.0.tgz", + "integrity": "sha512-UyBy5yJ0/wm4gNugCtNPjvddjAknMTuXR2aCHioXicH7aKRKGDBPp4xqTEi/doVcB3R+MN3wfU9o8d/9pwgK2A==", "dev": true, "hasInstallScript": true, "license": "MIT", "dependencies": { "@electron/get": "^2.0.0", - "@types/node": "^20.9.0", + "@types/node": "^24.9.0", "extract-zip": "^2.0.1" }, "bin": { @@ -6124,6 +6627,16 @@ "dev": true, "license": "ISC" }, + "node_modules/electron/node_modules/@types/node": { + "version": "24.10.9", + "resolved": "https://registry.npmjs.org/@types/node/-/node-24.10.9.tgz", + "integrity": "sha512-ne4A0IpG3+2ETuREInjPNhUGis1SFjv1d5asp8MzEAGtOZeTeHVDOYqOgqfhvseqg/iXty2hjBf1zAOb7RNiNw==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~7.16.0" + } + }, "node_modules/emoji-regex": { "version": "10.3.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-10.3.0.tgz", @@ -6140,10 +6653,11 @@ } }, "node_modules/encodeurl": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", - "integrity": "sha512-TPJXq8JqFaVYm2CWmPvnP2Iyo4ZSM7/QKcSmuMLDObfpH5fi7RUGmd/rTDf+rut/saiDiQEeVTNgAmJEdAOx0w==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", + "integrity": "sha512-Q0n9HRi4m6JuGIV1eFlmvJB7ZEVxu93IrMyiMsGC0lrMJMWzRgx6WGquyfQgZVb31vhGgXnfmPNNXmxnOkRBrg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -6157,10 +6671,11 @@ } }, "node_modules/enhanced-resolve": { - "version": "5.17.1", - "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.17.1.tgz", - "integrity": "sha512-LMHl3dXhTcfv8gM4kEzIUeTQ+7fpdA0l2tUf34BddXPkz2A5xJ5L/Pchd5BL6rdccM9QGvu0sWZzK1Z1t4wwyg==", + "version": "5.18.4", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.18.4.tgz", + "integrity": "sha512-LgQMM4WXU3QI+SYgEc2liRgznaD5ojbmY3sb8LxyguVkIg5FxdpTkvk72te2R38/TGKxH634oLxXRGY6d7AP+Q==", "dev": true, + "license": "MIT", "dependencies": { "graceful-fs": "^4.2.4", "tapable": "^2.2.0" @@ -6191,10 +6706,11 @@ } }, "node_modules/envinfo": { - "version": "7.8.1", - "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.8.1.tgz", - "integrity": "sha512-/o+BXHmB7ocbHEAs6F2EnG0ogybVVUdkRunTT2glZU9XAaGmhqskrvKwqXuDfNjEO0LZKWdejEEpnq8aM0tOaw==", + "version": "7.21.0", + "resolved": "https://registry.npmjs.org/envinfo/-/envinfo-7.21.0.tgz", + "integrity": "sha512-Lw7I8Zp5YKHFCXL7+Dz95g4CcbMEpgvqZNNq3AmlT5XAV6CgAAk6gyAMqn2zjw08K9BHfcNuKrMiCPLByGafow==", "dev": true, + "license": "MIT", "bin": { "envinfo": "dist/cli.js" }, @@ -6250,13 +6766,11 @@ } }, "node_modules/es-define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.0.tgz", - "integrity": "sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", "dev": true, - "dependencies": { - "get-intrinsic": "^1.2.4" - }, + "license": "MIT", "engines": { "node": ">= 0.4" } @@ -6276,6 +6790,35 @@ "integrity": "sha512-MVNK56NiMrOwitFB7cqDwq0CQutbw+0BvLshJSse0MUNU+y1FC3bUS/AQg7oUng+/wKrrki7JfmwtVHkVfPLlw==", "dev": true }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/es-to-primitive": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", @@ -6363,7 +6906,8 @@ "version": "1.0.3", "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", "integrity": "sha512-NiSupZ4OeuGwr68lGIeym/ksIZMJodUGOSCZ/FSnTxcrekbvqrgdUxlJOMpijaKZVjAJrWrGs/6Jy8OMuyj9ow==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/escape-string-regexp": { "version": "4.0.0", @@ -6378,31 +6922,32 @@ } }, "node_modules/eslint": { - "version": "9.11.1", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.11.1.tgz", - "integrity": "sha512-MobhYKIoAO1s1e4VUrgx1l1Sk2JBR/Gqjjgw8+mfgoLE2xwsHur4gdfTxyTgShrhvdVFTaJSgMiQBl1jv/AWxg==", - "dev": true, - "dependencies": { - "@eslint-community/eslint-utils": "^4.2.0", - "@eslint-community/regexpp": "^4.11.0", - "@eslint/config-array": "^0.18.0", - "@eslint/core": "^0.6.0", - "@eslint/eslintrc": "^3.1.0", - "@eslint/js": "9.11.1", - "@eslint/plugin-kit": "^0.2.0", + "version": "9.39.2", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.39.2.tgz", + "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.8.0", + "@eslint-community/regexpp": "^4.12.1", + "@eslint/config-array": "^0.21.1", + "@eslint/config-helpers": "^0.4.2", + "@eslint/core": "^0.17.0", + "@eslint/eslintrc": "^3.3.1", + "@eslint/js": "9.39.2", + "@eslint/plugin-kit": "^0.4.1", + "@humanfs/node": "^0.16.6", "@humanwhocodes/module-importer": "^1.0.1", - "@humanwhocodes/retry": "^0.3.0", - "@nodelib/fs.walk": "^1.2.8", + "@humanwhocodes/retry": "^0.4.2", "@types/estree": "^1.0.6", - "@types/json-schema": "^7.0.15", "ajv": "^6.12.4", "chalk": "^4.0.0", - "cross-spawn": "^7.0.2", + "cross-spawn": "^7.0.6", "debug": "^4.3.2", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^8.0.2", - "eslint-visitor-keys": "^4.0.0", - "espree": "^10.1.0", + "eslint-scope": "^8.4.0", + "eslint-visitor-keys": "^4.2.1", + "espree": "^10.4.0", "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", @@ -6412,14 +6957,11 @@ "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "is-path-inside": "^3.0.3", "json-stable-stringify-without-jsonify": "^1.0.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.3", - "strip-ansi": "^6.0.1", - "text-table": "^0.2.0" + "optionator": "^0.9.3" }, "bin": { "eslint": "bin/eslint.js" @@ -6440,12 +6982,13 @@ } }, "node_modules/eslint-formatter-compact": { - "version": "8.40.0", - "resolved": "https://registry.npmjs.org/eslint-formatter-compact/-/eslint-formatter-compact-8.40.0.tgz", - "integrity": "sha512-cwGUs113TgmTQXecx5kfRjB7m0y2wkDLSadPTE2pK6M/wO4N8PjmUaoWOFNCP9MHgsiZwgqd5bZFnDCnszC56Q==", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/eslint-formatter-compact/-/eslint-formatter-compact-9.0.1.tgz", + "integrity": "sha512-mBAti2tb403dQGMyilQTYHU80stem3N7jdtKW+tmn5gj3JNF7ki0rgCZtJFw4iMayTH862FTUIqCdp70ug0S0Q==", "dev": true, + "license": "MIT", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/eslint-plugin-header": { @@ -6458,25 +7001,29 @@ } }, "node_modules/eslint-plugin-jsdoc": { - "version": "50.3.1", - "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-50.3.1.tgz", - "integrity": "sha512-SY9oUuTMr6aWoJggUS40LtMjsRzJPB5ZT7F432xZIHK3EfHF+8i48GbUBpwanrtlL9l1gILNTHK9o8gEhYLcKA==", + "version": "61.5.0", + "resolved": "https://registry.npmjs.org/eslint-plugin-jsdoc/-/eslint-plugin-jsdoc-61.5.0.tgz", + "integrity": "sha512-PR81eOGq4S7diVnV9xzFSBE4CDENRQGP0Lckkek8AdHtbj+6Bm0cItwlFnxsLFriJHspiE3mpu8U20eODyToIg==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@es-joy/jsdoccomment": "~0.48.0", + "@es-joy/jsdoccomment": "~0.76.0", + "@es-joy/resolve.exports": "1.2.0", "are-docs-informative": "^0.0.2", "comment-parser": "1.4.1", - "debug": "^4.3.6", + "debug": "^4.4.3", "escape-string-regexp": "^4.0.0", - "espree": "^10.1.0", + "espree": "^10.4.0", "esquery": "^1.6.0", - "parse-imports": "^2.1.1", - "semver": "^7.6.3", + "html-entities": "^2.6.0", + "object-deep-merge": "^2.0.0", + "parse-imports-exports": "^0.2.4", + "semver": "^7.7.3", "spdx-expression-parse": "^4.0.0", - "synckit": "^0.9.1" + "to-valid-identifier": "^1.0.0" }, "engines": { - "node": ">=18" + "node": ">=20.11.0" }, "peerDependencies": { "eslint": "^7.0.0 || ^8.0.0 || ^9.0.0" @@ -6510,10 +7057,11 @@ } }, "node_modules/eslint-scope": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.1.0.tgz", - "integrity": "sha512-14dSvlhaVhKKsa9Fx1l8A17s7ah7Ef7wCakJ10LYk6+GYmP9yDti2oq2SEwcyndt6knfcZyhyxwY3i9yL78EQw==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.4.0.tgz", + "integrity": "sha512-sNXOfKCn74rt8RICKMvJS7XKV/Xk9kA7DyJr8mJik3S7Cwgy3qlkkmyS2uQB3jiJg6VNdZd/pDBJu0nvG2NlTg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" @@ -6538,10 +7086,11 @@ } }, "node_modules/eslint/node_modules/eslint-visitor-keys": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.1.0.tgz", - "integrity": "sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -6561,6 +7110,19 @@ "node": ">=10.13.0" } }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/esniff": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/esniff/-/esniff-2.0.1.tgz", @@ -6583,14 +7145,15 @@ "dev": true }, "node_modules/espree": { - "version": "10.2.0", - "resolved": "https://registry.npmjs.org/espree/-/espree-10.2.0.tgz", - "integrity": "sha512-upbkBJbckcCNBDBDXEbuhjbP68n+scUd3k/U2EkyM9nw+I/jPiL4cLF/Al06CF96wRltFda16sxDFrxsI1v0/g==", + "version": "10.4.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.4.0.tgz", + "integrity": "sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.12.0", + "acorn": "^8.15.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^4.1.0" + "eslint-visitor-keys": "^4.2.1" }, "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" @@ -6600,10 +7163,11 @@ } }, "node_modules/espree/node_modules/eslint-visitor-keys": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.1.0.tgz", - "integrity": "sha512-Q7lok0mqMUSf5a/AdAZkA5a/gHcO6snwQClVNNvFKCAVlxXucdU8pKydU5ZVZjBx5xr37vGbFFWtLQYreLzrZg==", + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.2.1.tgz", + "integrity": "sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==", "dev": true, + "license": "Apache-2.0", "engines": { "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, @@ -6664,18 +7228,19 @@ } }, "node_modules/event-stream": { - "version": "3.3.4", - "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", - "integrity": "sha1-SrTJoPWlTbkzi0w02Gv86PSzVXE= sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-4.0.1.tgz", + "integrity": "sha512-qACXdu/9VHPBzcyhdOWR5/IahhGMf0roTeZJfzz077GwylcDd90yOHLouhmv7GJ5XzPi6ekaQWd8AvPP2nOvpA==", "dev": true, + "license": "MIT", "dependencies": { - "duplexer": "~0.1.1", - "from": "~0", - "map-stream": "~0.1.0", - "pause-stream": "0.0.11", - "split": "0.3", - "stream-combiner": "~0.0.4", - "through": "~2.3.1" + "duplexer": "^0.1.1", + "from": "^0.1.7", + "map-stream": "0.0.7", + "pause-stream": "^0.0.11", + "split": "^1.0.1", + "stream-combiner": "^0.2.2", + "through": "^2.3.8" } }, "node_modules/events": { @@ -6687,136 +7252,15 @@ "node": ">=0.8.x" } }, - "node_modules/expand-brackets": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz", - "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI= sha512-w/ozOKR9Obk3qoWeY/WDi6MFta9AoMR+zud60mdnbniMcBxRuFJyDt2LdX/14A1UABeqk+Uk+LDfUpvoGKppZA==", - "dev": true, - "dependencies": { - "debug": "^2.3.3", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "posix-character-classes": "^0.1.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/expand-brackets/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", - "dev": true, - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", - "deprecated": "Please upgrade to v0.1.7", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/is-accessor-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", - "deprecated": "Please upgrade to v0.1.5", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/is-data-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/expand-brackets/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "dev": true, + "node_modules/eventsource-parser": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eventsource-parser/-/eventsource-parser-3.0.0.tgz", + "integrity": "sha512-T1C0XCUimhxVQzW4zFipdx0SficT651NnkR0ZSH3yQwh+mFMdLfgjABVi4YtMTtaL4s168593DaoaRLMqryavA==", + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=18.0.0" } }, - "node_modules/expand-brackets/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, "node_modules/expand-template": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", @@ -6828,8 +7272,9 @@ "node_modules/expand-tilde": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/expand-tilde/-/expand-tilde-2.0.2.tgz", - "integrity": "sha1-l+gBqgUt8CRU3kawK/YhZCzchQI= sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==", + "integrity": "sha512-A5EmesHW6rfnZ9ysHQjPdJRni0SRar0tjtG5MNtm9n5TUvsYU8oozprtRD4AqHxcZWWlVuAmQo2nWKfN9oyjTw==", "dev": true, + "license": "MIT", "dependencies": { "homedir-polyfill": "^1.0.1" }, @@ -6871,58 +7316,6 @@ "node": ">=0.10.0" } }, - "node_modules/extglob": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz", - "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==", - "dev": true, - "dependencies": { - "array-unique": "^0.3.2", - "define-property": "^1.0.0", - "expand-brackets": "^2.1.4", - "extend-shallow": "^2.0.1", - "fragment-cache": "^0.2.1", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extglob/node_modules/define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY= sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", - "dev": true, - "dependencies": { - "is-descriptor": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extglob/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/extglob/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/extract-zip": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-2.0.1.tgz", @@ -6952,22 +7345,37 @@ "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" } - }, - "node_modules/fancy-log": { - "version": "1.3.3", - "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", - "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", + }, + "node_modules/fancy-log": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-2.0.0.tgz", + "integrity": "sha512-9CzxZbACXMUXW13tS0tI8XsGGmxWzO2DmYrGuBJOJ8k8q2K7hwfJA5qHjuPPe8wtsco33YR9wc+Rlr5wYFvhSA==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-gray": "^0.1.1", - "color-support": "^1.1.3", - "parse-node-version": "^1.0.0", - "time-stamp": "^1.0.0" + "color-support": "^1.1.3" }, "engines": { - "node": ">= 0.10" + "node": ">=10.13.0" } }, + "node_modules/fast-content-type-parse": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-3.0.0.tgz", + "integrity": "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "MIT" + }, "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", @@ -7008,6 +7416,23 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc= sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/fastest-levenshtein": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/fastest-levenshtein/-/fastest-levenshtein-1.0.12.tgz", @@ -7015,10 +7440,11 @@ "dev": true }, "node_modules/fastq": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.9.0.tgz", - "integrity": "sha512-i7FVWL8HhVY+CTkwFxkN2mk3h+787ixS5S63eb78diVRc1MCssarHq3W5cj0av7YDSwmaV928RNag+U1etRQ7w==", + "version": "1.19.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.19.1.tgz", + "integrity": "sha512-GwLTyxkCXjXbxqIhTsMI2Nui8huMPtnxg7krajPJAjnEG/iiOS7i+zCtWGZR9G0NBKbXKh6X9m9UIsYX/N6vvQ==", "dev": true, + "license": "ISC", "dependencies": { "reusify": "^1.0.4" } @@ -7031,6 +7457,24 @@ "pend": "~1.2.0" } }, + "node_modules/fdir": { + "version": "6.5.0", + "resolved": "https://registry.npmjs.org/fdir/-/fdir-6.5.0.tgz", + "integrity": "sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=12.0.0" + }, + "peerDependencies": { + "picomatch": "^3 || ^4" + }, + "peerDependenciesMeta": { + "picomatch": { + "optional": true + } + } + }, "node_modules/file-entry-cache": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", @@ -7097,22 +7541,22 @@ "node": ">=8" } }, - "node_modules/find-parent-dir": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/find-parent-dir/-/find-parent-dir-0.3.1.tgz", - "integrity": "sha512-o4UcykWV/XN9wm+jMEtWLPlV8RXCZnMhQI6F6OdHeSez7iiJWePw8ijOlskJZMsaQoGR/b7dH6lO02HhaTN7+A==", - "dev": true - }, "node_modules/find-replace": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz", - "integrity": "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==", + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-5.0.2.tgz", + "integrity": "sha512-Y45BAiE3mz2QsrN2fb5QEtO4qb44NcS7en/0y9PEVsg351HsLeVclP8QPMH79Le9sH3rs5RSwJu99W0WPZO43Q==", "dev": true, - "dependencies": { - "array-back": "^3.0.1" - }, + "license": "MIT", "engines": { - "node": ">=4.0.0" + "node": ">=14" + }, + "peerDependencies": { + "@75lb/nature": "latest" + }, + "peerDependenciesMeta": { + "@75lb/nature": { + "optional": true + } } }, "node_modules/find-up": { @@ -7132,194 +7576,46 @@ } }, "node_modules/findup-sync": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-3.0.0.tgz", - "integrity": "sha512-YbffarhcicEhOrm4CtrwdKBdCuz576RLdhJDsIfvNtxUuhdRet1qZcsMjqbePtAseKdAnDyM/IyXbu7PRPRLYg==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-5.0.0.tgz", + "integrity": "sha512-MzwXju70AuyflbgeOhzvQWAvvQdo1XL0A9bVvlXsYcFEBM87WR4OakL4OfZq+QRmr+duJubio+UtNQCPsVESzQ==", "dev": true, + "license": "MIT", "dependencies": { "detect-file": "^1.0.0", - "is-glob": "^4.0.0", - "micromatch": "^3.0.4", + "is-glob": "^4.0.3", + "micromatch": "^4.0.4", "resolve-dir": "^1.0.1" }, "engines": { - "node": ">= 0.10" - } - }, - "node_modules/findup-sync/node_modules/braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "dependencies": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/findup-sync/node_modules/braces/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/findup-sync/node_modules/fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", - "dev": true, - "dependencies": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/findup-sync/node_modules/fill-range/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/findup-sync/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/findup-sync/node_modules/is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/findup-sync/node_modules/is-number/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/findup-sync/node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/findup-sync/node_modules/micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", - "dev": true, - "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/findup-sync/node_modules/to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", - "dev": true, - "dependencies": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" - }, - "engines": { - "node": ">=0.10.0" + "node": ">= 10.13.0" } }, "node_modules/fined": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/fined/-/fined-1.2.0.tgz", - "integrity": "sha512-ZYDqPLGxDkDhDZBjZBb+oD1+j0rA4E0pXY50eplAAOPg2N/gUBSSk5IM1/QhPfyVo19lJ+CvXpqfvk+b2p/8Ng==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/fined/-/fined-2.0.0.tgz", + "integrity": "sha512-OFRzsL6ZMHz5s0JrsEr+TpdGNCtrVtnuG3x1yzGNiQHT0yaDnXAj8V/lWcpJVrnoDpcwXcASxAZYbuXda2Y82A==", "dev": true, + "license": "MIT", "dependencies": { "expand-tilde": "^2.0.2", - "is-plain-object": "^2.0.3", + "is-plain-object": "^5.0.0", "object.defaults": "^1.1.0", - "object.pick": "^1.2.0", - "parse-filepath": "^1.0.1" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/fined/node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "dependencies": { - "isobject": "^3.0.1" + "object.pick": "^1.3.0", + "parse-filepath": "^1.0.2" }, "engines": { - "node": ">=0.10.0" + "node": ">= 10.13.0" } }, "node_modules/flagged-respawn": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-1.0.1.tgz", - "integrity": "sha512-lNaHNVymajmk0OJMBn8fVUAU1BtDeKIqKoVhk4xAALB57aALg6b4W0MfJ/cUE0g9YBXy5XhSlPIpYIJ7HaY/3Q==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/flagged-respawn/-/flagged-respawn-2.0.0.tgz", + "integrity": "sha512-Gq/a6YCi8zexmGHMuJwahTGzXlAZAOsbCVKduWXC6TlLCjjFRlExMJc4GC2NYPYZ0r/brw9P7CpRgQmlPVeOoA==", "dev": true, + "license": "MIT", "engines": { - "node": ">= 0.10" + "node": ">= 10.13.0" } }, "node_modules/flat": { @@ -7375,51 +7671,6 @@ "util-deprecate": "~1.0.1" } }, - "node_modules/font-finder": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/font-finder/-/font-finder-1.1.0.tgz", - "integrity": "sha512-wpCL2uIbi6GurJbU7ZlQ3nGd61Ho+dSU6U83/xJT5UPFfN35EeCW/rOtS+5k+IuEZu2SYmHzDIPL9eA5tSYRAw==", - "license": "MIT", - "dependencies": { - "get-system-fonts": "^2.0.0", - "promise-stream-reader": "^1.0.1" - }, - "engines": { - "node": ">8.0.0" - } - }, - "node_modules/font-ligatures": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/font-ligatures/-/font-ligatures-1.4.1.tgz", - "integrity": "sha512-7W6zlfyhvCqShZ5ReUWqmSd9vBaUudW0Hxis+tqUjtHhsPU+L3Grf8mcZAtCiXHTzorhwdRTId2WeH/88gdFkw==", - "license": "MIT", - "dependencies": { - "font-finder": "^1.0.3", - "lru-cache": "^6.0.0", - "opentype.js": "^0.8.0" - }, - "engines": { - "node": ">8.0.0" - } - }, - "node_modules/font-ligatures/node_modules/lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "license": "ISC", - "dependencies": { - "yallist": "^4.0.0" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/font-ligatures/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "license": "ISC" - }, "node_modules/for-each": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", @@ -7432,8 +7683,9 @@ "node_modules/for-in": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz", - "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==", + "integrity": "sha512-7EwmXrOjyL+ChxMhmG5lnW9MPt1aIeZEwKhQzoBUdTV0N3zuwWDZYVJatDvZ2OyzPUvdIAZDsCetk3coyMfcnQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -7441,8 +7693,9 @@ "node_modules/for-own": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/for-own/-/for-own-1.0.0.tgz", - "integrity": "sha1-xjMy9BXO3EsE2/5wz4NklMU8tEs= sha512-0OABksIGrxKK8K4kynWkQ7y1zounQxP+CWnyclVwj81KW3vlLlGUx57DKGcP/LH216GzqnstnPocF16Nxs0Ycg==", + "integrity": "sha512-0OABksIGrxKK8K4kynWkQ7y1zounQxP+CWnyclVwj81KW3vlLlGUx57DKGcP/LH216GzqnstnPocF16Nxs0Ycg==", "dev": true, + "license": "MIT", "dependencies": { "for-in": "^1.0.1" }, @@ -7451,12 +7704,13 @@ } }, "node_modules/foreground-child": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", - "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.1.tgz", + "integrity": "sha512-gIXjKqtFuWEgzFRJA9WCQeSJLZDjgJUOMCMzxtvFq/37KojM1BFGufqsCy0r4qSQmYLsZYMeyRqzIWOMup03sw==", "dev": true, + "license": "ISC", "dependencies": { - "cross-spawn": "^7.0.0", + "cross-spawn": "^7.0.6", "signal-exit": "^4.0.1" }, "engines": { @@ -7467,36 +7721,38 @@ } }, "node_modules/form-data": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.0.tgz", - "integrity": "sha512-ETEklSGi5t0QMZuiXoA/Q6vcnxcLQP5vdugSpuAyi6SVGi2clPPp+xgEhuMaHC+zGgn31Kd235W35f7Hykkaww==", + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.4.tgz", + "integrity": "sha512-KrGhL9Q4zjj0kiUt5OO4Mr/A/jlI2jDYs5eHBpYHPcBEVSiipAvn2Ko2HnPe20rmcuuvMHNdZFp+4IlGTMF0Ow==", "dev": true, + "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" }, "engines": { "node": ">= 6" } }, - "node_modules/fragment-cache": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz", - "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk= sha512-GMBAbW9antB8iZRHLoGw0b3HANt57diZYFO/HL1JGIC1MjKrdmhxvrJbupnVvpys0zsz7yBApXdQyfepKly2kA==", + "node_modules/form-data-encoder": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/form-data-encoder/-/form-data-encoder-4.1.0.tgz", + "integrity": "sha512-G6NsmEW15s0Uw9XnCg+33H3ViYRyiM0hMrMhhqQOR8NFc5GhYrI+6I3u7OTw7b91J2g8rtvMBZJDbcGb2YUniw==", "dev": true, - "dependencies": { - "map-cache": "^0.2.2" - }, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">= 18" } }, "node_modules/fresh": { "version": "0.5.2", "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", - "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac= sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", + "integrity": "sha512-zJ2mQYM18rEFOudeV4GShTGIQ7RbzA7ozbU9I/XBpm7kqgMywgmylMwXHxZJmkVoYkna9d2pVXVXPdYTP9ej8Q==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -7547,11 +7803,6 @@ "node": ">=8" } }, - "node_modules/fs-minipass/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, "node_modules/fs-mkdirp-stream": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-1.0.0.tgz", @@ -7617,13 +7868,48 @@ "deprecated": "This package is no longer supported.", "dev": true, "dependencies": { - "graceful-fs": "^4.1.2", - "inherits": "~2.0.0", - "mkdirp": ">=0.5 0", - "rimraf": "2" + "graceful-fs": "^4.1.2", + "inherits": "~2.0.0", + "mkdirp": ">=0.5 0", + "rimraf": "2" + }, + "engines": { + "node": ">=0.6" + } + }, + "node_modules/fstream/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fstream/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" }, "engines": { - "node": ">=0.6" + "node": "*" } }, "node_modules/fstream/node_modules/mkdirp": { @@ -7638,6 +7924,20 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/fstream/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, "node_modules/function-bind": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", @@ -7652,6 +7952,7 @@ "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6.9.0" } @@ -7666,16 +7967,22 @@ } }, "node_modules/get-intrinsic": { - "version": "1.2.4", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.2.4.tgz", - "integrity": "sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ==", + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", "dev": true, + "license": "MIT", "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", "function-bind": "^1.1.2", - "has-proto": "^1.0.1", - "has-symbols": "^1.0.3", - "hasown": "^2.0.0" + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" }, "engines": { "node": ">= 0.4" @@ -7684,6 +7991,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/get-stdin": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-7.0.0.tgz", @@ -7708,54 +8029,28 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/get-stream/node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" - } - }, - "node_modules/get-system-fonts": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/get-system-fonts/-/get-system-fonts-2.0.2.tgz", - "integrity": "sha512-zzlgaYnHMIEgHRrfC7x0Qp0Ylhw/sHpM6MHXeVBTYIsvGf5GpbnClB+Q6rAPdn+0gd2oZZIo6Tj3EaWrt4VhDQ==", - "license": "MIT", - "engines": { - "node": ">8.0.0" - } - }, - "node_modules/get-value": { - "version": "2.0.6", - "resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz", - "integrity": "sha1-3BXKHGcjh8p2vTesCjlbogQqLCg= sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/github-from-package": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4= sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==" }, "node_modules/glob": { - "version": "5.0.15", - "resolved": "https://registry.npmjs.org/glob/-/glob-5.0.15.tgz", - "integrity": "sha1-G8k2ueAvSmA/zCIuz3Yz0wuLk7E= sha512-c9IPMazfRITpmAAKi22dK1VKxGDX9ehhqfABDriL/lzO92xcUKEJPQHrVA/2YHSNFB4iFlykVmWvwo48nr3OxA==", - "deprecated": "Glob versions prior to v9 are no longer supported", + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/glob/-/glob-9.0.1.tgz", + "integrity": "sha512-psRdn8MI0gRcH0xow0VOhYxXD/6ZaRGmgtfN0oWN/hCgjxpRQBMCl7wE4JRJSAUTdJsW+FmD0EtE0CgJhKqSVw==", "dev": true, + "license": "ISC", "dependencies": { - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "2 || 3", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "fs.realpath": "^1.0.0", + "minimatch": "^7.3.0", + "minipass": "^4.2.4", + "path-scurry": "^1.5.0" }, "engines": { - "node": "*" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/glob-parent": { @@ -7788,325 +8083,66 @@ "unique-stream": "^2.0.2" }, "engines": { - "node": ">= 0.10" - } - }, - "node_modules/glob-stream/node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "deprecated": "Glob versions prior to v9 are no longer supported", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/glob-stream/node_modules/glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4= sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==", - "dev": true, - "dependencies": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - } - }, - "node_modules/glob-stream/node_modules/is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo= sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/glob-stream/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/glob-to-regexp": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", - "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", - "dev": true - }, - "node_modules/glob-watcher": { - "version": "5.0.5", - "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-5.0.5.tgz", - "integrity": "sha512-zOZgGGEHPklZNjZQaZ9f41i7F2YwE+tS5ZHrDhbBCk3stwahn5vQxnFmBJZHoYdusR6R1bLSXeGUy/BhctwKzw==", - "dev": true, - "dependencies": { - "anymatch": "^2.0.0", - "async-done": "^1.2.0", - "chokidar": "^2.0.0", - "is-negated-glob": "^1.0.0", - "just-debounce": "^1.0.0", - "normalize-path": "^3.0.0", - "object.defaults": "^1.1.0" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/glob-watcher/node_modules/anymatch": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-2.0.0.tgz", - "integrity": "sha512-5teOsQWABXHHBFP9y3skS5P3d/WfWXpv3FUpy+LorMrNYaT9pI4oLMQX7jzQ2KklNpGpWHzdCXTDT2Y3XGlZBw==", - "dev": true, - "dependencies": { - "micromatch": "^3.1.4", - "normalize-path": "^2.1.1" - } - }, - "node_modules/glob-watcher/node_modules/anymatch/node_modules/normalize-path": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-2.1.1.tgz", - "integrity": "sha1-GrKLVW4Zg2Oowab35vogE3/mrtk= sha512-3pKJwH184Xo/lnH6oyP1q2pMd7HcypqqmRs91/6/i2CGtWwIKGCkOOMTm/zXbgTEWHw1uNpNi/igc3ePOYHb6w==", - "dev": true, - "dependencies": { - "remove-trailing-separator": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/glob-watcher/node_modules/binary-extensions": { - "version": "1.13.1", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-1.13.1.tgz", - "integrity": "sha512-Un7MIEDdUC5gNpcGDV97op1Ywk748MpHcFTHoYs6qnj1Z3j7I53VG3nwZhKzoBZmbdRNnb6WRdFlwl7tSDuZGw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/glob-watcher/node_modules/braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "dependencies": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/glob-watcher/node_modules/braces/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/glob-watcher/node_modules/chokidar": { - "version": "2.1.8", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-2.1.8.tgz", - "integrity": "sha512-ZmZUazfOzf0Nve7duiCKD23PFSCs4JPoYyccjUFF3aQkQadqBhfzhjkwBH2mNOG9cTBwhamM37EIsIkZw3nRgg==", - "deprecated": "Chokidar 2 does not receive security updates since 2019. Upgrade to chokidar 3 with 15x fewer dependencies", - "dev": true, - "dependencies": { - "anymatch": "^2.0.0", - "async-each": "^1.0.1", - "braces": "^2.3.2", - "glob-parent": "^3.1.0", - "inherits": "^2.0.3", - "is-binary-path": "^1.0.0", - "is-glob": "^4.0.0", - "normalize-path": "^3.0.0", - "path-is-absolute": "^1.0.0", - "readdirp": "^2.2.1", - "upath": "^1.1.1" - }, - "optionalDependencies": { - "fsevents": "^1.2.7" - } - }, - "node_modules/glob-watcher/node_modules/fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", - "dev": true, - "dependencies": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/glob-watcher/node_modules/fill-range/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/glob-watcher/node_modules/fsevents": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.13.tgz", - "integrity": "sha512-oWb1Z6mkHIskLzEJ/XWX0srkpkTQ7vaopMQkyaEIoq0fmtFVxOthb8cCxeT+p3ynTdkk/RZwbgG4brR5BeWECw==", - "deprecated": "The v1 package contains DANGEROUS / INSECURE binaries. Upgrade to safe fsevents v2", - "dev": true, - "hasInstallScript": true, - "optional": true, - "os": [ - "darwin" - ], - "dependencies": { - "bindings": "^1.5.0", - "nan": "^2.12.1" - }, - "engines": { - "node": ">= 4.0" - } - }, - "node_modules/glob-watcher/node_modules/glob-parent": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", - "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4= sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==", - "dev": true, - "dependencies": { - "is-glob": "^3.1.0", - "path-dirname": "^1.0.0" - } - }, - "node_modules/glob-watcher/node_modules/glob-parent/node_modules/is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo= sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", - "dev": true, - "dependencies": { - "is-extglob": "^2.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/glob-watcher/node_modules/is-binary-path": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-1.0.1.tgz", - "integrity": "sha1-dfFmQrSA8YenEcgUFh/TpKdlWJg= sha512-9fRVlXc0uCxEDj1nQzaWONSpbTfx0FmJfzHF7pwlI8DkWGoHBBea4Pg5Ky0ojwwxQmnSifgbKkI06Qv0Ljgj+Q==", - "dev": true, - "dependencies": { - "binary-extensions": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/glob-watcher/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "dev": true, - "engines": { - "node": ">=0.10.0" + "node": ">= 0.10" } }, - "node_modules/glob-watcher/node_modules/is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "node_modules/glob-stream/node_modules/glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "dependencies": { - "kind-of": "^3.0.2" + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" }, "engines": { - "node": ">=0.10.0" + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/glob-watcher/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "node_modules/glob-stream/node_modules/glob-parent": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-3.1.0.tgz", + "integrity": "sha1-nmr2KZ2NO9K9QEMIMr0RPfkGxa4= sha512-E8Ak/2+dZY6fnzlR7+ueWvhsH1SjHr4jjss4YS/h4py44jY9MhK/VFdaZJAWDz6BbL21KeteKxFSFpq8OS5gVA==", "dev": true, "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" + "is-glob": "^3.1.0", + "path-dirname": "^1.0.0" } }, - "node_modules/glob-watcher/node_modules/micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "node_modules/glob-stream/node_modules/is-glob": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", + "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo= sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", "dev": true, "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" + "is-extglob": "^2.1.0" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/glob-watcher/node_modules/micromatch/node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "node_modules/glob-stream/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, "engines": { - "node": ">=0.10.0" + "node": "*" } }, - "node_modules/glob-watcher/node_modules/readable-stream": { + "node_modules/glob-stream/node_modules/readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", @@ -8121,31 +8157,60 @@ "util-deprecate": "~1.0.1" } }, - "node_modules/glob-watcher/node_modules/readdirp": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-2.2.1.tgz", - "integrity": "sha512-1JU/8q+VgFZyxwrJ+SVIOsh+KywWGpds3NTqikiKpDMZWScmAYyKIgqkO+ARvNWJfXeXR1zxz7aHF4u4CyH6vQ==", + "node_modules/glob-to-regexp": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/glob-to-regexp/-/glob-to-regexp-0.4.1.tgz", + "integrity": "sha512-lkX1HJXwyMcprw/5YUZc2s7DrpAiHB21/V+E1rHUrVNokkvB6bqMzT0VfV6/86ZNabt1k14YOIaT7nDvOX3Iiw==", + "dev": true + }, + "node_modules/glob-watcher": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/glob-watcher/-/glob-watcher-6.0.0.tgz", + "integrity": "sha512-wGM28Ehmcnk2NqRORXFOTOR064L4imSw3EeOqU5bIwUf62eXGwg89WivH6VMahL8zlQHeodzvHpXplrqzrz3Nw==", "dev": true, + "license": "MIT", "dependencies": { - "graceful-fs": "^4.1.11", - "micromatch": "^3.1.10", - "readable-stream": "^2.0.2" + "async-done": "^2.0.0", + "chokidar": "^3.5.3" }, "engines": { - "node": ">=0.10" + "node": ">= 10.13.0" } }, - "node_modules/glob-watcher/node_modules/to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "node_modules/glob/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-7.4.6.tgz", + "integrity": "sha512-sBz8G/YjVniEz6lKPNpKxXwazJe4c19fEfV2GDMX6AjFz+MX9uDWIZW8XreVhkFW3fkIdTv/gxWr/Kks5FFAVw==", "dev": true, + "license": "ISC", "dependencies": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob/node_modules/minipass": { + "version": "4.2.8", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-4.2.8.tgz", + "integrity": "sha512-fNzuVyifolSLFL4NzpF+wEF4qrgqaaKX0haXPQEdQ7NKAN+WecoKMHV09YcuL/DHxrUsYQOK3MiuDf7Ip2OXfQ==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=8" } }, "node_modules/global-agent": { @@ -8171,6 +8236,7 @@ "resolved": "https://registry.npmjs.org/global-modules/-/global-modules-1.0.0.tgz", "integrity": "sha512-sKzpEkf11GpOFuw0Zzjzmt4B4UZwjOcG757PPvrfhxcLFbq0wpsgpOqxpxtxFiCG4DtG93M6XRVbF2oGdev7bg==", "dev": true, + "license": "MIT", "dependencies": { "global-prefix": "^1.0.1", "is-windows": "^1.0.1", @@ -8183,8 +8249,9 @@ "node_modules/global-prefix": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/global-prefix/-/global-prefix-1.0.2.tgz", - "integrity": "sha1-2/dDxsFJklk8ZVVoy2btMsASLr4= sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==", + "integrity": "sha512-5lsx1NUDHtSjfg0eHlmYvZKv8/nVqX4ckFbM+FrGcQ+04KWcWFo9P5MxPZYSzUvyzmdTbI7Eix8Q4IbELDqzKg==", "dev": true, + "license": "MIT", "dependencies": { "expand-tilde": "^2.0.2", "homedir-polyfill": "^1.0.1", @@ -8201,6 +8268,7 @@ "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz", "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==", "dev": true, + "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, @@ -8213,6 +8281,7 @@ "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=18" }, @@ -8237,24 +8306,26 @@ } }, "node_modules/glogg": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/glogg/-/glogg-1.0.2.tgz", - "integrity": "sha512-5mwUoSuBk44Y4EshyiqcH95ZntbDdTQqA3QYSrxmzj28Ai0vXBGMH1ApSANH14j2sIRtqCEyg6PfsuP7ElOEDA==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/glogg/-/glogg-2.2.0.tgz", + "integrity": "sha512-eWv1ds/zAlz+M1ioHsyKJomfY7jbDDPpwSkv14KQj89bycx1nvK5/2Cj/T9g7kzJcX5Bc7Yv22FjfBZS/jl94A==", "dev": true, + "license": "MIT", "dependencies": { - "sparkles": "^1.0.0" + "sparkles": "^2.1.0" }, "engines": { - "node": ">= 0.10" + "node": ">= 10.13.0" } }, "node_modules/gopd": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.0.1.tgz", - "integrity": "sha512-d65bNlIadxvpb/A2abVdlqKqV563juRnZ1Wtk6s1sIR8uNsXR70xqIzVqxVf1eTqDunwT2MkczEeaezCKTZhwA==", + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", "dev": true, - "dependencies": { - "get-intrinsic": "^1.1.3" + "license": "MIT", + "engines": { + "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" @@ -8297,21 +8368,22 @@ "dev": true }, "node_modules/gulp": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/gulp/-/gulp-4.0.2.tgz", - "integrity": "sha512-dvEs27SCZt2ibF29xYgmnwwCYZxdxhQ/+LFWlbAW8y7jt68L/65402Lz3+CKy0Ov4rOs+NERmDq7YlZaDqUIfA==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/gulp/-/gulp-5.0.1.tgz", + "integrity": "sha512-PErok3DZSA5WGMd6XXV3IRNO0mlB+wW3OzhFJLEec1jSERg2j1bxJ6e5Fh6N6fn3FH2T9AP4UYNb/pYlADB9sA==", "dev": true, + "license": "MIT", "dependencies": { - "glob-watcher": "^5.0.3", - "gulp-cli": "^2.2.0", - "undertaker": "^1.2.1", - "vinyl-fs": "^3.0.0" + "glob-watcher": "^6.0.0", + "gulp-cli": "^3.1.0", + "undertaker": "^2.0.0", + "vinyl-fs": "^4.0.2" }, "bin": { "gulp": "bin/gulp.js" }, "engines": { - "node": ">= 0.10" + "node": ">=10.13.0" } }, "node_modules/gulp-azure-storage": { @@ -8370,6 +8442,22 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, + "node_modules/gulp-azure-storage/node_modules/event-stream": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "integrity": "sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==", + "dev": true, + "license": "MIT", + "dependencies": { + "duplexer": "~0.1.1", + "from": "~0", + "map-stream": "~0.1.0", + "pause-stream": "0.0.11", + "split": "0.3", + "stream-combiner": "~0.0.4", + "through": "~2.3.1" + } + }, "node_modules/gulp-azure-storage/node_modules/find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", @@ -8395,6 +8483,12 @@ "node": ">=8" } }, + "node_modules/gulp-azure-storage/node_modules/map-stream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", + "integrity": "sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==", + "dev": true + }, "node_modules/gulp-azure-storage/node_modules/p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", @@ -8431,6 +8525,29 @@ "node": ">= 0.10" } }, + "node_modules/gulp-azure-storage/node_modules/split": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", + "integrity": "sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "through": "2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/gulp-azure-storage/node_modules/stream-combiner": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "integrity": "sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "duplexer": "~0.1.1" + } + }, "node_modules/gulp-azure-storage/node_modules/string-width": { "version": "4.2.3", "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", @@ -8518,16 +8635,19 @@ } }, "node_modules/gulp-bom": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/gulp-bom/-/gulp-bom-3.0.0.tgz", - "integrity": "sha512-iw/J94F+MVlxG64Q17BSkHsyjpY17qHl3N3A/jDdrL77zQBkhKtTiKLqM4di9CUX/qFToyyeDsOWwH+rESBgmA==", + "version": "5.0.0", + "resolved": "https://registry.npmjs.org/gulp-bom/-/gulp-bom-5.0.0.tgz", + "integrity": "sha512-DiFsJR8p/ENCtjNcPNyb3OxgVv189jU5qtVAX9/jRtBKBKPyktaR/WbxJZyNwupShI53vF48kqkTZ83hMsRmJA==", "dev": true, + "license": "MIT", "dependencies": { - "plugin-error": "^1.0.1", - "through2": "^3.0.1" + "gulp-plugin-extras": "^0.1.0" }, "engines": { - "node": ">=8" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" }, "peerDependencies": { "gulp": ">=4" @@ -8600,191 +8720,93 @@ } }, "node_modules/gulp-cli": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-2.3.0.tgz", - "integrity": "sha512-zzGBl5fHo0EKSXsHzjspp3y5CONegCm8ErO5Qh0UzFzk2y4tMvzLWhoDokADbarfZRL2pGpRp7yt6gfJX4ph7A==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/gulp-cli/-/gulp-cli-3.1.0.tgz", + "integrity": "sha512-zZzwlmEsTfXcxRKiCHsdyjZZnFvXWM4v1NqBJSYbuApkvVKivjcmOS2qruAJ+PkEHLFavcDKH40DPc1+t12a9Q==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-colors": "^1.0.1", - "archy": "^1.0.0", - "array-sort": "^1.0.0", - "color-support": "^1.1.3", - "concat-stream": "^1.6.0", - "copy-props": "^2.0.1", - "fancy-log": "^1.3.2", - "gulplog": "^1.0.0", - "interpret": "^1.4.0", - "isobject": "^3.0.1", - "liftoff": "^3.1.0", - "matchdep": "^2.0.0", - "mute-stdout": "^1.0.0", - "pretty-hrtime": "^1.0.0", - "replace-homedir": "^1.0.0", - "semver-greatest-satisfied-range": "^1.1.0", - "v8flags": "^3.2.0", - "yargs": "^7.1.0" + "@gulpjs/messages": "^1.1.0", + "chalk": "^4.1.2", + "copy-props": "^4.0.0", + "gulplog": "^2.2.0", + "interpret": "^3.1.1", + "liftoff": "^5.0.1", + "mute-stdout": "^2.0.0", + "replace-homedir": "^2.0.0", + "semver-greatest-satisfied-range": "^2.0.0", + "string-width": "^4.2.3", + "v8flags": "^4.0.0", + "yargs": "^16.2.0" }, "bin": { "gulp": "bin/gulp.js" }, "engines": { - "node": ">= 0.10" - } - }, - "node_modules/gulp-cli/node_modules/ansi-colors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", - "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", - "dev": true, - "dependencies": { - "ansi-wrap": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gulp-cli/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gulp-cli/node_modules/camelcase": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-3.0.0.tgz", - "integrity": "sha512-4nhGqUkc4BqbBBB4Q6zLuD7lzzrHYrjKGeYaEji/3tFR5VdJu9v+LilhGIVe8wxEJPPOeWo7eg8dwY13TZ1BNg==", - "dev": true, - "engines": { - "node": ">=0.10.0" + "node": ">=10.13.0" } }, "node_modules/gulp-cli/node_modules/cliui": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-3.2.0.tgz", - "integrity": "sha1-EgYBU3qRbSmUD5NNo7SNWFo5IT0= sha512-0yayqDxWQbqk3ojkYqUKqaAQ6AfNKeKWRNA8kR0WXzAsdHpP4BIaOmMAG87JGuO6qcobyW4GjxHd9PmhEd+T9w==", + "version": "7.0.4", + "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", + "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", "dev": true, + "license": "ISC", "dependencies": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1", - "wrap-ansi": "^2.0.0" - } - }, - "node_modules/gulp-cli/node_modules/decamelize": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-1.2.0.tgz", - "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA= sha512-z2S+W9X73hAUUki+N+9Za2lBlun89zigOyGrsax+KUQ6wKW4ZoWpEYBkGhQjwAjjDCkWxhY0VKEhk8wzY7F5cA==", - "dev": true, - "engines": { - "node": ">=0.10.0" + "string-width": "^4.2.0", + "strip-ansi": "^6.0.0", + "wrap-ansi": "^7.0.0" } }, - "node_modules/gulp-cli/node_modules/get-caller-file": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-1.0.3.tgz", - "integrity": "sha512-3t6rVToeoZfYSGd8YoLFR2DJkiQrIiUrGcjvFX2mDw3bn6k2OtwHN0TNCLbBO+w8qTvimhDkv+LSscbJY1vE6w==", - "dev": true - }, - "node_modules/gulp-cli/node_modules/is-fullwidth-code-point": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz", - "integrity": "sha1-754xOG8DGn8NZDr4L95QxFfvAMs= sha512-1pqUqRjkhPJ9miNq9SwMfdvi6lBJcd6eFxvfaivQhaH3SgisfiuudvFntdKOmxuee/77l+FPjKrQjWvmPjWrRw==", + "node_modules/gulp-cli/node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true, - "dependencies": { - "number-is-nan": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gulp-cli/node_modules/require-main-filename": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-1.0.1.tgz", - "integrity": "sha1-l/cXtp1IeE9fUmpsWqj/3aBVpNE= sha512-IqSUtOVP4ksd1C/ej5zeEh/BIP2ajqpn8c5x+q99gvcIG/Qf0cud5raVnE/Dwd0ua9TXYDoDc0RE5hBSdz22Ug==", - "dev": true + "license": "MIT" }, "node_modules/gulp-cli/node_modules/string-width": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-1.0.2.tgz", - "integrity": "sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M= sha512-0XsVpQLnVCXHJfyEs8tC0zpTVIr5PKKsQtkT29IwupnPTjtPmQ3xT/4yCREF9hYkV/3M3kzcUTSAZT6a6h81tw==", - "dev": true, - "dependencies": { - "code-point-at": "^1.0.0", - "is-fullwidth-code-point": "^1.0.0", - "strip-ansi": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gulp-cli/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", - "dev": true, - "dependencies": { - "ansi-regex": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/gulp-cli/node_modules/which-module": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/which-module/-/which-module-1.0.0.tgz", - "integrity": "sha1-u6Y8qGGUiZT/MHc2CJ47lgJsKk8= sha512-F6+WgncZi/mJDrammbTuHe1q0R5hOXv/mBaiNA2TCNT/LTHusX0V+CJnj9XT8ki5ln2UZyyddDgHfCzyrOH7MQ==", - "dev": true - }, - "node_modules/gulp-cli/node_modules/wrap-ansi": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-2.1.0.tgz", - "integrity": "sha1-2Pw9KE3QV5T+hJc8rs3Rz4JP3YU= sha512-vAaEaDM946gbNpH5pLVNR+vX2ht6n0Bt3GXwVB1AuAqZosOvHNF3P7wDnh8KLkSqgUh0uh77le7Owgoz+Z9XBw==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, + "license": "MIT", "dependencies": { - "string-width": "^1.0.1", - "strip-ansi": "^3.0.1" + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/gulp-cli/node_modules/y18n": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/y18n/-/y18n-3.2.2.tgz", - "integrity": "sha512-uGZHXkHnhF0XeeAPgnKfPv1bgKAYyVvmNL1xlKsPYZPaIHxGti2hHqvOCQv71XMsLxu1QjergkqogUnms5D3YQ==", - "dev": true - }, "node_modules/gulp-cli/node_modules/yargs": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-7.1.2.tgz", - "integrity": "sha512-ZEjj/dQYQy0Zx0lgLMLR8QuaqTihnxirir7EwUHp1Axq4e3+k8jXU5K0VLbNvedv1f4EWtBonDIZm0NUr+jCcA==", + "version": "16.2.0", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", + "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, + "license": "MIT", "dependencies": { - "camelcase": "^3.0.0", - "cliui": "^3.2.0", - "decamelize": "^1.1.1", - "get-caller-file": "^1.0.1", - "os-locale": "^1.4.0", - "read-pkg-up": "^1.0.1", + "cliui": "^7.0.2", + "escalade": "^3.1.1", + "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "require-main-filename": "^1.0.1", - "set-blocking": "^2.0.0", - "string-width": "^1.0.2", - "which-module": "^1.0.0", - "y18n": "^3.2.1", - "yargs-parser": "^5.0.1" + "string-width": "^4.2.0", + "y18n": "^5.0.5", + "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" } }, "node_modules/gulp-cli/node_modules/yargs-parser": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-5.0.1.tgz", - "integrity": "sha512-wpav5XYiddjXxirPoCTUPbqM0PXvJ9hiBMvuJgInvo4/lAOTZzUprArw17q2O1P2+GHhbBr18/iQwjL5Z9BqfA==", + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true, - "dependencies": { - "camelcase": "^3.0.0", - "object.assign": "^4.1.0" + "license": "ISC", + "engines": { + "node": ">=10" } }, "node_modules/gulp-filter": { @@ -9075,6 +9097,22 @@ "node": ">=0.10.0" } }, + "node_modules/gulp-gzip/node_modules/fancy-log": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", + "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-gray": "^0.1.1", + "color-support": "^1.1.3", + "parse-node-version": "^1.0.0", + "time-stamp": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/gulp-gzip/node_modules/readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", @@ -9101,26 +9139,92 @@ } }, "node_modules/gulp-json-editor": { - "version": "2.5.0", - "resolved": "https://registry.npmjs.org/gulp-json-editor/-/gulp-json-editor-2.5.0.tgz", - "integrity": "sha512-HyrBSaE+Di6oQbKsfNM6X7dPFowOuTTuVYjxratU8QAiW7LR7Rydm+/fSS3OehdnuP++A/07q/nksihuD5FZSA==", + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/gulp-json-editor/-/gulp-json-editor-2.6.0.tgz", + "integrity": "sha512-Ni0ZUpNrhesHiTlHQth/Nv1rXCn0LUicEvzA5XuGy186C4PVeNoRjfuAIQrbmt3scKv8dgGbCs0hd77ScTw7hA==", "dev": true, + "license": "MIT", "dependencies": { - "deepmerge": "^3.0.0", - "detect-indent": "^5.0.0", - "js-beautify": "^1.8.9", - "plugin-error": "^1.0.1", - "through2": "^3.0.0" + "deepmerge": "^4.3.1", + "detect-indent": "^6.1.0", + "js-beautify": "^1.14.11", + "plugin-error": "^2.0.1", + "through2": "^4.0.2" + } + }, + "node_modules/gulp-json-editor/node_modules/ansi-colors": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-1.1.0.tgz", + "integrity": "sha512-SFKX67auSNoVR38N3L+nvsPjOE0bybKTYbkf5tRvushrAPQ9V75huw0ZxBkKVeRU9kqH3d6HA4xTckbwZ4ixmA==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-wrap": "^0.1.0" }, "engines": { - "node": ">=6" + "node": ">=0.10.0" + } + }, + "node_modules/gulp-json-editor/node_modules/plugin-error": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/plugin-error/-/plugin-error-2.0.1.tgz", + "integrity": "sha512-zMakqvIDyY40xHOvzXka0kUvf40nYIuwRE8dWhti2WtjQZ31xAgBZBhxsK7vK3QbRXS1Xms/LO7B5cuAsfB2Gg==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-colors": "^1.0.1" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/gulp-json-editor/node_modules/through2": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/through2/-/through2-4.0.2.tgz", + "integrity": "sha512-iOqSav00cVxEEICeD7TjLB1sueEL+81Wpzp2bY17uZjZN0pWZPuo4suZ/61VujxmqSGFfgOcNuTZ85QJwNZQpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "readable-stream": "3" + } + }, + "node_modules/gulp-plugin-extras": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/gulp-plugin-extras/-/gulp-plugin-extras-0.1.0.tgz", + "integrity": "sha512-SqJI+BiJrCydau75ZVBlWmzkg6aEaFj2Hrcyg8+dtQx2JUvME1tjOB0Qf/JSuURgDUJFbnLiiAt7L1GoGsW7ug==", + "dev": true, + "license": "MIT", + "dependencies": { + "@types/vinyl": "^2.0.9", + "chalk": "^5.3.0", + "easy-transform-stream": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gulp-plugin-extras/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" } }, "node_modules/gulp-plumber": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/gulp-plumber/-/gulp-plumber-1.2.0.tgz", - "integrity": "sha512-L/LJftsbKoHbVj6dN5pvMsyJn9jYI0wT0nMg3G6VZhDac4NesezecYTi8/48rHi+yEic3sUpw6jlSc7qNWh32A==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/gulp-plumber/-/gulp-plumber-1.2.1.tgz", + "integrity": "sha512-mctAi9msEAG7XzW5ytDVZ9PxWMzzi1pS2rBH7lA095DhMa6KEXjm+St0GOCc567pJKJ/oCvosVAZEpAey0q2eQ==", "dev": true, + "license": "MIT", "dependencies": { "chalk": "^1.1.3", "fancy-log": "^1.3.2", @@ -9218,6 +9322,22 @@ "node": ">=0.10.0" } }, + "node_modules/gulp-plumber/node_modules/fancy-log": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", + "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-gray": "^0.1.1", + "color-support": "^1.1.3", + "parse-node-version": "^1.0.0", + "time-stamp": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/gulp-plumber/node_modules/kind-of": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-1.1.0.tgz", @@ -9290,42 +9410,30 @@ } }, "node_modules/gulp-rename": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/gulp-rename/-/gulp-rename-1.2.2.tgz", - "integrity": "sha1-OtRCh2PwXidk3sHGfYaNsnVoeBc= sha512-qhfUlYwq5zIP4cpRjx+np2vZVnr0xCRQrF3RsGel8uqL47Gu3yjmllSfnvJyl/39zYuxS68e1nnxImbm7388vw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/gulp-rename/-/gulp-rename-2.1.0.tgz", + "integrity": "sha512-dGuzuH8jQGqCMqC544IEPhs5+O2l+IkdoSZsgd4kY97M1CxQeI3qrmweQBIrxLBbjbe/8uEWK8HHcNBc3OCy4g==", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10.0", - "npm": ">=1.2.10" + "node": ">=4" } }, "node_modules/gulp-replace": { - "version": "0.5.4", - "resolved": "https://registry.npmjs.org/gulp-replace/-/gulp-replace-0.5.4.tgz", - "integrity": "sha1-aaZ5FLvRPFYr/xT1BKQDeWqg2qk= sha512-lHL+zKJN8uV95UkONnfRkoj2yJxPPupt2SahxA4vo5c+Ee3+WaIiMdWbOyUhg8BhAROQrWKnnxKOWPdVrnBwGw==", + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/gulp-replace/-/gulp-replace-1.1.4.tgz", + "integrity": "sha512-SVSF7ikuWKhpAW4l4wapAqPPSToJoiNKsbDoUnRrSgwZHH7lH8pbPeQj1aOVYQrbZKhfSVBxVW+Py7vtulRktw==", "dev": true, + "license": "MIT", "dependencies": { - "istextorbinary": "1.0.2", - "readable-stream": "^2.0.1", - "replacestream": "^4.0.0" + "@types/node": "*", + "@types/vinyl": "^2.0.4", + "istextorbinary": "^3.0.0", + "replacestream": "^4.0.3", + "yargs-parser": ">=5.0.0-security.0" }, "engines": { - "node": ">=0.10" - } - }, - "node_modules/gulp-replace/node_modules/readable-stream": { - "version": "2.3.7", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", - "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", - "dev": true, - "dependencies": { - "core-util-is": "~1.0.0", - "inherits": "~2.0.3", - "isarray": "~1.0.0", - "process-nextick-args": "~2.0.0", - "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" + "node": ">=10" } }, "node_modules/gulp-sourcemaps": { @@ -9377,6 +9485,16 @@ "util-deprecate": "~1.0.1" } }, + "node_modules/gulp-sourcemaps/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/gulp-sourcemaps/node_modules/through2": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", @@ -9410,6 +9528,28 @@ "vinyl-fs": "^3.0.3" } }, + "node_modules/gulp-symdest/node_modules/event-stream": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "integrity": "sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==", + "dev": true, + "license": "MIT", + "dependencies": { + "duplexer": "~0.1.1", + "from": "~0", + "map-stream": "~0.1.0", + "pause-stream": "0.0.11", + "split": "0.3", + "stream-combiner": "~0.0.4", + "through": "~2.3.1" + } + }, + "node_modules/gulp-symdest/node_modules/map-stream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", + "integrity": "sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==", + "dev": true + }, "node_modules/gulp-symdest/node_modules/mkdirp": { "version": "0.5.5", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", @@ -9422,13 +9562,37 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/gulp-symdest/node_modules/split": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", + "integrity": "sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "through": "2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/gulp-symdest/node_modules/stream-combiner": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "integrity": "sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "duplexer": "~0.1.1" + } + }, "node_modules/gulp-untar": { - "version": "0.0.7", - "resolved": "https://registry.npmjs.org/gulp-untar/-/gulp-untar-0.0.7.tgz", - "integrity": "sha512-0QfbCH2a1k2qkTLWPqTX+QO4qNsHn3kC546YhAP3/n0h+nvtyGITDuDrYBMDZeW4WnFijmkOvBWa5HshTic1tw==", + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/gulp-untar/-/gulp-untar-0.0.8.tgz", + "integrity": "sha512-mqW7v2uvrxd8IoCCwJ04sPYgWjR3Gsi6yfhVWBK3sFMDP7FuoT7GNmxrCMwkk4RWqQohx8DRv+cDq4SRDXATGA==", "dev": true, + "license": "MIT", "dependencies": { - "event-stream": "~3.3.4", + "event-stream": "3.3.4", "streamifier": "~0.1.1", "tar": "^2.2.1", "through2": "~2.0.3", @@ -9450,6 +9614,28 @@ "integrity": "sha1-uI+UqCzzi4eR1YBG6kAprYjKmdE= sha512-dhUqc57gSMCo6TX85FLfe51eC/s+Im2MLkAgJwfaRRexR2tA4dd3eLEW4L6efzHc2iNorrRRXITifnDLlRrhaA==", "dev": true }, + "node_modules/gulp-untar/node_modules/event-stream": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "integrity": "sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==", + "dev": true, + "license": "MIT", + "dependencies": { + "duplexer": "~0.1.1", + "from": "~0", + "map-stream": "~0.1.0", + "pause-stream": "0.0.11", + "split": "0.3", + "stream-combiner": "~0.0.4", + "through": "~2.3.1" + } + }, + "node_modules/gulp-untar/node_modules/map-stream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", + "integrity": "sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==", + "dev": true + }, "node_modules/gulp-untar/node_modules/readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", @@ -9474,6 +9660,29 @@ "node": ">= 0.4" } }, + "node_modules/gulp-untar/node_modules/split": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", + "integrity": "sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "through": "2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/gulp-untar/node_modules/stream-combiner": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "integrity": "sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==", + "dev": true, + "license": "MIT", + "dependencies": { + "duplexer": "~0.1.1" + } + }, "node_modules/gulp-untar/node_modules/tar": { "version": "2.2.2", "resolved": "https://registry.npmjs.org/tar/-/tar-2.2.2.tgz", @@ -9526,6 +9735,22 @@ "yazl": "^2.2.1" } }, + "node_modules/gulp-vinyl-zip/node_modules/event-stream": { + "version": "3.3.4", + "resolved": "https://registry.npmjs.org/event-stream/-/event-stream-3.3.4.tgz", + "integrity": "sha512-QHpkERcGsR0T7Qm3HNJSyXKEEj8AHNxkY3PK8TS2KJvQ7NiSHe3DDpwVKKtoYprL/AreyzFBeIkBIWChAqn60g==", + "dev": true, + "license": "MIT", + "dependencies": { + "duplexer": "~0.1.1", + "from": "~0", + "map-stream": "~0.1.0", + "pause-stream": "0.0.11", + "split": "0.3", + "stream-combiner": "~0.0.4", + "through": "~2.3.1" + } + }, "node_modules/gulp-vinyl-zip/node_modules/fd-slicer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.0.1.tgz", @@ -9535,6 +9760,12 @@ "pend": "~1.2.0" } }, + "node_modules/gulp-vinyl-zip/node_modules/map-stream": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", + "integrity": "sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==", + "dev": true + }, "node_modules/gulp-vinyl-zip/node_modules/queue": { "version": "4.5.0", "resolved": "https://registry.npmjs.org/queue/-/queue-4.5.0.tgz", @@ -9559,13 +9790,36 @@ "util-deprecate": "~1.0.1" } }, - "node_modules/gulp-vinyl-zip/node_modules/replace-ext": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz", - "integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==", + "node_modules/gulp-vinyl-zip/node_modules/replace-ext": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-1.0.1.tgz", + "integrity": "sha512-yD5BHCe7quCgBph4rMQ+0KkIRKwWCrHDOX1p1Gp6HwjPM5kVoCdKGNhN7ydqqsX6lJEnQDKZ/tFMiEdQ1dvPEw==", + "dev": true, + "engines": { + "node": ">= 0.10" + } + }, + "node_modules/gulp-vinyl-zip/node_modules/split": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", + "integrity": "sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==", + "dev": true, + "license": "MIT", + "dependencies": { + "through": "2" + }, + "engines": { + "node": "*" + } + }, + "node_modules/gulp-vinyl-zip/node_modules/stream-combiner": { + "version": "0.0.4", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", + "integrity": "sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==", "dev": true, - "engines": { - "node": ">= 0.10" + "license": "MIT", + "dependencies": { + "duplexer": "~0.1.1" } }, "node_modules/gulp-vinyl-zip/node_modules/through2": { @@ -9605,16 +9859,174 @@ "fd-slicer": "~1.0.1" } }, + "node_modules/gulp/node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, + "node_modules/gulp/node_modules/fs-mkdirp-stream": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/fs-mkdirp-stream/-/fs-mkdirp-stream-2.0.1.tgz", + "integrity": "sha512-UTOY+59K6IA94tec8Wjqm0FSh5OVudGNB0NL/P6fB3HiE3bYOY3VYBGijsnOHNkQSwC1FKkU77pmq7xp9CskLw==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.8", + "streamx": "^2.12.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/gulp/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/gulp/node_modules/glob-stream": { + "version": "8.0.3", + "resolved": "https://registry.npmjs.org/glob-stream/-/glob-stream-8.0.3.tgz", + "integrity": "sha512-fqZVj22LtFJkHODT+M4N1RJQ3TjnnQhfE9GwZI8qXscYarnhpip70poMldRnP8ipQ/w0B621kOhfc53/J9bd/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@gulpjs/to-absolute-glob": "^4.0.0", + "anymatch": "^3.1.3", + "fastq": "^1.13.0", + "glob-parent": "^6.0.2", + "is-glob": "^4.0.3", + "is-negated-glob": "^1.0.0", + "normalize-path": "^3.0.0", + "streamx": "^2.12.5" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/gulp/node_modules/lead": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/lead/-/lead-4.0.0.tgz", + "integrity": "sha512-DpMa59o5uGUWWjruMp71e6knmwKU3jRBBn1kjuLWN9EeIOxNeSAwvHf03WIl8g/ZMR2oSQC9ej3yeLBwdDc/pg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/gulp/node_modules/now-and-later": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/now-and-later/-/now-and-later-3.0.0.tgz", + "integrity": "sha512-pGO4pzSdaxhWTGkfSfHx3hVzJVslFPwBp2Myq9MYN/ChfJZF87ochMAXnvz6/58RJSf5ik2q9tXprBBrk2cpcg==", + "dev": true, + "license": "MIT", + "dependencies": { + "once": "^1.4.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/gulp/node_modules/resolve-options": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/resolve-options/-/resolve-options-2.0.0.tgz", + "integrity": "sha512-/FopbmmFOQCfsCx77BRFdKOniglTiHumLgwvd6IDPihy1GKkadZbgQJBcTb2lMzSR1pndzd96b1nZrreZ7+9/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "value-or-function": "^4.0.0" + }, + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/gulp/node_modules/to-through": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/to-through/-/to-through-3.0.0.tgz", + "integrity": "sha512-y8MN937s/HVhEoBU1SxfHC+wxCHkV1a9gW8eAdTadYh/bGyesZIVcbjI+mSpFbSVwQici/XjBjuUyri1dnXwBw==", + "dev": true, + "license": "MIT", + "dependencies": { + "streamx": "^2.12.5" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/gulp/node_modules/value-or-function": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/value-or-function/-/value-or-function-4.0.0.tgz", + "integrity": "sha512-aeVK81SIuT6aMJfNo9Vte8Dw0/FZINGBV8BfCraGtqVxIeLAEhJyoWs8SmvRVmXfGss2PmmOwZCuBPbZR+IYWg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10.13.0" + } + }, + "node_modules/gulp/node_modules/vinyl-fs": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-4.0.2.tgz", + "integrity": "sha512-XRFwBLLTl8lRAOYiBqxY279wY46tVxLaRhSwo3GzKEuLz1giffsOquWWboD/haGf5lx+JyTigCFfe7DWHoARIA==", + "dev": true, + "license": "MIT", + "dependencies": { + "fs-mkdirp-stream": "^2.0.1", + "glob-stream": "^8.0.3", + "graceful-fs": "^4.2.11", + "iconv-lite": "^0.6.3", + "is-valid-glob": "^1.0.0", + "lead": "^4.0.0", + "normalize-path": "3.0.0", + "resolve-options": "^2.0.0", + "stream-composer": "^1.0.2", + "streamx": "^2.14.0", + "to-through": "^3.0.0", + "value-or-function": "^4.0.0", + "vinyl": "^3.0.1", + "vinyl-sourcemap": "^2.0.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/gulp/node_modules/vinyl-sourcemap": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/vinyl-sourcemap/-/vinyl-sourcemap-2.0.0.tgz", + "integrity": "sha512-BAEvWxbBUXvlNoFQVFVHpybBbjW1r03WhohJzJDSfgrrK5xVYIDTan6xN14DlyImShgDRv2gl9qhM6irVMsV0Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "convert-source-map": "^2.0.0", + "graceful-fs": "^4.2.10", + "now-and-later": "^3.0.0", + "streamx": "^2.12.5", + "vinyl": "^3.0.0", + "vinyl-contents": "^2.0.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/gulplog": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-1.0.0.tgz", - "integrity": "sha1-4oxNRdBey77YGDY86PnFkmIp/+U= sha512-hm6N8nrm3Y08jXie48jsC55eCZz9mnb4OirAStEk2deqeyhXU3C1otDVh+ccttMuc1sBi6RX6ZJ720hs9RCvgw==", + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/gulplog/-/gulplog-2.2.0.tgz", + "integrity": "sha512-V2FaKiOhpR3DRXZuYdRLn/qiY0yI5XmqbTKrYbdemJ+xOh2d2MOweI/XFgMzd/9+1twdvMwllnZbWZNJ+BOm4A==", "dev": true, + "license": "MIT", "dependencies": { - "glogg": "^1.0.0" + "glogg": "^2.2.0" }, "engines": { - "node": ">= 0.10" + "node": ">= 10.13.0" } }, "node_modules/gunzip-maybe": { @@ -9713,23 +10125,12 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-proto": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/has-proto/-/has-proto-1.0.1.tgz", - "integrity": "sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/has-symbols": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.3.tgz", - "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.4" }, @@ -9738,12 +10139,13 @@ } }, "node_modules/has-tostringtag": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.0.tgz", - "integrity": "sha512-kFjcSNhnlGV1kyoGk7OXKSawH5JOb/LzUc5w9B02hOTO0dfFRjbHQKvg1d6cf3HbeUmtU9VbbV3qzZ2Teh97WQ==", + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", "dev": true, + "license": "MIT", "dependencies": { - "has-symbols": "^1.0.2" + "has-symbols": "^1.0.3" }, "engines": { "node": ">= 0.4" @@ -9752,69 +10154,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/has-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", - "integrity": "sha1-GLKB2lhbHFxR3vJMkw7SmgvmsXc= sha512-IBXk4GTsLYdQ7Rvt+GRBrFSVEkmuOUy4re0Xjd9kJSUQpnTrWR4/y9RpfexN9vkAPMFuQoeWKwqzPozRTlasGw==", - "dev": true, - "dependencies": { - "get-value": "^2.0.6", - "has-values": "^1.0.0", - "isobject": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-values": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-1.0.0.tgz", - "integrity": "sha1-lbC2P+whRmGab+V/51Yo1aOe/k8= sha512-ODYZC64uqzmtfGMEAX/FvZiRyWLpAC3vYnNunURUnkGVTS+mI0smVsWaPydRBsE3g+ok7h960jChO8mFcWlHaQ==", - "dev": true, - "dependencies": { - "is-number": "^3.0.0", - "kind-of": "^4.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-values/node_modules/is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-values/node_modules/is-number/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/has-values/node_modules/kind-of": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-4.0.0.tgz", - "integrity": "sha1-IIE989cSkosgc3hpGkUGb65y3Vc= sha512-24XsCxmEbRwEDbz/qz3stgin8TTzZ1ESR56OMCN0ujYg+vRutNSiOj9bHH9u85DKgXguraugV5sFuvbD4FW/hw==", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", @@ -9841,6 +10180,7 @@ "resolved": "https://registry.npmjs.org/homedir-polyfill/-/homedir-polyfill-1.0.3.tgz", "integrity": "sha512-eSmmWE5bZTK2Nou4g0AI3zZ9rswp7GRKoKXS1BLUkvPviOqs4YTN1djQIqrXy9k5gEtdLPy86JjRwsNM9tnDcA==", "dev": true, + "license": "MIT", "dependencies": { "parse-passwd": "^1.0.0" }, @@ -9854,6 +10194,23 @@ "integrity": "sha512-mxIDAb9Lsm6DoOJ7xH+5+X4y1LU/4Hi50L9C5sIswK3JzULS4bwk1FvjdBgvYR4bzT4tuUQiC15FE2f5HbLvYw==", "dev": true }, + "node_modules/html-entities": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/html-entities/-/html-entities-2.6.0.tgz", + "integrity": "sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/mdevils" + }, + { + "type": "patreon", + "url": "https://patreon.com/mdevils" + } + ], + "license": "MIT" + }, "node_modules/html-escaper": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.0.tgz", @@ -9865,6 +10222,7 @@ "resolved": "https://registry.npmjs.org/http-assert/-/http-assert-1.5.0.tgz", "integrity": "sha512-uPpH7OKX4H25hBmU6G1jWNaqJGpTXxey+YOUizJUAgu0AjLUeC8D73hTrhvDS5D+GJN1DN1+hhc/eF/wpxtp0w==", "dev": true, + "license": "MIT", "dependencies": { "deep-equal": "~1.0.1", "http-errors": "~1.8.0" @@ -9876,8 +10234,9 @@ "node_modules/http-assert/node_modules/depd": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "integrity": "sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -9887,6 +10246,7 @@ "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", "dev": true, + "license": "MIT", "dependencies": { "depd": "~1.1.2", "inherits": "2.0.4", @@ -9901,33 +10261,39 @@ "node_modules/http-assert/node_modules/statuses": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "integrity": "sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/http-cache-semantics": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.1.tgz", - "integrity": "sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==", - "dev": true + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.2.0.tgz", + "integrity": "sha512-dTxcvPXqPvXBQpq5dUr6mEMJX4oIEFv6bwom3FDwKRDsuIjjJGANqhBuoAn9c1RQJIdAKav33ED65E2ys+87QQ==", + "dev": true, + "license": "BSD-2-Clause" }, "node_modules/http-errors": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.0.tgz", - "integrity": "sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-2.0.1.tgz", + "integrity": "sha512-4FbRdAX+bSdmo4AUFuS0WNiPz8NgFt+r8ThgNWmlrjQjt1Q7ZR9+zTlce2859x4KSXrwIsaeTqDoKQmtP8pLmQ==", "dev": true, "license": "MIT", "dependencies": { - "depd": "2.0.0", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": "2.0.1", - "toidentifier": "1.0.1" + "depd": "~2.0.0", + "inherits": "~2.0.4", + "setprototypeof": "~1.2.0", + "statuses": "~2.0.2", + "toidentifier": "~1.0.1" }, "engines": { "node": ">= 0.8" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/http-proxy-agent": { @@ -9952,107 +10318,51 @@ "resolve-alpn": "^1.0.0" }, "engines": { - "node": ">=10.19.0" - } - }, - "node_modules/https-proxy-agent": { - "version": "7.0.5", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.5.tgz", - "integrity": "sha512-1e4Wqeblerz+tMKPIq2EMGiiWW1dIjZOksyHWSUm1rmuvw/how9hBHZ38lAGj5ID4Ik6EdkOw7NmWPy6LAwalw==", - "dependencies": { - "agent-base": "^7.0.2", - "debug": "4" - }, - "engines": { - "node": ">= 14" - } - }, - "node_modules/husky": { - "version": "0.13.4", - "resolved": "https://registry.npmjs.org/husky/-/husky-0.13.4.tgz", - "integrity": "sha512-kafsK/82ndSVKJe1IoR/z7NKkiI2LYM6H+VNI/YlKOeoOXEJTpD65TNu05Zx7pzSZzLuAdMt4fHgpUsnd6HJ7A==", - "dev": true, - "hasInstallScript": true, - "dependencies": { - "chalk": "^1.1.3", - "find-parent-dir": "^0.3.0", - "is-ci": "^1.0.9", - "normalize-path": "^1.0.0" - } - }, - "node_modules/husky/node_modules/ansi-regex": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", - "integrity": "sha512-TIGnTpdo+E3+pCyAluZvtED5p5wCqLdezCyhPZzKPcxvFplEt4i+W7OONCKgeZFT3+y5NZZfOOS/Bdcanm1MYA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/husky/node_modules/ansi-styles": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", - "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4= sha512-kmCevFghRiWM7HB5zTPULl4r9bVFSWjz62MhqizDGUrq2NWuNMQyuv4tHHoKJHs69M/MF64lEcHdYIocrdWQYA==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/husky/node_modules/chalk": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", - "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg= sha512-U3lRVLMSlsCfjqYPbLyVv11M9CPW4I728d6TCKMAOJueEeB9/8o+eSsMnxPJD+Q+K909sdESg7C+tIkoH6on1A==", - "dev": true, - "dependencies": { - "ansi-styles": "^2.2.1", - "escape-string-regexp": "^1.0.2", - "has-ansi": "^2.0.0", - "strip-ansi": "^3.0.0", - "supports-color": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" + "node": ">=10.19.0" } }, - "node_modules/husky/node_modules/escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ= sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", - "dev": true, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, "engines": { - "node": ">=0.8.0" + "node": ">= 14" } }, - "node_modules/husky/node_modules/normalize-path": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-1.0.0.tgz", - "integrity": "sha512-7WyT0w8jhpDStXRq5836AMmihQwq2nrUVQrgjvUo/p/NZf9uy/MeJ246lBJVmWuYXMlJuG9BNZHF0hWjfTbQUA==", + "node_modules/husky": { + "version": "9.1.7", + "resolved": "https://registry.npmjs.org/husky/-/husky-9.1.7.tgz", + "integrity": "sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==", "dev": true, + "license": "MIT", + "bin": { + "husky": "bin.js" + }, "engines": { - "node": ">=0.10.0" + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/typicode" } }, - "node_modules/husky/node_modules/strip-ansi": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", - "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8= sha512-VhumSSbBqDTP8p2ZLKj40UjBCV4+v8bUSEpUb4KjRgWk9pbqGF4REFj6KEagidb2f/M6AzC0EmFyDNGaw9OCzg==", + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-regex": "^2.0.0" + "safer-buffer": ">= 2.1.2 < 3.0.0" }, "engines": { "node": ">=0.10.0" } }, - "node_modules/husky/node_modules/supports-color": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", - "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc= sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==", - "dev": true, - "engines": { - "node": ">=0.8.0" - } - }, "node_modules/icss-utils": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/icss-utils/-/icss-utils-5.1.0.tgz", @@ -10100,10 +10410,11 @@ "dev": true }, "node_modules/import-fresh": { - "version": "3.3.0", - "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", - "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", + "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, + "license": "MIT", "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -10174,21 +10485,13 @@ } }, "node_modules/interpret": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-1.4.0.tgz", - "integrity": "sha512-agE4QfB2Lkp9uICn7BAqoscw4SZP9kTE2hxiFI3jBPmXJfdqiahTbUuKGsMoN2GtqL9AxhYioAcVvgsb1HvRbA==", - "dev": true, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/invert-kv": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/invert-kv/-/invert-kv-1.0.0.tgz", - "integrity": "sha1-EEqOSqym09jNFXqO+L+rLXo//bY= sha512-xgs2NH9AE66ucSq4cNG1nhSFghr5l6tdL15Pk+jl46bmmBapgoaY/AacXyaDznAqmGL99TiLSQgO/XazFSKYeQ==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", + "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", "dev": true, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=10.13.0" } }, "node_modules/ip-address": { @@ -10216,28 +10519,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-accessor-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz", - "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==", - "deprecated": "Please upgrade to v1.0.1", - "dev": true, - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-accessor-descriptor/node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-arguments": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.1.1.tgz", @@ -10290,18 +10571,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-ci": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/is-ci/-/is-ci-1.2.1.tgz", - "integrity": "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg==", - "dev": true, - "dependencies": { - "ci-info": "^1.5.0" - }, - "bin": { - "is-ci": "bin.js" - } - }, "node_modules/is-core-module": { "version": "2.11.0", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.11.0.tgz", @@ -10314,28 +10583,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/is-data-descriptor": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz", - "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==", - "deprecated": "Please upgrade to v1.0.1", - "dev": true, - "dependencies": { - "kind-of": "^6.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-data-descriptor/node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-date-object": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", @@ -10354,33 +10601,11 @@ "integrity": "sha512-YDoFpuZWu1VRXlsnlYMzKyVRITXj7Ej/V9gXQ2/pAe7X1J7M/RNOqaIYi6qUn+B7nGyB9pDXrv02dsB58d2ZAQ==", "dev": true }, - "node_modules/is-descriptor": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz", - "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==", - "dev": true, - "dependencies": { - "is-accessor-descriptor": "^1.0.0", - "is-data-descriptor": "^1.0.0", - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/is-descriptor/node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/is-docker": { "version": "2.2.1", "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.2.1.tgz", "integrity": "sha512-F+i2BKsFrH66iaUFc0woD8sLy8getkwTwtOBjvs56Cx4CgJDeKQeqfz8wAYiSb8JOprWhHH5p77PbmYCvvUuXQ==", + "dev": true, "bin": { "is-docker": "cli.js" }, @@ -10464,6 +10689,51 @@ "node": ">=0.10.0" } }, + "node_modules/is-in-ssh": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-in-ssh/-/is-in-ssh-1.0.0.tgz", + "integrity": "sha512-jYa6Q9rH90kR1vKB6NM7qqd1mge3Fx4Dhw5TVlK1MUBqhEOuCagrEHMevNuCcbECmXZ0ThXkRm+Ymr51HwEPAw==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "license": "MIT", + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-inside-container/node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "license": "MIT", + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/is-interactive": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-interactive/-/is-interactive-2.0.0.tgz", @@ -10656,6 +10926,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz", "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==", + "dev": true, "dependencies": { "is-docker": "^2.0.0" }, @@ -10685,23 +10956,25 @@ } }, "node_modules/istanbul-lib-coverage": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.0.tgz", - "integrity": "sha512-eOeJ5BHCmHYvQK7xt9GkdHuzuCGS1Y6g9Gvnx3Ym33fz/HpLRYxiS0wHNr+m/MBC8B647Xt608vCDEvhl9c6Mw==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=8" } }, "node_modules/istanbul-lib-instrument": { - "version": "6.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.1.tgz", - "integrity": "sha512-EAMEJBsYuyyztxMxW3g7ugGPkrZsV57v0Hmv3mm1uQsmB+QnZuepg731CRaIgeUVSdmsTngOkSnauNF8p7FIhA==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", + "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "@babel/core": "^7.12.3", - "@babel/parser": "^7.14.7", - "@istanbuljs/schema": "^0.1.2", + "@babel/core": "^7.23.9", + "@babel/parser": "^7.23.9", + "@istanbuljs/schema": "^0.1.3", "istanbul-lib-coverage": "^3.2.0", "semver": "^7.5.4" }, @@ -10745,24 +11018,26 @@ } }, "node_modules/istanbul-lib-source-maps": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", - "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", + "version": "5.0.6", + "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-5.0.6.tgz", + "integrity": "sha512-yg2d+Em4KizZC5niWhQaIomgf5WlL4vOOjZ5xGCmF8SnPE/mDWWXgvRExdcpCgh9lLRRa1/fSYp2ymmbJ1pI+A==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { + "@jridgewell/trace-mapping": "^0.3.23", "debug": "^4.1.1", - "istanbul-lib-coverage": "^3.0.0", - "source-map": "^0.6.1" + "istanbul-lib-coverage": "^3.0.0" }, "engines": { "node": ">=10" } }, "node_modules/istanbul-reports": { - "version": "3.1.6", - "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.6.tgz", - "integrity": "sha512-TLgnMkKg3iTDsQ9PbPTdpfAK2DzjF9mqUG7RMgcQl8oFjad8ob4laGxv5XV5U9MAfx8D6tSJiUyuAwzLicaxlg==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "html-escaper": "^2.0.0", "istanbul-lib-report": "^3.0.0" @@ -10772,16 +11047,20 @@ } }, "node_modules/istextorbinary": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-1.0.2.tgz", - "integrity": "sha1-rOGTVNGpoBc+/rEITOD4ewrX3s8= sha512-qZ5ptUDuni2pdCngFTraYa5kalQ0mX47Mhn08tT0DZZv/7yhX1eMb9lFtXVbWhFtgRtpLG/UdqVAjh9teO5x+w==", + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-3.3.0.tgz", + "integrity": "sha512-Tvq1W6NAcZeJ8op+Hq7tdZ434rqnMx4CCZ7H0ff83uEloDvVbqAwaMTZcafKGJT0VHkYzuXUiCY4hlXQg6WfoQ==", "dev": true, + "license": "MIT", "dependencies": { - "binaryextensions": "~1.0.0", - "textextensions": "~1.0.0" + "binaryextensions": "^2.2.0", + "textextensions": "^3.2.0" }, "engines": { - "node": ">=0.4" + "node": ">=8" + }, + "funding": { + "url": "https://bevry.me/fund" } }, "node_modules/jackspeak": { @@ -10846,67 +11125,123 @@ "integrity": "sha512-7rCnleh0z2CkXhH67J8K1Ytz0b2Y+yxTPL+/KOJoa20hfnVQ/3/T6W/KflYI4bRHRagNeXeU2bkNGI3v1oS/lw==" }, "node_modules/js-beautify": { - "version": "1.8.9", - "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.8.9.tgz", - "integrity": "sha512-MwPmLywK9RSX0SPsUJjN7i+RQY9w/yC17Lbrq9ViEefpLRgqAR2BgrMN2AbifkUuhDV8tRauLhLda/9+bE0YQA==", + "version": "1.15.4", + "resolved": "https://registry.npmjs.org/js-beautify/-/js-beautify-1.15.4.tgz", + "integrity": "sha512-9/KXeZUKKJwqCXUdBxFJ3vPh467OCckSBmYDwSK/EtV090K+iMJ7zx2S3HLVDIWFQdqMIsZWbnaGiba18aWhaA==", "dev": true, + "license": "MIT", "dependencies": { - "config-chain": "^1.1.12", - "editorconfig": "^0.15.2", - "glob": "^7.1.3", - "mkdirp": "~0.5.0", - "nopt": "~4.0.1" + "config-chain": "^1.1.13", + "editorconfig": "^1.0.4", + "glob": "^10.4.2", + "js-cookie": "^3.0.5", + "nopt": "^7.2.1" }, "bin": { "css-beautify": "js/bin/css-beautify.js", "html-beautify": "js/bin/html-beautify.js", "js-beautify": "js/bin/js-beautify.js" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/js-beautify/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" } }, "node_modules/js-beautify/node_modules/glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "deprecated": "Glob versions prior to v9 are no longer supported", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "dev": true, + "license": "ISC", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, - "engines": { - "node": "*" + "bin": { + "glob": "dist/esm/bin.mjs" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/js-beautify/node_modules/mkdirp": { - "version": "0.5.5", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", - "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "node_modules/js-beautify/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { - "minimist": "^1.2.5" + "@isaacs/cliui": "^8.0.2" }, - "bin": { - "mkdirp": "bin/cmd.js" + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/js-beautify/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/js-beautify/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/js-cookie": { + "version": "3.0.5", + "resolved": "https://registry.npmjs.org/js-cookie/-/js-cookie-3.0.5.tgz", + "integrity": "sha512-cEiJEAEoIbWfCZYKWhVwFuvPX1gETRYPw6LlaTKoxD3s2AkXzkCjnp6h0V77ozyqj0jakteJ4YqDJT830+lVGw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=14" } }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -10929,24 +11264,26 @@ } }, "node_modules/jsdoc-type-pratt-parser": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-4.1.0.tgz", - "integrity": "sha512-Hicd6JK5Njt2QB6XYFS7ok9e37O8AYk3jTcppG4YVQnYjOemymvTcmc7OWsmq/Qqj5TdRFO5/x/tIPmBeRtGHg==", + "version": "6.10.0", + "resolved": "https://registry.npmjs.org/jsdoc-type-pratt-parser/-/jsdoc-type-pratt-parser-6.10.0.tgz", + "integrity": "sha512-+LexoTRyYui5iOhJGn13N9ZazL23nAHGkXsa1p/C8yeq79WRfLBag6ZZ0FQG2aRoc9yfo59JT9EYCQonOkHKkQ==", "dev": true, + "license": "MIT", "engines": { - "node": ">=12.0.0" + "node": ">=20.0.0" } }, "node_modules/jsesc": { - "version": "2.5.2", - "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-2.5.2.tgz", - "integrity": "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", + "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "dev": true, + "license": "MIT", "bin": { "jsesc": "bin/jsesc" }, "engines": { - "node": ">=4" + "node": ">=6" } }, "node_modules/json-buffer": { @@ -11038,21 +11375,9 @@ "isarray": "~1.0.0", "process-nextick-args": "~2.0.0", "safe-buffer": "~5.1.1", - "string_decoder": "~1.1.1", - "util-deprecate": "~1.0.1" - } - }, - "node_modules/just-debounce": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/just-debounce/-/just-debounce-1.0.0.tgz", - "integrity": "sha1-h/zPrv/AtozRnVX2cilD+SnqNeo= sha512-/QLqfspz7WJ+TPmzDp5WJOlm2r3j+/12rGo7dG5uwD9vGM5sWg8p251b7Us0p19JqjddJzcYOK2v6FN92nREmg==", - "dev": true - }, - "node_modules/just-extend": { - "version": "4.2.1", - "resolved": "https://registry.npmjs.org/just-extend/-/just-extend-4.2.1.tgz", - "integrity": "sha512-g3UB796vUFIY90VIv/WX3L2c8CS2MdWUww3CNrYmqza1Fg0DURc2K/O4YrnklBdQarSJ/y8JnJYDGc+1iumQjg==", - "dev": true + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } }, "node_modules/kerberos": { "version": "2.1.1", @@ -11072,7 +11397,9 @@ "version": "1.1.0", "resolved": "https://registry.npmjs.org/keygrip/-/keygrip-1.1.0.tgz", "integrity": "sha512-iYSchDJ+liQ8iwbSI2QqsQOvqv58eJCEanyJPJi+Khyu8smkcKSFUCbPwzFcL7YVtZ6eONjqRX/38caJ7QjRAQ==", + "deprecated": "Package no longer supported. Contact Support at https://www.npmjs.com/support for more info.", "dev": true, + "license": "MIT", "dependencies": { "tsscmp": "1.0.6" }, @@ -11090,67 +11417,51 @@ } }, "node_modules/kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", + "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/koa": { - "version": "2.15.4", - "resolved": "https://registry.npmjs.org/koa/-/koa-2.15.4.tgz", - "integrity": "sha512-7fNBIdrU2PEgLljXoPWoyY4r1e+ToWCmzS/wwMPbUNs7X+5MMET1ObhJBlUkF5uZG9B6QhM2zS1TsH6adegkiQ==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/koa/-/koa-3.1.1.tgz", + "integrity": "sha512-KDDuvpfqSK0ZKEO2gCPedNjl5wYpfj+HNiuVRlbhd1A88S3M0ySkdf2V/EJ4NWt5dwh5PXCdcenrKK2IQJAxsg==", "dev": true, "license": "MIT", "dependencies": { - "accepts": "^1.3.5", - "cache-content-type": "^1.0.0", - "content-disposition": "~0.5.2", - "content-type": "^1.0.4", - "cookies": "~0.9.0", - "debug": "^4.3.2", + "accepts": "^1.3.8", + "content-disposition": "~0.5.4", + "content-type": "^1.0.5", + "cookies": "~0.9.1", "delegates": "^1.0.0", - "depd": "^2.0.0", - "destroy": "^1.0.4", - "encodeurl": "^1.0.2", + "destroy": "^1.2.0", + "encodeurl": "^2.0.0", "escape-html": "^1.0.3", "fresh": "~0.5.2", - "http-assert": "^1.3.0", - "http-errors": "^1.6.3", - "is-generator-function": "^1.0.7", + "http-assert": "^1.5.0", + "http-errors": "^2.0.0", "koa-compose": "^4.1.0", - "koa-convert": "^2.0.0", - "on-finished": "^2.3.0", - "only": "~0.0.2", - "parseurl": "^1.3.2", - "statuses": "^1.5.0", - "type-is": "^1.6.16", + "mime-types": "^3.0.1", + "on-finished": "^2.4.1", + "parseurl": "^1.3.3", + "statuses": "^2.0.1", + "type-is": "^2.0.1", "vary": "^1.1.2" }, "engines": { - "node": "^4.8.4 || ^6.10.1 || ^7.10.1 || >= 8.1.4" + "node": ">= 18" } }, "node_modules/koa-compose": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/koa-compose/-/koa-compose-4.1.0.tgz", "integrity": "sha512-8ODW8TrDuMYvXRwra/Kh7/rJo9BtOfPc6qO8eAfC80CnCvSjSl0bkRM24X6/XBBEyj0v1nRUQ1LyOy3dbqOWXw==", - "dev": true - }, - "node_modules/koa-convert": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/koa-convert/-/koa-convert-2.0.0.tgz", - "integrity": "sha512-asOvN6bFlSnxewce2e/DK3p4tltyfC4VM7ZwuTuepI7dEQVcvpyFuBcEARu1+Hxg8DIwytce2n7jrZtRlPrARA==", "dev": true, - "dependencies": { - "co": "^4.6.0", - "koa-compose": "^4.1.0" - }, - "engines": { - "node": ">= 10" - } + "license": "MIT" }, "node_modules/koa-morgan": { "version": "1.0.1", @@ -11162,10 +11473,11 @@ } }, "node_modules/koa-mount": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/koa-mount/-/koa-mount-4.0.0.tgz", - "integrity": "sha512-rm71jaA/P+6HeCpoRhmCv8KVBIi0tfGuO/dMKicbQnQW/YJntJ6MnnspkodoA4QstMVEZArsCphmd0bJEtoMjQ==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/koa-mount/-/koa-mount-4.2.0.tgz", + "integrity": "sha512-2iHQc7vbA9qLeVq5gKAYh3m5DOMMlMfIKjW/REPAS18Mf63daCJHHVXY9nbu7ivrnYn5PiPC4CE523Tf5qvjeQ==", "dev": true, + "license": "MIT", "dependencies": { "debug": "^4.0.1", "koa-compose": "^4.1.0" @@ -11244,58 +11556,49 @@ "ms": "^2.1.1" } }, - "node_modules/koa/node_modules/http-errors": { - "version": "1.8.1", - "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.8.1.tgz", - "integrity": "sha512-Kpk9Sm7NmI+RHhnj6OIWDI1d6fIoFAtFt9RLaTMRlg/8w49juAStsrBgp0Dp4OdxdVbRIeKhtCUvoi/RuAhO4g==", - "dev": true, - "dependencies": { - "depd": "~1.1.2", - "inherits": "2.0.4", - "setprototypeof": "1.2.0", - "statuses": ">= 1.5.0 < 2", - "toidentifier": "1.0.1" - }, - "engines": { - "node": ">= 0.6" - } - }, - "node_modules/koa/node_modules/http-errors/node_modules/depd": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", - "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak= sha512-7emPTl6Dpo6JRXOXjLRxck+FlLRX5847cLKEn00PLAgc3g2hTZZgr+e4c2v6QpSmLeFP3n5yUo7ft6avBK/5jQ==", + "node_modules/koa/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } }, - "node_modules/koa/node_modules/statuses": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", - "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow= sha512-OpZ3zP+jT1PI7I8nemJX4AKmAX070ZkYPVWV/AaKTJl+tXCTGyVdC1a4SL8RUQYEwk/f34ZX8UTykN68FwrqAA==", + "node_modules/koa/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, "engines": { - "node": ">= 0.6" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/last-run": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/last-run/-/last-run-1.1.1.tgz", - "integrity": "sha1-RblpQsF7HHnHchmCWbqUO+v4yls= sha512-U/VxvpX4N/rFvPzr3qG5EtLKEnNI0emvIQB3/ecEwv+8GHaUKbIB8vxv1Oai5FAF0d0r7LXHhLLe5K/yChm5GQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/last-run/-/last-run-2.0.0.tgz", + "integrity": "sha512-j+y6WhTLN4Itnf9j5ZQos1BGPCS8DAwmgMroR3OzfxAsBxam0hMw7J8M3KqZl0pLQJ1jNnwIexg5DYpC/ctwEQ==", "dev": true, - "dependencies": { - "default-resolution": "^2.0.0", - "es6-weak-map": "^2.0.1" - }, + "license": "MIT", "engines": { - "node": ">= 0.10" + "node": ">= 10.13.0" } }, "node_modules/lazy.js": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/lazy.js/-/lazy.js-0.4.3.tgz", - "integrity": "sha1-h/Z6B602VVEh5P/xUg3zG+Znhtg= sha512-kHcnVaCZzhv6P+YgC4iRZFw62+biYIcBYU8qqKzJysC7cdKwPgb3WRtcBPyINTSLZwsjyFdBtd97sHbkseTZKw==", - "dev": true + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/lazy.js/-/lazy.js-0.5.1.tgz", + "integrity": "sha512-p9v24vaKrzS2mEx3yuzva/3M6I3+HwvXd0pB1Xf/IvsFIMdhQgmym7JBO0e7c0OZmKTo07sCuiCIm6jazSWFNw==", + "dev": true, + "license": "MIT" }, "node_modules/lazystream": { "version": "1.0.0", @@ -11324,18 +11627,6 @@ "util-deprecate": "~1.0.1" } }, - "node_modules/lcid": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/lcid/-/lcid-1.0.0.tgz", - "integrity": "sha1-MIrMr6C8SDo4Z7S28rlQYlHRuDU= sha512-YiGkH6EnGrDGqLMITnGjXtGmNtjoXw9SVUzcaos8RBi7Ps0VBylkq+vOcY9QE5poLasPCR849ucFUkl0UzUyOw==", - "dev": true, - "dependencies": { - "invert-kv": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/lead": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/lead/-/lead-1.0.0.tgz", @@ -11371,34 +11662,22 @@ } }, "node_modules/liftoff": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-3.1.0.tgz", - "integrity": "sha512-DlIPlJUkCV0Ips2zf2pJP0unEoT1kwYhiiPUGF3s/jtxTCjziNLoiVVh+jqWOWeFi6mmwQ5fNxvAUyPad4Dfog==", - "dev": true, - "dependencies": { - "extend": "^3.0.0", - "findup-sync": "^3.0.0", - "fined": "^1.0.1", - "flagged-respawn": "^1.0.0", - "is-plain-object": "^2.0.4", - "object.map": "^1.0.0", - "rechoir": "^0.6.2", - "resolve": "^1.1.7" - }, - "engines": { - "node": ">= 0.8" - } - }, - "node_modules/liftoff/node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/liftoff/-/liftoff-5.0.1.tgz", + "integrity": "sha512-wwLXMbuxSF8gMvubFcFRp56lkFV69twvbU5vDPbaw+Q+/rF8j0HKjGbIdlSi+LuJm9jf7k9PB+nTxnsLMPcv2Q==", "dev": true, + "license": "MIT", "dependencies": { - "isobject": "^3.0.1" + "extend": "^3.0.2", + "findup-sync": "^5.0.0", + "fined": "^2.0.0", + "flagged-respawn": "^2.0.0", + "is-plain-object": "^5.0.0", + "rechoir": "^0.8.0", + "resolve": "^1.20.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=10.13.0" } }, "node_modules/lilconfig": { @@ -11477,16 +11756,18 @@ } }, "node_modules/lodash": { - "version": "4.17.21", - "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", - "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", - "dev": true + "version": "4.17.23", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.23.tgz", + "integrity": "sha512-LgVTMpQtIopCi79SJeDiP0TfWi5CNEc/L/aRdTh3yIvmZXTnheWpKjSZhnvMl8iXbC1tFg9gdHHDMLoV7CnG+w==", + "dev": true, + "license": "MIT" }, "node_modules/lodash.camelcase": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz", - "integrity": "sha1-soqmKIorn8ZRA1x3EfZathkDMaY= sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", - "dev": true + "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==", + "dev": true, + "license": "MIT" }, "node_modules/lodash.clone": { "version": "4.5.0", @@ -11500,12 +11781,6 @@ "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8= sha512-H5ZhCF25riFd9uB5UCkVKo61m3S/xZk1x4wA6yp/L3RFP6Z/eHH1ymQcGLo7J3GMPfm0V/7m1tryHuGVxpqEBQ==", "dev": true }, - "node_modules/lodash.get": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz", - "integrity": "sha1-LRd/ZS+jHpObRDjVNBSZ36OCXpk= sha512-z+Uw/vLuy6gQe8cfaFWD7p0wVv8fJl3mbzXh33RS+0oW2wvUqiRXiQ69gLWSLpgB5/6sU+r6BlQR0MBILadqTQ==", - "dev": true - }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", @@ -11523,324 +11798,114 @@ "version": "4.6.0", "resolved": "https://registry.npmjs.org/lodash.some/-/lodash.some-4.6.0.tgz", "integrity": "sha1-G7nzFO9ri63tE7VJFpsqlF62jk0= sha512-j7MJE+TuT51q9ggt4fSgVqro163BEFjAt3u97IqU+JA2DkWl80nFTrowzLpZ/BnpN7rrl0JA/593NAdd8p/scQ==", - "dev": true - }, - "node_modules/lodash.uniq": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", - "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/log-symbols": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", - "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", - "dev": true, - "dependencies": { - "chalk": "^4.1.0", - "is-unicode-supported": "^0.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/lowercase-keys": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", - "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/lru-cache": { - "version": "4.1.5", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-4.1.5.tgz", - "integrity": "sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==", - "dev": true, - "dependencies": { - "pseudomap": "^1.0.2", - "yallist": "^2.1.2" - } - }, - "node_modules/lru-queue": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", - "integrity": "sha1-Jzi9nw089PhEkMVzbEhpmsYyzaM= sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==", - "dev": true, - "dependencies": { - "es5-ext": "~0.10.2" - } - }, - "node_modules/make-dir": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", - "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", - "dev": true, - "dependencies": { - "semver": "^7.5.3" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/make-error": { - "version": "1.3.6", - "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", - "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", - "dev": true - }, - "node_modules/make-iterator": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/make-iterator/-/make-iterator-1.0.1.tgz", - "integrity": "sha512-pxiuXh0iVEq7VM7KMIhs5gxsfxCux2URptUQaXo4iZZJxBAzTPOLE2BumO5dbfVYq/hBJFBR/a1mFDmOx5AGmw==", - "dev": true, - "dependencies": { - "kind-of": "^6.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/make-iterator/node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/map-cache": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", - "integrity": "sha1-wyq9C9ZSXZsFFkW7TyasXcmKDb8= sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/map-stream": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.1.0.tgz", - "integrity": "sha1-5WqpTEyAVaFkBKBnS3jyFffI4ZQ= sha512-CkYQrPYZfWnu/DAmVCpTSX/xHpKZ80eKh2lAkyA6AJTef6bW+6JpbQZN5rofum7da+SyN1bi5ctTm+lTfcCW3g==", - "dev": true - }, - "node_modules/map-visit": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/map-visit/-/map-visit-1.0.0.tgz", - "integrity": "sha1-7Nyo8TFE5mDxtb1B8S80edmN+48= sha512-4y7uGv8bd2WdM9vpQsiQNo41Ln1NvhvDRuVt0k2JZQ+ezN2uaQes7lZeZ+QQUHOLQAtDaBJ+7wCbi+ab/KFs+w==", - "dev": true, - "dependencies": { - "object-visit": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/markdown-it": { - "version": "14.1.0", - "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", - "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1", - "entities": "^4.4.0", - "linkify-it": "^5.0.0", - "mdurl": "^2.0.0", - "punycode.js": "^2.3.1", - "uc.micro": "^2.1.0" - }, - "bin": { - "markdown-it": "bin/markdown-it.mjs" - } - }, - "node_modules/matchdep": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/matchdep/-/matchdep-2.0.0.tgz", - "integrity": "sha1-xvNINKDY28OzfCfui7yyfHd1WC4= sha512-LFgVbaHIHMqCRuCZyfCtUOq9/Lnzhi7Z0KFUE2fhD54+JN2jLh3hC02RLkqauJ3U4soU6H1J3tfj/Byk7GoEjA==", - "dev": true, - "dependencies": { - "findup-sync": "^2.0.0", - "micromatch": "^3.0.4", - "resolve": "^1.4.0", - "stack-trace": "0.0.10" - }, - "engines": { - "node": ">= 0.10.0" - } - }, - "node_modules/matchdep/node_modules/braces": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-2.3.2.tgz", - "integrity": "sha512-aNdbnj9P8PjdXU4ybaWLK2IF3jc/EoDYbC7AazW6to3TRsfXxscC9UXOB5iDiEQrkyIbWp2SLQda4+QAa7nc3w==", - "dev": true, - "dependencies": { - "arr-flatten": "^1.1.0", - "array-unique": "^0.3.2", - "extend-shallow": "^2.0.1", - "fill-range": "^4.0.0", - "isobject": "^3.0.1", - "repeat-element": "^1.1.2", - "snapdragon": "^0.8.1", - "snapdragon-node": "^2.0.1", - "split-string": "^3.0.2", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/matchdep/node_modules/braces/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } + "dev": true }, - "node_modules/matchdep/node_modules/fill-range": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz", - "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc= sha512-VcpLTWqWDiTerugjj8e3+esbg+skS3M9e54UuR3iCeIDMXCLTsAH8hTSzDQU/X6/6t3eYkOKoZSef2PlU6U1XQ==", + "node_modules/lodash.uniq": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz", + "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==", "dev": true, - "dependencies": { - "extend-shallow": "^2.0.1", - "is-number": "^3.0.0", - "repeat-string": "^1.6.1", - "to-regex-range": "^2.1.0" - }, - "engines": { - "node": ">=0.10.0" - } + "license": "MIT" }, - "node_modules/matchdep/node_modules/fill-range/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", + "node_modules/log-symbols": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, "dependencies": { - "is-extendable": "^0.1.0" + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" }, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/matchdep/node_modules/findup-sync": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/findup-sync/-/findup-sync-2.0.0.tgz", - "integrity": "sha1-kyaxSIwi0aYIhlCoaQGy2akKLLw= sha512-vs+3unmJT45eczmcAZ6zMJtxN3l/QXeccaXQx5cu/MeJMhewVfoWZqibRkOxPnmoR59+Zy5hjabfQc6JLSah4g==", - "dev": true, - "dependencies": { - "detect-file": "^1.0.0", - "is-glob": "^3.1.0", - "micromatch": "^3.0.4", - "resolve-dir": "^1.0.1" + "node": ">=10" }, - "engines": { - "node": ">= 0.10" + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/matchdep/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "node_modules/lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/matchdep/node_modules/is-glob": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-3.1.0.tgz", - "integrity": "sha1-e6WuJCF4BKxwcHuWkiVnSGzD6Eo= sha512-UFpDDrPgM6qpnFNI+rh/p3bUaq9hKLZN8bMUWzxmcnZVS3omf4IPK+BrewlnWjO1WmUsMYuSjKh4UJuV4+Lqmw==", - "dev": true, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", "dependencies": { - "is-extglob": "^2.1.0" + "yallist": "^4.0.0" }, "engines": { - "node": ">=0.10.0" + "node": ">=10" } }, - "node_modules/matchdep/node_modules/is-number": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-3.0.0.tgz", - "integrity": "sha1-JP1iAaR4LPUFYcgQJ2r8fRLXEZU= sha512-4cboCqIpliH+mAvFNegjZQ4kgKc3ZUhQVr3HvWbSh5q3WH2v82ct+T2Y1hdU5Gdtorx/cLifQjqCbL7bpznLTg==", + "node_modules/lru-queue": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/lru-queue/-/lru-queue-0.1.0.tgz", + "integrity": "sha1-Jzi9nw089PhEkMVzbEhpmsYyzaM= sha512-BpdYkt9EvGl8OfWHDQPISVpcl5xZthb+XPsbELj5AQXxIC8IriDZIQYjBJPEm5rS420sjZ0TLEzRcq5KdBhYrQ==", "dev": true, "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" + "es5-ext": "~0.10.2" } }, - "node_modules/matchdep/node_modules/is-number/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", "dev": true, "dependencies": { - "is-buffer": "^1.1.5" + "semver": "^7.5.3" }, "engines": { - "node": ">=0.10.0" + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/matchdep/node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", + "node_modules/make-error": { + "version": "1.3.6", + "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", + "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", + "dev": true + }, + "node_modules/map-cache": { + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/map-cache/-/map-cache-0.2.2.tgz", + "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, - "node_modules/matchdep/node_modules/micromatch": { - "version": "3.1.10", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-3.1.10.tgz", - "integrity": "sha512-MWikgl9n9M3w+bpsY3He8L+w9eF9338xRl8IAO5viDizwSzziFEyUzo2xrrloB64ADbTf8uA8vRqqttDTOmccg==", + "node_modules/map-stream": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/map-stream/-/map-stream-0.0.7.tgz", + "integrity": "sha512-C0X0KQmGm3N2ftbTGBhSyuydQ+vV1LC3f3zPvT3RXHXNZrvfPZcoXp/N5DOa8vedX/rTMm2CjTtivFg2STJMRQ==", "dev": true, - "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "braces": "^2.3.1", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "extglob": "^2.0.4", - "fragment-cache": "^0.2.1", - "kind-of": "^6.0.2", - "nanomatch": "^1.2.9", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } + "license": "MIT" }, - "node_modules/matchdep/node_modules/to-regex-range": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-2.1.1.tgz", - "integrity": "sha1-fIDBe53+vlmeJzZ+DU3VWQFB2zg= sha512-ZZWNfCjUokXXDGXFpZehJIkZqq91BcULFq/Pi7M5i4JnxXdhMKAK682z8bCW3o8Hj1wuuzoKcW3DfVzaP6VuNg==", + "node_modules/markdown-it": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.1.0.tgz", + "integrity": "sha512-a54IwgWPaeBCAAsv13YgmALOF1elABB08FxO9i+r4VFk5Vl4pKokRPeX8u5TCgSsPi6ec1otfLjdOpVcgbpshg==", "dev": true, "dependencies": { - "is-number": "^3.0.0", - "repeat-string": "^1.6.1" + "argparse": "^2.0.1", + "entities": "^4.4.0", + "linkify-it": "^5.0.0", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" }, - "engines": { - "node": ">=0.10.0" + "bin": { + "markdown-it": "bin/markdown-it.mjs" } }, "node_modules/matcher": { @@ -11856,6 +11921,16 @@ "node": ">=10" } }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/md5": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/md5/-/md5-2.3.0.tgz", @@ -11880,12 +11955,13 @@ "dev": true }, "node_modules/media-typer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", - "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g= sha512-dq+qelQ9akHpcOl/gUVRTxVIOkAJ1wR3QAvb4RsVjS8oVoFjDGTc679wJYmUmknUF5HwMLOgb5O+a3KxfWapPQ==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-1.1.0.tgz", + "integrity": "sha512-aisnrDP4GNe06UcKFnV5bfMNPBUw4jsLGaWwWfnH3v02GnBuXX2MCVn5RbrWo0j3pczUilYblq7fQ7Nw2t5XKw==", "dev": true, + "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">= 0.8" } }, "node_modules/memoizee": { @@ -11994,21 +12070,23 @@ } }, "node_modules/mime-db": { - "version": "1.45.0", - "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.45.0.tgz", - "integrity": "sha512-CkqLUxUk15hofLoLyljJSrukZi8mAtgd+yE5uO4tqRZsdsAJKv0O+rFMhVDRJgozy+yG6md5KwuXhD4ocIoP+w==", + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } }, "node_modules/mime-types": { - "version": "2.1.28", - "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.28.tgz", - "integrity": "sha512-0TO2yJ5YHYr7M2zzT7gDU1tbwHxEUWBCLt0lscSNpcdAfFyJOVEpRYNS7EXVcTLNj/25QO8gulHC5JtTzSE2UQ==", + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", "dev": true, + "license": "MIT", "dependencies": { - "mime-db": "1.45.0" + "mime-db": "1.52.0" }, "engines": { "node": ">= 0.6" @@ -12033,15 +12111,19 @@ } }, "node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "version": "10.1.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.1.1.tgz", + "integrity": "sha512-enIvLvRAFZYXJzkCYG5RKmPfrFArdLv+R+lbQ53BmIMLIry74bjKzX6iHAm8WYamJkhSSEabrWN5D97XnKObjQ==", "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^1.1.7" + "@isaacs/brace-expansion": "^5.0.0" }, "engines": { - "node": "*" + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/minimist": { @@ -12083,24 +12165,6 @@ "node": ">=8" } }, - "node_modules/minizlib/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, - "node_modules/mixin-deep": { - "version": "1.3.2", - "resolved": "https://registry.npmjs.org/mixin-deep/-/mixin-deep-1.3.2.tgz", - "integrity": "sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA==", - "dev": true, - "dependencies": { - "for-in": "^1.0.2", - "is-extendable": "^1.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/mkdirp": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", @@ -12118,31 +12182,32 @@ "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" }, "node_modules/mocha": { - "version": "10.8.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz", - "integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==", + "version": "11.7.5", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.5.tgz", + "integrity": "sha512-mTT6RgopEYABzXWFx+GcJ+ZQ32kp4fMf0xvpZIIfSq9Z8lC/++MtcCnQ9t5FP2veYEP95FIYSvW+U9fV4xrlig==", "dev": true, "license": "MIT", "dependencies": { - "ansi-colors": "^4.1.3", "browser-stdout": "^1.3.1", - "chokidar": "^3.5.3", + "chokidar": "^4.0.1", "debug": "^4.3.5", - "diff": "^5.2.0", + "diff": "^7.0.0", "escape-string-regexp": "^4.0.0", "find-up": "^5.0.0", - "glob": "^8.1.0", + "glob": "^10.4.5", "he": "^1.2.0", + "is-path-inside": "^3.0.3", "js-yaml": "^4.1.0", "log-symbols": "^4.1.0", - "minimatch": "^5.1.6", + "minimatch": "^9.0.5", "ms": "^2.1.3", + "picocolors": "^1.1.1", "serialize-javascript": "^6.0.2", "strip-json-comments": "^3.1.1", "supports-color": "^8.1.1", - "workerpool": "^6.5.1", - "yargs": "^16.2.0", - "yargs-parser": "^20.2.9", + "workerpool": "^9.2.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", "yargs-unparser": "^2.0.0" }, "bin": { @@ -12150,7 +12215,7 @@ "mocha": "bin/mocha.js" }, "engines": { - "node": ">= 14.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/mocha-junit-reporter": { @@ -12200,59 +12265,48 @@ "mocha": ">=3.1.2" } }, - "node_modules/mocha/node_modules/ansi-colors": { - "version": "4.1.3", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", - "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", - "dev": true, - "license": "MIT", - "engines": { - "node": ">=6" - } - }, "node_modules/mocha/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } }, - "node_modules/mocha/node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "node_modules/mocha/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", "dev": true, + "license": "MIT", "dependencies": { - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", - "wrap-ansi": "^7.0.0" + "readdirp": "^4.0.1" + }, + "engines": { + "node": ">= 14.16.0" + }, + "funding": { + "url": "https://paulmillr.com/funding/" } }, - "node_modules/mocha/node_modules/emoji-regex": { - "version": "8.0.0", - "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", - "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", - "dev": true - }, "node_modules/mocha/node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "deprecated": "Glob versions prior to v9 are no longer supported", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "dev": true, "license": "ISC", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" }, - "engines": { - "node": ">=12" + "bin": { + "glob": "dist/esm/bin.mjs" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -12267,87 +12321,89 @@ "node": ">=8" } }, - "node_modules/mocha/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "node_modules/mocha/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", "dev": true, - "license": "ISC", + "license": "BlueOak-1.0.0", "dependencies": { - "brace-expansion": "^2.0.1" + "@isaacs/cliui": "^8.0.2" }, - "engines": { - "node": ">=10" - } - }, - "node_modules/mocha/node_modules/string-width": { - "version": "4.2.3", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", - "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", - "dev": true, - "dependencies": { - "emoji-regex": "^8.0.0", - "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.1" + "funding": { + "url": "https://github.com/sponsors/isaacs" }, - "engines": { - "node": ">=8" + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" } }, - "node_modules/mocha/node_modules/supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "node_modules/mocha/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, + "license": "ISC", "dependencies": { - "has-flag": "^4.0.0" + "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.17" }, "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/mocha/node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "node_modules/mocha/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, - "dependencies": { - "cliui": "^7.0.2", - "escalade": "^3.1.1", - "get-caller-file": "^2.0.5", - "require-directory": "^2.1.1", - "string-width": "^4.2.0", - "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" - }, + "license": "ISC", "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.17" } }, - "node_modules/mocha/node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "node_modules/mocha/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" + } + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, - "license": "ISC", + "dependencies": { + "has-flag": "^4.0.0" + }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, "node_modules/morgan": { - "version": "1.10.0", - "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.0.tgz", - "integrity": "sha512-AbegBVI4sh6El+1gNwvD5YIck7nSA36weD7xvIxG4in80j/UoK8AEGaWnnz8v1GxonMCltmlNs5ZKbGvl9b1XQ==", + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/morgan/-/morgan-1.10.1.tgz", + "integrity": "sha512-223dMRJtI/l25dJKWpgij2cMtywuG/WiUKXdvwfbhGKBhy1puASqXwFzmWZ7+K73vUPoR7SS2Qz2cI/g9MKw0A==", "dev": true, + "license": "MIT", "dependencies": { "basic-auth": "~2.0.1", "debug": "2.6.9", "depd": "~2.0.0", "on-finished": "~2.3.0", - "on-headers": "~1.0.2" + "on-headers": "~1.1.0" }, "engines": { "node": ">= 0.8.0" @@ -12413,21 +12469,28 @@ "node": ">=0.10.0" } }, - "node_modules/mute-stdout": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-1.0.1.tgz", - "integrity": "sha512-kDcwXR4PS7caBpuRYYBUz9iVixUk3anO3f5OYFiIPwK/20vCzKCHyKoulbiDY1S53zD2bxUpxN/IJ+TnXjfvxg==", + "node_modules/multimatch/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, "engines": { - "node": ">= 0.10" + "node": "*" } }, - "node_modules/nan": { - "version": "2.14.2", - "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.2.tgz", - "integrity": "sha512-M2ufzIiINKCuDfBSAUr1vWQ+vuVcA9kqx8JJUsbQi6yf1uGRyb7HfpdfUr5qLXf3B/t8dPvcjhKMmlfnP47EzQ==", + "node_modules/mute-stdout": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/mute-stdout/-/mute-stdout-2.0.0.tgz", + "integrity": "sha512-32GSKM3Wyc8dg/p39lWPKYu8zci9mJFzV1Np9Of0ZEpe6Fhssn/FbI7ywAMd40uX+p3ZKh3T5EeCFv81qS3HmQ==", "dev": true, - "optional": true + "license": "MIT", + "engines": { + "node": ">= 10.13.0" + } }, "node_modules/nanoid": { "version": "3.3.8", @@ -12448,37 +12511,6 @@ "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" } }, - "node_modules/nanomatch": { - "version": "1.2.13", - "resolved": "https://registry.npmjs.org/nanomatch/-/nanomatch-1.2.13.tgz", - "integrity": "sha512-fpoe2T0RbHwBTBUOftAfBPaDEi06ufaUai0mE6Yn1kacc3SnTErfb/h+X94VXzI64rKFHYImXSvdwGGCmwOqCA==", - "dev": true, - "dependencies": { - "arr-diff": "^4.0.0", - "array-unique": "^0.3.2", - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "fragment-cache": "^0.2.1", - "is-windows": "^1.0.2", - "kind-of": "^6.0.2", - "object.pick": "^1.3.0", - "regex-not": "^1.0.0", - "snapdragon": "^0.8.1", - "to-regex": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/nanomatch/node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/napi-build-utils": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", @@ -12509,10 +12541,11 @@ "dev": true }, "node_modules/negotiator": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", - "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.3.tgz", + "integrity": "sha512-+EUsqGPLsM+j/zdChZjsnX51g4XrHFOIXwfnCVPGlQk/k5giakcKsuxCObBRu6DSm9opw/O6slWbJdghQM4bBg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.6" } @@ -12535,34 +12568,6 @@ "integrity": "sha512-1nh45deeb5olNY7eX82BkPO7SSxR5SSYJiPTrTdFUVYwAl8CKMA5N9PjTYkHiRjisVcxcQ1HXdLhx2qxxJzLNQ==", "dev": true }, - "node_modules/nise": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/nise/-/nise-5.1.0.tgz", - "integrity": "sha512-W5WlHu+wvo3PaKLsJJkgPup2LrsXCcm7AWwyNZkUnn5rwPkuPBi3Iwk5SQtN0mv+K65k7nKKjwNQ30wg3wLAQQ==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^1.7.0", - "@sinonjs/fake-timers": "^7.0.4", - "@sinonjs/text-encoding": "^0.7.1", - "just-extend": "^4.0.2", - "path-to-regexp": "^1.7.0" - } - }, - "node_modules/nise/node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8= sha512-D2S+3GLxWH+uhrNEcoh/fnmYeP8E8/zHl644d/jdA0g2uyXvy3sb0qxotE+ne0LtccHknQzWwZEzhak7oJ0COQ==", - "dev": true - }, - "node_modules/nise/node_modules/path-to-regexp": { - "version": "1.9.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.9.0.tgz", - "integrity": "sha512-xIp7/apCFJuUHdDLWe8O1HIkb0kQrOMb/0u6FXQjemHn/ii5LrIzU6bdECnsiTF/GjZkMEKg1xdiZwNqDYlZ6g==", - "dev": true, - "dependencies": { - "isarray": "0.0.1" - } - }, "node_modules/node-abi": { "version": "3.8.0", "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.8.0.tgz", @@ -12626,9 +12631,9 @@ } }, "node_modules/node-pty": { - "version": "1.1.0-beta30", - "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.1.0-beta30.tgz", - "integrity": "sha512-cmNYVWfbf961aOqnxIFXssvw6Fp6/78BQBNlwYRWUHBenJjUhCJ1wMZpJy+SegoLC07P9D6HTtq39Kd89rpv/w==", + "version": "1.1.0-beta31", + "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.1.0-beta31.tgz", + "integrity": "sha512-DwNyk7nQ8NfHX7NrIqvNQ5GiK6eBbsRYJ+hvHK04PTzZ6o5j1Qsc67g0QxXW8tki/ZJmE9Zxw6PEGncvDshdVw==", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -12643,16 +12648,19 @@ "license": "MIT" }, "node_modules/nopt": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/nopt/-/nopt-4.0.1.tgz", - "integrity": "sha1-0NRoWv1UFRk8jHUFYC0NF81kR00= sha512-+5XZFpQZEY0cg5JaxLwGxDlKNKYxuXwGt8/Oi3UXm5/4ymrJve9d2CURituxv3rSrVCGZj4m1U1JlHTdcKt2Ng==", + "version": "7.2.1", + "resolved": "https://registry.npmjs.org/nopt/-/nopt-7.2.1.tgz", + "integrity": "sha512-taM24ViiimT/XntxbPyJQzCG+p4EKOpgD3mxFwW38mGjVUrfERQOeY4EDHjdnptttfHuHQXFx+lTP08Q+mLa/w==", "dev": true, + "license": "ISC", "dependencies": { - "abbrev": "1", - "osenv": "^0.1.4" + "abbrev": "^2.0.0" }, "bin": { "nopt": "bin/nopt.js" + }, + "engines": { + "node": "^14.17.0 || ^16.13.0 || >=18.0.0" } }, "node_modules/normalize-package-data": { @@ -12800,6 +12808,19 @@ "node": ">=0.8.0" } }, + "node_modules/npm-run-all/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/npm-run-all/node_modules/path-key": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-2.0.1.tgz", @@ -12875,15 +12896,6 @@ "url": "https://github.com/fb55/nth-check?sponsor=1" } }, - "node_modules/number-is-nan": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/number-is-nan/-/number-is-nan-1.0.1.tgz", - "integrity": "sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0= sha512-4jbtZXNAsfZbAHiiqjLPBiCl16dES1zI4Hpzzxw61Tk+loF+sBDBKx1ICKKKwIqQ7M0mFn1TmkN7euSncWgHiQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/object-assign": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", @@ -12893,92 +12905,12 @@ "node": ">=0.10.0" } }, - "node_modules/object-copy": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/object-copy/-/object-copy-0.1.0.tgz", - "integrity": "sha1-fn2Fi3gb18mRpBupde04EnVOmYw= sha512-79LYn6VAb63zgtmAteVOWo9Vdj71ZVBy3Pbse+VqxDpEP83XuujMrGqHIwAXJ5I/aM0zU7dIyIAhifVTPrNItQ==", - "dev": true, - "dependencies": { - "copy-descriptor": "^0.1.0", - "define-property": "^0.2.5", - "kind-of": "^3.0.3" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", - "dev": true, - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy/node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", - "deprecated": "Please upgrade to v0.1.7", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy/node_modules/is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", - "deprecated": "Please upgrade to v0.1.5", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy/node_modules/is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy/node_modules/is-descriptor/node_modules/kind-of": { - "version": "5.1.0", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-5.1.0.tgz", - "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/object-copy/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "node_modules/object-deep-merge": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/object-deep-merge/-/object-deep-merge-2.0.0.tgz", + "integrity": "sha512-3DC3UMpeffLTHiuXSy/UG4NOIYTLlY9u3V82+djSCLYClWobZiS4ivYzpIUWrRY/nfsJ8cWsKyG3QfyLePmhvg==", "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } + "license": "MIT" }, "node_modules/object-inspect": { "version": "1.13.2", @@ -13001,18 +12933,6 @@ "node": ">= 0.4" } }, - "node_modules/object-visit": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object-visit/-/object-visit-1.0.1.tgz", - "integrity": "sha1-95xEk68MU3e1n+OdOV5BBC3QRbs= sha512-GBaMwwAVK9qbQN3Scdo0OyvgPW7l3lnaVMj84uTOZlswkX0KpF6fyDBJhtTthf7pymztoN36/KEr1DyhF96zEA==", - "dev": true, - "dependencies": { - "isobject": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/object.assign": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.2.tgz", @@ -13034,8 +12954,9 @@ "node_modules/object.defaults": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/object.defaults/-/object.defaults-1.1.0.tgz", - "integrity": "sha1-On+GgzS0B96gbaFtiNXNKeQ1/s8= sha512-c/K0mw/F11k4dEUBMW8naXUuBuhxRCfG7W+yFy8EcijU/rSmazOUd1XAEEe6bC0OuXY4HUKjTJv7xbxIMqdxrA==", + "integrity": "sha512-c/K0mw/F11k4dEUBMW8naXUuBuhxRCfG7W+yFy8EcijU/rSmazOUd1XAEEe6bC0OuXY4HUKjTJv7xbxIMqdxrA==", "dev": true, + "license": "MIT", "dependencies": { "array-each": "^1.0.1", "array-slice": "^1.0.0", @@ -13046,24 +12967,12 @@ "node": ">=0.10.0" } }, - "node_modules/object.map": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object.map/-/object.map-1.0.1.tgz", - "integrity": "sha1-z4Plncj8wK1fQlDh94s7gb2AHTc= sha512-3+mAJu2PLfnSVGHwIWubpOFLscJANBKuB/6A4CxBstc4aqwQY0FWcsppuy4jU5GSB95yES5JHSI+33AWuS4k6w==", - "dev": true, - "dependencies": { - "for-own": "^1.0.0", - "make-iterator": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/object.pick": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/object.pick/-/object.pick-1.3.0.tgz", - "integrity": "sha1-h6EKxMFpS9Lhy/U1kaZhQftd10c= sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==", + "integrity": "sha512-tqa/UMy/CCoYmj+H5qc07qvSL9dqcs/WZENZ1JbtWBlATP+iVOe778gE6MSijnyCnORzDuX6hU+LA4SZ09YjFQ==", "dev": true, + "license": "MIT", "dependencies": { "isobject": "^3.0.1" }, @@ -13071,24 +12980,12 @@ "node": ">=0.10.0" } }, - "node_modules/object.reduce": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/object.reduce/-/object.reduce-1.0.1.tgz", - "integrity": "sha1-b+NI8qx/oPlcpiEiZZkJaCW7A60= sha512-naLhxxpUESbNkRqc35oQ2scZSJueHGQNUfMW/0U37IgN6tE2dgDWg3whf+NEliy3F/QysrO48XKUz/nGPe+AQw==", - "dev": true, - "dependencies": { - "for-own": "^1.0.0", - "make-iterator": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/on-finished": { "version": "2.4.1", "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz", "integrity": "sha512-oVlzkg3ENAhCk2zdv7IJwd/QUD4z2RxRwpkcGY8psCVcCYZNq4wYnVWALHM+brtuJjePWiYF/ClmuDr8Ch5+kg==", "dev": true, + "license": "MIT", "dependencies": { "ee-first": "1.1.1" }, @@ -13097,10 +12994,11 @@ } }, "node_modules/on-headers": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.0.2.tgz", - "integrity": "sha512-pZAE+FJLoyITytdqK0U5s+FIpjN0JP3OzFi/u8Rx+EV5/W+JTWGXG8xFzevE7AjBfDqHv/8vL8qQsIhHnqRkrA==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/on-headers/-/on-headers-1.1.0.tgz", + "integrity": "sha512-737ZY3yNnXy37FHkQxPzt4UZ2UWPWiCZWLvFZ4fu5cueciegX0zGPnrlY6bwRg4FdQOe9YU8MkmJwGhoMybl8A==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8" } @@ -13128,23 +13026,21 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/only": { - "version": "0.0.2", - "resolved": "https://registry.npmjs.org/only/-/only-0.0.2.tgz", - "integrity": "sha1-Kv3oTQPlC5qO3EROMGEKcCle37Q= sha512-Fvw+Jemq5fjjyWz6CpKx6w9s7xxqo3+JCyM0WXWeCSOboZ8ABkyvP8ID4CZuChA/wxSx+XSJmdOm8rGVyJ1hdQ==", - "dev": true - }, "node_modules/open": { - "version": "8.4.2", - "resolved": "https://registry.npmjs.org/open/-/open-8.4.2.tgz", - "integrity": "sha512-7x81NCL719oNbsq/3mh+hVrAWmFuEYUqrq/Iw3kUzH8ReypT9QQ0BLoJS7/G9k6N81XjW4qHWtjWwe/9eLy1EQ==", + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/open/-/open-11.0.0.tgz", + "integrity": "sha512-smsWv2LzFjP03xmvFoJ331ss6h+jixfA4UUV/Bsiyuu4YJPfN+FIQGOIiv4w9/+MoHkfkJ22UIaQWRVFRfH6Vw==", + "license": "MIT", "dependencies": { - "define-lazy-prop": "^2.0.0", - "is-docker": "^2.1.1", - "is-wsl": "^2.2.0" + "default-browser": "^5.4.0", + "define-lazy-prop": "^3.0.0", + "is-in-ssh": "^1.0.0", + "is-inside-container": "^1.0.0", + "powershell-utils": "^0.1.0", + "wsl-utils": "^0.3.0" }, "engines": { - "node": ">=12" + "node": ">=20" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -13299,57 +13195,20 @@ "integrity": "sha512-gjcpUc3clBf9+210TRaDWbf+rZZZEshZ+DlXMRCeAjp0xhTrnQsKHypIy1J3d5hKdUzj69t708EHtU8P6bUn0A==", "dev": true }, - "node_modules/os-homedir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", - "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M= sha512-B5JU3cabzk8c67mRRd3ECmROafjYMXbuzlwtqdM8IbS8ktlTix8aFGb2bAGKrSRIlnfKwovGUUr72JUPyOb6kQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/os-locale": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/os-locale/-/os-locale-1.4.0.tgz", - "integrity": "sha1-IPnxeuKe00XoveWDsT0gCYA8FNk= sha512-PRT7ZORmwu2MEFt4/fv3Q+mEfN4zetKxufQrkShY2oGvUms9r8otu5HfdyIFHkYXjO7laNsoVGmM2MANfuTA8g==", - "dev": true, - "dependencies": { - "lcid": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/os-tmpdir": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", - "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ= sha512-D2FR03Vir7FIu45XBY20mTb+/ZSWB00sjU9jdQXt83gDrI4Ztz5Fs7/yy74g2N5SVQY4xY1qDr4rNddwYRVX0g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/osenv": { - "version": "0.1.5", - "resolved": "https://registry.npmjs.org/osenv/-/osenv-0.1.5.tgz", - "integrity": "sha512-0CWcCECdMVc2Rw3U5w9ZjqX6ga6ubk1xDVKxtBQPK7wis/0F2r9T6k4ydGYhecl7YUBxBVxhL5oisPsNxAPe2g==", - "deprecated": "This package is no longer supported.", - "dev": true, - "dependencies": { - "os-homedir": "^1.0.0", - "os-tmpdir": "^1.0.0" - } - }, "node_modules/p-all": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/p-all/-/p-all-1.0.0.tgz", - "integrity": "sha1-k731OlWiOCH9+pi0F0qZv38x340= sha512-OtbznqfGjQT+i89LK9C9YPh1G8d6n8xgsJ8yRVXrx6PRXrlOthNJhP+dHxrPopty8fugYb1DodpwrzP7z0Mtvw==", + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/p-all/-/p-all-5.0.1.tgz", + "integrity": "sha512-LMT7WX9ZSaq3J1zjloApkIVmtz0ZdMFSIqbuiEa3txGYPLjUPOvgOPOx3nFjo+f37ZYL+1aY666I2SG7GVwLOA==", "dev": true, + "license": "MIT", "dependencies": { - "p-map": "^1.0.0" + "p-map": "^6.0.0" }, "engines": { - "node": ">=4" + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-cancelable": { @@ -13392,12 +13251,16 @@ } }, "node_modules/p-map": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/p-map/-/p-map-1.2.0.tgz", - "integrity": "sha512-r6zKACMNhjPJMTl8KcFH4li//gkrXWfbD6feV8l6doRHlzljFWGJ2AP6iKaCJXyZmAUMOPtvbW7EXkbWO/pLEA==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-6.0.0.tgz", + "integrity": "sha512-T8BatKGY+k5rU+Q/GTYgrEf2r4xRMevAN5mtXc2aPc4rS1j3s+vWTaO2Wag94neXuCAUAs8cxBL9EeB5EA6diw==", "dev": true, + "license": "MIT", "engines": { - "node": ">=4" + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-try": { @@ -13410,10 +13273,11 @@ } }, "node_modules/package-json-from-dist": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", - "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", - "dev": true + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" }, "node_modules/pako": { "version": "0.2.9", @@ -13426,6 +13290,7 @@ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, + "license": "MIT", "dependencies": { "callsites": "^3.0.0" }, @@ -13436,8 +13301,9 @@ "node_modules/parse-filepath": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/parse-filepath/-/parse-filepath-1.0.2.tgz", - "integrity": "sha1-pjISf1Oq89FYdvWHLz/6x2PWyJE= sha512-FwdRXKCohSVeXqwtYonZTXtbGJKrn+HNyWDYVcp5yuJlesTwNH4rsmRZ+GrKAPJ5bLpRxESMeS+Rl0VCHRvB2Q==", + "integrity": "sha512-FwdRXKCohSVeXqwtYonZTXtbGJKrn+HNyWDYVcp5yuJlesTwNH4rsmRZ+GrKAPJ5bLpRxESMeS+Rl0VCHRvB2Q==", "dev": true, + "license": "MIT", "dependencies": { "is-absolute": "^1.0.0", "map-cache": "^0.2.0", @@ -13447,17 +13313,14 @@ "node": ">=0.8" } }, - "node_modules/parse-imports": { - "version": "2.2.1", - "resolved": "https://registry.npmjs.org/parse-imports/-/parse-imports-2.2.1.tgz", - "integrity": "sha512-OL/zLggRp8mFhKL0rNORUTR4yBYujK/uU+xZL+/0Rgm2QE4nLO9v8PzEweSJEbMGKmDRjJE4R3IMJlL2di4JeQ==", + "node_modules/parse-imports-exports": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/parse-imports-exports/-/parse-imports-exports-0.2.4.tgz", + "integrity": "sha512-4s6vd6dx1AotCx/RCI2m7t7GCh5bDRUtGNvRfHSP2wbBQdMi67pPe7mtzmgwcaQ8VKK/6IB7Glfyu3qdZJPybQ==", "dev": true, + "license": "MIT", "dependencies": { - "es-module-lexer": "^1.5.3", - "slashes": "^3.0.12" - }, - "engines": { - "node": ">= 18" + "parse-statements": "1.0.11" } }, "node_modules/parse-json": { @@ -13478,6 +13341,7 @@ "resolved": "https://registry.npmjs.org/parse-node-version/-/parse-node-version-1.0.1.tgz", "integrity": "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.10" } @@ -13485,30 +13349,30 @@ "node_modules/parse-passwd": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/parse-passwd/-/parse-passwd-1.0.0.tgz", - "integrity": "sha1-bVuTSkVpk7I9N/QKOC1vFmao5cY= sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", + "integrity": "sha512-1Y1A//QUXEZK7YKz+rD9WydcE1+EuPr6ZBgKecAB8tmoW6UFv0NREVJe1p+jRxtThkcbbKkfwIbWJe/IeE6m2Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } }, + "node_modules/parse-statements": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/parse-statements/-/parse-statements-1.0.11.tgz", + "integrity": "sha512-HlsyYdMBnbPQ9Jr/VgJ1YF4scnldvJpJxCVx6KgqPL4dxppsWrJHCIIxQXMJrqGnsRkNPATbeMJ8Yxu7JMsYcA==", + "dev": true, + "license": "MIT" + }, "node_modules/parseurl": { "version": "1.3.3", "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==", "dev": true, + "license": "MIT", "engines": { "node": ">= 0.8" } }, - "node_modules/pascalcase": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/pascalcase/-/pascalcase-0.1.1.tgz", - "integrity": "sha1-s2PlXoAGym/iF4TS2yK9FdeRfxQ= sha512-XHXfu/yOQRy9vYOtUDVMN60OEJjW013GoObG1o+xwQTpB9eYJX/BjXMsdW13ZDPruFhYYn0AG22w0xgQMwl3Nw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/path-browserify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-browserify/-/path-browserify-1.0.1.tgz", @@ -13557,8 +13421,9 @@ "node_modules/path-root": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/path-root/-/path-root-0.1.1.tgz", - "integrity": "sha1-mkpoFMrBwM1zNgqV8yCDyOpHRbc= sha512-QLcPegTHF11axjfojBIoDygmS2E3Lf+8+jI6wOVmNVenrKSo3mFdSGiIgdSHenczw3wPtlVMQaFVwGmM7BJdtg==", + "integrity": "sha512-QLcPegTHF11axjfojBIoDygmS2E3Lf+8+jI6wOVmNVenrKSo3mFdSGiIgdSHenczw3wPtlVMQaFVwGmM7BJdtg==", "dev": true, + "license": "MIT", "dependencies": { "path-root-regex": "^0.1.0" }, @@ -13569,8 +13434,9 @@ "node_modules/path-root-regex": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/path-root-regex/-/path-root-regex-0.1.2.tgz", - "integrity": "sha1-v8zcjfWxLcUsi0PsONGNcsBLqW0= sha512-4GlJ6rZDhQZFE0DPVKh0e9jmZ5egZfxTkp7bcRDuPlJXbAwhxcl2dINPUAsjLdejqaLsCeg8axcLjIbvBjN4pQ==", + "integrity": "sha512-4GlJ6rZDhQZFE0DPVKh0e9jmZ5egZfxTkp7bcRDuPlJXbAwhxcl2dINPUAsjLdejqaLsCeg8axcLjIbvBjN4pQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -13601,19 +13467,14 @@ } }, "node_modules/path-to-regexp": { - "version": "6.3.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-6.3.0.tgz", - "integrity": "sha512-Yhpw4T9C6hPpgPeA28us07OJeqZ5EzQTkbfwuhsUg0c237RomFoETJgmp2sa3F/41gfLE6G5cqcYwznmeEeOlQ==", - "dev": true, - "license": "MIT" - }, - "node_modules/path-type": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", - "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-8.3.0.tgz", + "integrity": "sha512-7jdwVIRtsP8MYpdXSwOS0YdD0Du+qOoF/AEPIt88PcCFrZCzx41oxku1jD88hZBwbNUIEfpqvuhjFaMAqMTWnA==", "dev": true, - "engines": { - "node": ">=8" + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/pause-stream": { @@ -13667,9 +13528,9 @@ "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA= sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==" }, "node_modules/picocolors": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.0.tgz", - "integrity": "sha512-TQ92mBOW0l3LeMeyLV6mzy/kWr8lkd/hp3mTg7wYK7zJhuBStmGMBG0BdeDZS/dZx1IukaX6Bk11zcln25o1Aw==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", "dev": true, "license": "ISC" }, @@ -13705,27 +13566,6 @@ "node": ">=4" } }, - "node_modules/pinkie": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", - "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA= sha512-MnUuEycAemtSaeFSjXKW/aroV7akBbY+Sv+RkyqFjgAe73F+MR0TBWKBRDkmfWq/HiFmdavfZ1G7h4SPZXaCSg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/pinkie-promise": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", - "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o= sha512-0Gni6D4UcLTbv9c57DfxDGdr41XfgUjqWZu492f0cIGr16zDU06BWP/RAEvOuo7CQ0CNjHaLlM59YJJFm3NWlw==", - "dev": true, - "dependencies": { - "pinkie": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/pkg-dir": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", @@ -13791,13 +13631,13 @@ } }, "node_modules/playwright": { - "version": "1.50.0", - "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.50.0.tgz", - "integrity": "sha512-+GinGfGTrd2IfX1TA4N2gNmeIksSb+IAe589ZH+FlmpV3MYTx6+buChGIuDLQwrGNCw2lWibqV50fU510N7S+w==", + "version": "1.56.1", + "resolved": "https://registry.npmjs.org/playwright/-/playwright-1.56.1.tgz", + "integrity": "sha512-aFi5B0WovBHTEvpM3DzXTUaeN6eN0qWnTkKx4NQaH4Wvcmc153PdaY2UBdSYKaGYw+UyWXSVyxDUg5DoPEttjw==", "dev": true, "license": "Apache-2.0", "dependencies": { - "playwright-core": "1.50.0" + "playwright-core": "1.56.1" }, "bin": { "playwright": "cli.js" @@ -13810,9 +13650,9 @@ } }, "node_modules/playwright-core": { - "version": "1.47.2", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.47.2.tgz", - "integrity": "sha512-3JvMfF+9LJfe16l7AbSmU555PaTl2tPyQsVInqm3id16pdDfvZ8TTZ/pyzmkbDrZTQefyzU7AIHlZqQnxpqHVQ==", + "version": "1.57.0", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.57.0.tgz", + "integrity": "sha512-agTcKlMw/mjBWOnD6kFZttAAGHgi/Nw0CZ2o6JqWSbMlI219lAFLZZCyqByTsvVAJq5XA5H8cA6PrvBRpBWEuQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -13823,9 +13663,9 @@ } }, "node_modules/playwright/node_modules/playwright-core": { - "version": "1.50.0", - "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.50.0.tgz", - "integrity": "sha512-CXkSSlr4JaZs2tZHI40DsZUN/NIwgaUPsyLuOAaIZp2CyF2sN5MM5NJsyB188lFSSozFxQ5fPT4qM+f0tH/6wQ==", + "version": "1.56.1", + "resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.56.1.tgz", + "integrity": "sha512-hutraynyn31F+Bifme+Ps9Vq59hKuUCz7H1kDOcBs+2oGguKkWTU50bBWrtz34OUWmIwpBTWDxaRPXrIXkgvmQ==", "dev": true, "license": "Apache-2.0", "bin": { @@ -13884,19 +13724,10 @@ "node": ">=0.10.0" } }, - "node_modules/posix-character-classes": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz", - "integrity": "sha1-AerA/jta9xoqbAL+q7jB/vfgDqs= sha512-xTgYBc3fuo7Yt7JbiuFxSYGToMoz8fLoE6TC9Wx1P/u+LfeThMOAqmuyECnlBaaJb+u1m9hHiXUEtwW4OzfUJg==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/postcss": { - "version": "8.4.38", - "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.4.38.tgz", - "integrity": "sha512-Wglpdk03BSfXkHoQa3b/oulrotAkwrlLDRSOb9D0bN86FdRyE9lppSp33aHNPgBa0JKCoB+drFLZkQoRRYae5A==", + "version": "8.5.3", + "resolved": "https://registry.npmjs.org/postcss/-/postcss-8.5.3.tgz", + "integrity": "sha512-dle9A3yYxlBSrt8Fu+IpjGT8SY8hN0mlaA6GY8t0P5PjIOZemULz/E2Bnm/2dcUOena75OTNkHI76uZBNUUq3A==", "dev": true, "funding": [ { @@ -13912,10 +13743,11 @@ "url": "https://github.com/sponsors/ai" } ], + "license": "MIT", "dependencies": { - "nanoid": "^3.3.7", - "picocolors": "^1.0.0", - "source-map-js": "^1.2.0" + "nanoid": "^3.3.8", + "picocolors": "^1.1.1", + "source-map-js": "^1.2.1" }, "engines": { "node": "^10 || ^12 || >=14" @@ -14131,10 +13963,11 @@ } }, "node_modules/postcss-modules-extract-imports": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.0.0.tgz", - "integrity": "sha512-bdHleFnP3kZ4NYDhuGlVK+CMrQ/pqUm8bx/oGL93K6gVwiclvX5x0n76fYMKuIGKzlABOy13zsvqjb0f92TEXw==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/postcss-modules-extract-imports/-/postcss-modules-extract-imports-3.1.0.tgz", + "integrity": "sha512-k3kNe0aNFQDAZGbin48pL2VNidTF0w4/eASDsxlyspobzU3wZQLOGj7L9gfRe0Jo9/4uud09DsjFNH7winGv8Q==", "dev": true, + "license": "ISC", "engines": { "node": "^10 || ^12 || >= 14" }, @@ -14143,13 +13976,14 @@ } }, "node_modules/postcss-modules-local-by-default": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.0.4.tgz", - "integrity": "sha512-L4QzMnOdVwRm1Qb8m4x8jsZzKAaPAgrUF1r/hjDR2Xj7R+8Zsf97jAlSQzWtKx5YNiNGN8QxmPFIc/sh+RQl+Q==", + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/postcss-modules-local-by-default/-/postcss-modules-local-by-default-4.2.0.tgz", + "integrity": "sha512-5kcJm/zk+GJDSfw+V/42fJ5fhjL5YbFDl8nVdXkJPLLW+Vf9mTD5Xe0wqIaDnLuL2U6cDNpTr+UQ+v2HWIBhzw==", "dev": true, + "license": "MIT", "dependencies": { "icss-utils": "^5.0.0", - "postcss-selector-parser": "^6.0.2", + "postcss-selector-parser": "^7.0.0", "postcss-value-parser": "^4.1.0" }, "engines": { @@ -14159,13 +13993,28 @@ "postcss": "^8.1.0" } }, + "node_modules/postcss-modules-local-by-default/node_modules/postcss-selector-parser": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/postcss-modules-scope": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.1.1.tgz", - "integrity": "sha512-uZgqzdTleelWjzJY+Fhti6F3C9iF1JR/dODLs/JDefozYcKTBCdD8BIl6nNPbTbcLnGrk56hzwZC2DaGNvYjzA==", + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/postcss-modules-scope/-/postcss-modules-scope-3.2.1.tgz", + "integrity": "sha512-m9jZstCVaqGjTAuny8MdgE88scJnCiQSlSrOWcTQgM2t32UBe+MUmFSO5t7VMSfAf/FJKImAxBav8ooCHJXCJA==", "dev": true, + "license": "ISC", "dependencies": { - "postcss-selector-parser": "^6.0.4" + "postcss-selector-parser": "^7.0.0" }, "engines": { "node": "^10 || ^12 || >= 14" @@ -14174,6 +14023,20 @@ "postcss": "^8.1.0" } }, + "node_modules/postcss-modules-scope/node_modules/postcss-selector-parser": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/postcss-selector-parser/-/postcss-selector-parser-7.1.1.tgz", + "integrity": "sha512-orRsuYpJVw8LdAwqqLykBj9ecS5/cRHlI5+nvTo8LcCKmzDmqVORXtOIYEEQuL9D4BxtA1lm5isAqzQZCoQ6Eg==", + "dev": true, + "license": "MIT", + "dependencies": { + "cssesc": "^3.0.0", + "util-deprecate": "^1.0.2" + }, + "engines": { + "node": ">=4" + } + }, "node_modules/postcss-modules-values": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/postcss-modules-values/-/postcss-modules-values-4.0.0.tgz", @@ -14553,6 +14416,18 @@ "integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==", "dev": true }, + "node_modules/powershell-utils": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/powershell-utils/-/powershell-utils-0.1.0.tgz", + "integrity": "sha512-dM0jVuXJPsDN6DvRpea484tCUaMiXWjuCn++HGTqUWzGDjv5tZkEZldAJ/UMlqRYGFrD/etByo4/xOuC/snX2A==", + "license": "MIT", + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/prebuild-install": { "version": "7.1.2", "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.2.tgz", @@ -14593,9 +14468,10 @@ } }, "node_modules/prebuild-install/node_modules/tar-fs": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", - "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", + "license": "MIT", "dependencies": { "chownr": "^1.1.1", "mkdirp-classic": "^0.5.2", @@ -14627,15 +14503,6 @@ "node": ">= 0.8.0" } }, - "node_modules/pretty-hrtime": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/pretty-hrtime/-/pretty-hrtime-1.0.3.tgz", - "integrity": "sha1-t+PqQkNaTJsnWdmeDyAesZWALuE= sha512-66hKPCr+72mlfiSjlEB1+45IjXSqvVAIy6mocupoww4tBFE9R9IhwwUGoI4G++Tc9Aq+2rxOt0RFU6gPcrte0A==", - "dev": true, - "engines": { - "node": ">= 0.8" - } - }, "node_modules/process": { "version": "0.11.10", "resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz", @@ -14660,20 +14527,12 @@ "node": ">=0.4.0" } }, - "node_modules/promise-stream-reader": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-stream-reader/-/promise-stream-reader-1.0.1.tgz", - "integrity": "sha512-Tnxit5trUjBAqqZCGWwjyxhmgMN4hGrtpW3Oc/tRI4bpm/O2+ej72BB08l6JBnGQgVDGCLvHFGjGgQS6vzhwXg==", - "license": "MIT", - "engines": { - "node": ">8.0.0" - } - }, "node_modules/proto-list": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz", - "integrity": "sha1-IS1b/hMYMGpCD2QCuOJv85ZHqEk= sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", - "dev": true + "integrity": "sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==", + "dev": true, + "license": "ISC" }, "node_modules/proxy-from-env": { "version": "1.1.0", @@ -14714,12 +14573,6 @@ "node": ">=4.2.0" } }, - "node_modules/pseudomap": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pseudomap/-/pseudomap-1.0.2.tgz", - "integrity": "sha1-8FKijacOYYkX7wqKw0wa5aaChrM= sha512-b/YwNhb8lk1Zz2+bXXpS/LK9OisiZZ1SNsSLxN1x2OXVEhW2Ckr/7mWE5vrC1ZTiJlD9g19jWszTmJsB+oEpFQ==", - "dev": true - }, "node_modules/psl": { "version": "1.8.0", "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", @@ -14727,10 +14580,11 @@ "dev": true }, "node_modules/pump": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/pump/-/pump-1.0.2.tgz", - "integrity": "sha1-Oz7mUS+U8OV1U4wXmV+fFpkKXVE= sha512-0l9Rf87wCGXiNCxHxjixpBTPa0iLYFp6an+fwXp7Yz6Fxyhdo7YiBsV76yqzwajT/2+XjKdiCaCDVIcvyEHqCA==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", "dev": true, + "license": "MIT", "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" @@ -14781,12 +14635,6 @@ "inherits": "~2.0.0" } }, - "node_modules/queue-tick": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/queue-tick/-/queue-tick-1.0.1.tgz", - "integrity": "sha512-kJt5qhMxoszgU/62PLP1CJytzd2NKetjSRnyuj31fDd3Rlcz3fzlFdFLD1SItunPwyqEOkca6GbV612BWfaBag==", - "dev": true - }, "node_modules/quick-lru": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/quick-lru/-/quick-lru-5.1.1.tgz", @@ -14831,10 +14679,17 @@ } }, "node_modules/rcedit": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/rcedit/-/rcedit-1.1.0.tgz", - "integrity": "sha512-JkXJ0IrUcdupLoIx6gE4YcFaMVSGtu7kQf4NJoDJUnfBZGuATmJ2Yal2v55KTltp+WV8dGr7A0RtOzx6jmtM6Q==", - "dev": true + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/rcedit/-/rcedit-5.0.2.tgz", + "integrity": "sha512-dgysxaeXZ4snLpPjn8aVtHvZDCx+aRcvZbaWBgl1poU6OPustMvOkj9a9ZqASQ6i5Y5szJ13LSvglEOwrmgUxA==", + "dev": true, + "license": "MIT", + "dependencies": { + "cross-spawn-windows-exe": "^1.1.0" + }, + "engines": { + "node": ">= 22.12.0" + } }, "node_modules/read-pkg": { "version": "3.0.0", @@ -14850,121 +14705,6 @@ "node": ">=4" } }, - "node_modules/read-pkg-up": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-1.0.1.tgz", - "integrity": "sha1-nWPBMnbAZZGNV/ACpX9AobZD+wI= sha512-WD9MTlNtI55IwYUS27iHh9tK3YoIVhxis8yKhLpTqWtml739uXc9NWTpxoHkfZf3+DkCCsXox94/VWZniuZm6A==", - "dev": true, - "dependencies": { - "find-up": "^1.0.0", - "read-pkg": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/read-pkg-up/node_modules/find-up": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/find-up/-/find-up-1.1.2.tgz", - "integrity": "sha1-ay6YIrGizgpgq2TWEOzK1TyyTQ8= sha512-jvElSjyuo4EMQGoTwo1uJU5pQMwTW5lS1x05zzfJuTIyLR3zwO27LYrxNg+dlvKpGOuGy/MzBdXh80g0ve5+HA==", - "dev": true, - "dependencies": { - "path-exists": "^2.0.0", - "pinkie-promise": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/read-pkg-up/node_modules/load-json-file": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/load-json-file/-/load-json-file-1.1.0.tgz", - "integrity": "sha1-lWkFcI1YtLq0wiYbBPWfMcmTdMA= sha512-cy7ZdNRXdablkXYNI049pthVeXFurRyb9+hA/dZzerZ0pGTx42z+y+ssxBaVV2l70t1muq5IdKhn4UtcoGUY9A==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "parse-json": "^2.2.0", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0", - "strip-bom": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/read-pkg-up/node_modules/parse-json": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-2.2.0.tgz", - "integrity": "sha1-9ID0BDTvgHQfhGkJn43qGPVaTck= sha512-QR/GGaKCkhwk1ePQNYDRKYZ3mwU9ypsKhB0XyFnLQdomyEqk3e8wpW3V5Jp88zbxK4n5ST1nqo+g9juTpownhQ==", - "dev": true, - "dependencies": { - "error-ex": "^1.2.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/read-pkg-up/node_modules/path-exists": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-2.1.0.tgz", - "integrity": "sha1-D+tsZPD8UY2adU3V77YscCJ2H0s= sha512-yTltuKuhtNeFJKa1PiRzfLAU5182q1y4Eb4XCJ3PBqyzEDkAZRzBrKKBct682ls9reBVHf9udYLN5Nd+K1B9BQ==", - "dev": true, - "dependencies": { - "pinkie-promise": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/read-pkg-up/node_modules/path-type": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/path-type/-/path-type-1.1.0.tgz", - "integrity": "sha1-WcRPfuSR2nBNpBXaWkBwuk+P5EE= sha512-S4eENJz1pkiQn9Znv33Q+deTOKmbl+jj1Fl+qiP/vYezj+S8x+J3Uo0ISrx/QoEvIlOaDWJhPaRd1flJ9HXZqg==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "pify": "^2.0.0", - "pinkie-promise": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/read-pkg-up/node_modules/pify": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", - "integrity": "sha512-udgsAY+fTnvv7kI7aaxbqwWNb0AHiB0qBO89PZKPkoTmGOgdbrHDKD+0B2X4uTfJ/FT1R09r9gTsjUjNJotuog==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/read-pkg-up/node_modules/read-pkg": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-1.1.0.tgz", - "integrity": "sha1-9f+qXs0pyzHAR0vKfXVra7KePyg= sha512-7BGwRHqt4s/uVbuyoeejRn4YmFnYZiFl4AuaeXHlgZf3sONF0SOGlxs2Pw8g6hCKupo08RafIO5YXFNOKTfwsQ==", - "dev": true, - "dependencies": { - "load-json-file": "^1.0.0", - "normalize-package-data": "^2.3.2", - "path-type": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/read-pkg-up/node_modules/strip-bom": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-2.0.0.tgz", - "integrity": "sha1-YhmoVhZSBJHzV4i9vxRHqZx+aw4= sha512-kwrX1y7czp1E69n2ajbG65mIo9dqvJ+8aBQXOGVxqwvNbsXdFM6Lq37dLAY3mknUwru8CfcCbfOLL/gMo+fi3g==", - "dev": true, - "dependencies": { - "is-utf8": "^0.2.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/read-pkg/node_modules/path-type": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-3.0.0.tgz", @@ -15003,28 +14743,16 @@ } }, "node_modules/rechoir": { - "version": "0.6.2", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.6.2.tgz", - "integrity": "sha1-hSBLVNuoLVdC4oyWdW70OvUOM4Q= sha512-HFM8rkZ+i3zrV+4LQjwQ0W+ez98pApMGM3HUrN04j3CqzPOzl9nmP15Y8YXNm8QHGv/eacOVEjqhmWpkRV0NAw==", - "dev": true, - "dependencies": { - "resolve": "^1.1.6" - }, - "engines": { - "node": ">= 0.10" - } - }, - "node_modules/regex-not": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz", - "integrity": "sha512-J6SDjUgDxQj5NusnOtdFxDwN/+HWykR8GELwctJ7mdqhcyy1xEc4SRFHUXvxTp661YaVKAjfRLZ9cCqS6tn32A==", + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", + "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", "dev": true, + "license": "MIT", "dependencies": { - "extend-shallow": "^3.0.2", - "safe-regex": "^1.1.0" + "resolve": "^1.20.0" }, "engines": { - "node": ">=0.10.0" + "node": ">= 10.13.0" } }, "node_modules/remove-bom-buffer": { @@ -15085,24 +14813,6 @@ "integrity": "sha1-wkvOKig62tW8P1jg1IJJuSN52O8= sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", "dev": true }, - "node_modules/repeat-element": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/repeat-element/-/repeat-element-1.1.3.tgz", - "integrity": "sha512-ahGq0ZnV5m5XtZLMb+vP76kcAM5nkLqk0lpqAuojSKGgQtn4eRi4ZZGm2olo2zKFH+sMsWaqOCW1dqAnOru72g==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/repeat-string": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/repeat-string/-/repeat-string-1.6.1.tgz", - "integrity": "sha1-jcrkcOHIirwtYA//Sndihtp15jc= sha512-PV0dzCYDNfRi1jCDbJzpW7jNNDRuCOG/jI5ctQcGKt/clZD+YcPS3yIlWuTJMmESC8aevCFmWJy5wjAFgNqN6w==", - "dev": true, - "engines": { - "node": ">=0.10" - } - }, "node_modules/replace-ext": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-2.0.0.tgz", @@ -15113,17 +14823,13 @@ } }, "node_modules/replace-homedir": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/replace-homedir/-/replace-homedir-1.0.0.tgz", - "integrity": "sha1-6H9tUTuSjd6AgmDBK+f+xv9ueYw= sha512-CHPV/GAglbIB1tnQgaiysb8H2yCy8WQ7lcEwQ/eT+kLj0QHV8LnJW0zpqpE7RSkrMSRoa+EBoag86clf7WAgSg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/replace-homedir/-/replace-homedir-2.0.0.tgz", + "integrity": "sha512-bgEuQQ/BHW0XkkJtawzrfzHFSN70f/3cNOiHa2QsYxqrjaC30X1k74FJ6xswVBP0sr0SpGIdVFuPwfrYziVeyw==", "dev": true, - "dependencies": { - "homedir-polyfill": "^1.0.1", - "is-absolute": "^1.0.0", - "remove-trailing-separator": "^1.1.0" - }, + "license": "MIT", "engines": { - "node": ">= 0.10" + "node": ">= 10.13.0" } }, "node_modules/replacestream": { @@ -15175,6 +14881,7 @@ "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -15191,6 +14898,19 @@ "integrity": "sha512-KigOCHcocU3XODJxsu8i/j8T9tzT4adHiecwORRQ0ZZFcp7ahwXuRU1m+yuO90C5ZUyGeGfocHDI14M3L3yDAQ==", "dev": true }, + "node_modules/reserved-identifiers": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/reserved-identifiers/-/reserved-identifiers-1.2.0.tgz", + "integrity": "sha512-yE7KUfFvaBFzGPs5H3Ops1RevfUEsDc5Iz65rOwWg4lE8HJSYtle77uul3+573457oHvBKuHYDl/xqUkKpEEdw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/resolve": { "version": "1.22.1", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.1.tgz", @@ -15238,8 +14958,9 @@ "node_modules/resolve-dir": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/resolve-dir/-/resolve-dir-1.0.1.tgz", - "integrity": "sha1-eaQGRMNivoLybv/nOcm7U4IEb0M= sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==", + "integrity": "sha512-R7uiTjECzvOsWSfdM0QKFNBVFcK27aHOUwdvK53BcW8zqnGdYp0Fbj82cy54+2A4P2tFM22J5kRfe1R+lM/1yg==", "dev": true, + "license": "MIT", "dependencies": { "expand-tilde": "^2.0.0", "global-modules": "^1.0.0" @@ -15253,6 +14974,7 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } @@ -15327,13 +15049,6 @@ "node": ">= 0.6" } }, - "node_modules/resolve-url": { - "version": "0.2.1", - "resolved": "https://registry.npmjs.org/resolve-url/-/resolve-url-0.2.1.tgz", - "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo= sha512-ZuF55hVUQaaczgOIwqWzkEcEidmlD/xl44x1UZnhOXcYuFN2S6+rcxpG+C1N3So0wvNI3DmJICUFfu2SxhBmvg==", - "deprecated": "https://github.com/lydell/resolve-url#deprecated", - "dev": true - }, "node_modules/responselike": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/responselike/-/responselike-2.0.1.tgz", @@ -15368,54 +15083,86 @@ "integrity": "sha512-VUJ49FC8U1OxwZLxIbTTrDvLnf/6TDgxZcK8wxR8zs13xpx7xbG60ndBlhNrFi2EMuFRoeDoJO7wthSLq42EjA==", "dev": true }, - "node_modules/ret": { - "version": "0.1.15", - "resolved": "https://registry.npmjs.org/ret/-/ret-0.1.15.tgz", - "integrity": "sha512-TTlYpa+OL+vMMNG24xSlQGEJ3B/RzEfUlLct7b5G/ytav+wPrplCpVMFuwzXbkecJrb6IYo1iFb0S9v37754mg==", + "node_modules/reusify": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", + "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/rimraf": { + "version": "6.1.2", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.1.2.tgz", + "integrity": "sha512-cFCkPslJv7BAXJsYlK1dZsbP8/ZNLkCAQ0bi1hf5EKX2QHegmDFEFA6QhuYJlk7UDdc+02JjO80YSOrWPpw06g==", "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "glob": "^13.0.0", + "package-json-from-dist": "^1.0.1" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, "engines": { - "node": ">=0.12" + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/glob": { + "version": "13.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.0.tgz", + "integrity": "sha512-tvZgpqk6fz4BaNZ66ZsRaZnbHvP/jG3uKJvAZOwEVUL4RTA5nJeeLYfyN9/VA8NX/V3IBG+hkeuGpKjvELkVhA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.1.1", + "minipass": "^7.1.2", + "path-scurry": "^2.0.0" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/reusify": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", - "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", + "node_modules/rimraf/node_modules/lru-cache": { + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", "dev": true, + "license": "BlueOak-1.0.0", "engines": { - "iojs": ">=1.0.0", - "node": ">=0.10.0" + "node": "20 || >=22" } }, - "node_modules/rimraf": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", - "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", - "deprecated": "Rimraf versions prior to v4 are no longer supported", + "node_modules/rimraf/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" } }, - "node_modules/rimraf/node_modules/glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "deprecated": "Glob versions prior to v9 are no longer supported", + "node_modules/rimraf/node_modules/path-scurry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz", + "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" }, "engines": { - "node": "*" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -15439,6 +15186,18 @@ "node": ">=8.0" } }, + "node_modules/run-applescript": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/run-parallel": { "version": "1.1.10", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.10.tgz", @@ -15464,14 +15223,12 @@ "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" }, - "node_modules/safe-regex": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/safe-regex/-/safe-regex-1.1.0.tgz", - "integrity": "sha1-QKNmnzsHfR6UPURinhV91IAjvy4= sha512-aJXcif4xnaNUzvUuC5gcb46oTS7zvg4jpMTnuqtrEPlR3vFr4pxtdTwaF1Qs3Enjn9HK+ZlwQui+a7z0SywIzg==", + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "dev": true, - "dependencies": { - "ret": "~0.1.10" - } + "license": "MIT" }, "node_modules/sax": { "version": "1.2.4", @@ -15480,18 +15237,19 @@ "dev": true }, "node_modules/schema-utils": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.0.0.tgz", - "integrity": "sha512-1edyXKgh6XnJsJSQ8mKWXnN/BVaIbFMLpouRUrXgVq7WYne5kw3MW7UPhO44uRXQSIpTSXoJbmrR2X0w9kUTyg==", + "version": "4.3.3", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-4.3.3.tgz", + "integrity": "sha512-eflK8wEtyOE6+hsaRVPxvUKYCpRgzLqDTb8krvAsRIwOGlHoSgYLgBXoubGgLd2fT41/OUYdb48v4k4WWHQurA==", "dev": true, + "license": "MIT", "dependencies": { "@types/json-schema": "^7.0.9", - "ajv": "^8.8.0", + "ajv": "^8.9.0", "ajv-formats": "^2.1.1", - "ajv-keywords": "^5.0.0" + "ajv-keywords": "^5.1.0" }, "engines": { - "node": ">= 12.13.0" + "node": ">= 10.13.0" }, "funding": { "type": "opencollective", @@ -15499,15 +15257,16 @@ } }, "node_modules/schema-utils/node_modules/ajv": { - "version": "8.12.0", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", - "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz", + "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==", "dev": true, + "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.1", + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "require-from-string": "^2.0.2" }, "funding": { "type": "github", @@ -15519,6 +15278,7 @@ "resolved": "https://registry.npmjs.org/ajv-keywords/-/ajv-keywords-5.1.0.tgz", "integrity": "sha512-YCS/JNFAUyr5vAuhk1DWm1CBxRHW9LbJ2ozWeemrIqpbsqKjHVxYPyi5GC0rjZIT5JxJ3virVTS8wk4i/Z+krw==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.3" }, @@ -15530,12 +15290,14 @@ "version": "1.0.0", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/semver": { - "version": "7.6.3", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", - "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -15551,15 +15313,16 @@ "optional": true }, "node_modules/semver-greatest-satisfied-range": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-1.1.0.tgz", - "integrity": "sha1-E+jCZYq5aRywzXEJMkAoDTb3els= sha512-Ny/iyOzSSa8M5ML46IAx3iXc6tfOsYU2R4AXi2UpHk60Zrgyq6eqPj/xiOfS0rRl/iiQ/rdJkVjw/5cdUyCntQ==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/semver-greatest-satisfied-range/-/semver-greatest-satisfied-range-2.0.0.tgz", + "integrity": "sha512-lH3f6kMbwyANB7HuOWRMlLCa2itaCrZJ+SAqqkSZrZKO/cAsk2EOyaKHUtNkVLFyFW9pct22SFesFp3Z7zpA0g==", "dev": true, + "license": "MIT", "dependencies": { - "sver-compat": "^1.5.0" + "sver": "^1.8.3" }, "engines": { - "node": ">= 0.10" + "node": ">= 10.13.0" } }, "node_modules/serialize-error": { @@ -15624,54 +15387,6 @@ "node": ">= 0.4" } }, - "node_modules/set-value": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/set-value/-/set-value-2.0.1.tgz", - "integrity": "sha512-JxHc1weCN68wRY0fhCoXpyK55m/XPHafOmK4UWD7m2CI14GMcFypt4w/0+NV5f/ZMby2F6S2wwA7fgynh9gWSw==", - "dev": true, - "dependencies": { - "extend-shallow": "^2.0.1", - "is-extendable": "^0.1.1", - "is-plain-object": "^2.0.3", - "split-string": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/set-value/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/set-value/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/set-value/node_modules/is-plain-object": { - "version": "2.0.4", - "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz", - "integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==", - "dev": true, - "dependencies": { - "isobject": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", @@ -15689,6 +15404,7 @@ "resolved": "https://registry.npmjs.org/shallow-clone/-/shallow-clone-3.0.1.tgz", "integrity": "sha512-/6KqX+GVUdqPuPPd2LxDDxzX6CAbjJehAAOKlNpqqUpAqPM6HeL8f+o3a+JsyGjn2lv0WY8UsTgUJjU9Ok55NA==", "dev": true, + "license": "MIT", "dependencies": { "kind-of": "^6.0.2" }, @@ -15696,15 +15412,6 @@ "node": ">=8" } }, - "node_modules/shallow-clone/node_modules/kind-of": { - "version": "6.0.3", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz", - "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", @@ -15732,12 +15439,6 @@ "integrity": "sha512-Vpfqwm4EnqGdlsBFNmHhxhElJYrdfcxPThu+ryKS5J8L/fhAwLazFZtq+S+TWZ9ANj2piSQLGj6NQg+lKPmxrw==", "dev": true }, - "node_modules/sigmund": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sigmund/-/sigmund-1.0.1.tgz", - "integrity": "sha1-P/IfGYytIXX587eBhT/ZTQ0ZtZA= sha512-fCvEXfh6NWpm+YSuY2bpXb/VIihqWA6hLsgboC+0nl71Q7N7o2eaCW8mJa/NLvQhs6jpd3VZV4UiUQlV6+lc8g==", - "dev": true - }, "node_modules/signal-exit": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", @@ -15790,284 +15491,64 @@ "dependencies": { "decompress-response": "^6.0.0", "once": "^1.3.1", - "simple-concat": "^1.0.0" - } - }, - "node_modules/sinon": { - "version": "12.0.1", - "resolved": "https://registry.npmjs.org/sinon/-/sinon-12.0.1.tgz", - "integrity": "sha512-iGu29Xhym33ydkAT+aNQFBINakjq69kKO6ByPvTsm3yyIACfyQttRTP03aBP/I8GfhFmLzrnKwNNkr0ORb1udg==", - "deprecated": "16.1.1", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^1.8.3", - "@sinonjs/fake-timers": "^8.1.0", - "@sinonjs/samsam": "^6.0.2", - "diff": "^5.0.0", - "nise": "^5.1.0", - "supports-color": "^7.2.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/sinon" - } - }, - "node_modules/sinon-test": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/sinon-test/-/sinon-test-3.1.3.tgz", - "integrity": "sha512-jBDvPVW2z8uAoiud3Nqc6+e8+WX6UTB1gPQuYXK00mSnp9m/JYyeLdBjLlqbnk1DVmsgRCAHSoXYPNLHp0t56Q==", - "dev": true, - "peerDependencies": { - "sinon": "2.x - 13.x" - } - }, - "node_modules/sinon/node_modules/@sinonjs/fake-timers": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-8.1.0.tgz", - "integrity": "sha512-OAPJUAtgeINhh/TAlUID4QTs53Njm7xzddaVlEs/SXwgtiD1tW22zAB/W1wdqfrpmikgaWQ9Fw6Ws+hsiRm5Vg==", - "dev": true, - "dependencies": { - "@sinonjs/commons": "^1.7.0" - } - }, - "node_modules/sinon/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/sinon/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/slashes": { - "version": "3.0.12", - "resolved": "https://registry.npmjs.org/slashes/-/slashes-3.0.12.tgz", - "integrity": "sha512-Q9VME8WyGkc7pJf6QEkj3wE+2CnvZMI+XJhwdTPR8Z/kWQRXi7boAWLDibRPyHRTUTPx5FaU7MsyrjI3yLB4HA==", - "dev": true - }, - "node_modules/smart-buffer": { - "version": "4.2.0", - "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", - "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", - "engines": { - "node": ">= 6.0.0", - "npm": ">= 3.0.0" - } - }, - "node_modules/snapdragon": { - "version": "0.8.2", - "resolved": "https://registry.npmjs.org/snapdragon/-/snapdragon-0.8.2.tgz", - "integrity": "sha512-FtyOnWN/wCHTVXOMwvSv26d+ko5vWlIDD6zoUJ7LW8vh+ZBC8QdljveRP+crNrtBwioEUWy/4dMtbBjA4ioNlg==", - "dev": true, - "dependencies": { - "base": "^0.11.1", - "debug": "^2.2.0", - "define-property": "^0.2.5", - "extend-shallow": "^2.0.1", - "map-cache": "^0.2.2", - "source-map": "^0.5.6", - "source-map-resolve": "^0.5.0", - "use": "^3.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-node": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/snapdragon-node/-/snapdragon-node-2.1.1.tgz", - "integrity": "sha512-O27l4xaMYt/RSQ5TR3vpWCAB5Kb/czIcqUFOM/C4fYcLnbZUc1PkjTAMjof2pBWaSTwOUd6qUHcFGVGj7aIwnw==", - "dev": true, - "dependencies": { - "define-property": "^1.0.0", - "isobject": "^3.0.0", - "snapdragon-util": "^3.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-node/node_modules/define-property": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz", - "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY= sha512-cZTYKFWspt9jZsMscWo8sc/5lbPC9Q0N5nBLgb+Yd915iL3udB1uFgS3B8YCx66UVHq018DAVFoee7x+gxggeA==", - "dev": true, - "dependencies": { - "is-descriptor": "^1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-util": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/snapdragon-util/-/snapdragon-util-3.0.1.tgz", - "integrity": "sha512-mbKkMdQKsjX4BAL4bRYTj21edOf8cN7XHdYUJEe+Zn99hVEYcMvKPct1IqNe7+AZPirn8BCDOQBHQZknqmKlZQ==", - "dev": true, - "dependencies": { - "kind-of": "^3.2.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon-util/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon/node_modules/debug": { - "version": "2.6.9", - "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", - "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", - "dev": true, - "dependencies": { - "ms": "2.0.0" - } - }, - "node_modules/snapdragon/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", - "dev": true, - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon/node_modules/extend-shallow": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz", - "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8= sha512-zCnTtlxNoAiDc3gqY2aYAWFx7XWWiasuF2K8Me5WbN8otHKTUKBwjPtNpRs/rbUZm7KxWAaNj7P1a/p52GbVug==", - "dev": true, - "dependencies": { - "is-extendable": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon/node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", - "deprecated": "Please upgrade to v0.1.7", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon/node_modules/is-accessor-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/snapdragon/node_modules/is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", - "deprecated": "Please upgrade to v0.1.5", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" + "simple-concat": "^1.0.0" } }, - "node_modules/snapdragon/node_modules/is-data-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "node_modules/sinon": { + "version": "21.0.0", + "resolved": "https://registry.npmjs.org/sinon/-/sinon-21.0.0.tgz", + "integrity": "sha512-TOgRcwFPbfGtpqvZw+hyqJDvqfapr1qUlOizROIk4bBLjlsjlB00Pg6wMFXNtJRpu+eCZuVOaLatG7M8105kAw==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { - "is-buffer": "^1.1.5" + "@sinonjs/commons": "^3.0.1", + "@sinonjs/fake-timers": "^13.0.5", + "@sinonjs/samsam": "^8.0.1", + "diff": "^7.0.0", + "supports-color": "^7.2.0" }, - "engines": { - "node": ">=0.10.0" + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/sinon" } }, - "node_modules/snapdragon/node_modules/is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", + "node_modules/sinon-test": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/sinon-test/-/sinon-test-3.1.3.tgz", + "integrity": "sha512-jBDvPVW2z8uAoiud3Nqc6+e8+WX6UTB1gPQuYXK00mSnp9m/JYyeLdBjLlqbnk1DVmsgRCAHSoXYPNLHp0t56Q==", "dev": true, - "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "engines": { - "node": ">=0.10.0" + "peerDependencies": { + "sinon": "2.x - 13.x" } }, - "node_modules/snapdragon/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "node_modules/sinon/node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/snapdragon/node_modules/ms": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", - "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g= sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==", - "dev": true - }, - "node_modules/snapdragon/node_modules/source-map": { - "version": "0.5.7", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", - "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w= sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "node_modules/sinon/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, "engines": { - "node": ">=0.10.0" + "node": ">=8" } }, - "node_modules/snapdragon/node_modules/source-map-resolve": { - "version": "0.5.3", - "resolved": "https://registry.npmjs.org/source-map-resolve/-/source-map-resolve-0.5.3.tgz", - "integrity": "sha512-Htz+RnsXWk5+P2slx5Jh3Q66vhQj1Cllm0zvnaY98+NFx+Dv2CF/f5O/t8x+KaNdrdIAsruNzoh/KpialbqAnw==", - "deprecated": "See https://github.com/lydell/source-map-resolve#deprecated", - "dev": true, - "dependencies": { - "atob": "^2.1.2", - "decode-uri-component": "^0.2.0", - "resolve-url": "^0.2.1", - "source-map-url": "^0.4.0", - "urix": "^0.1.0" + "node_modules/smart-buffer": { + "version": "4.2.0", + "resolved": "https://registry.npmjs.org/smart-buffer/-/smart-buffer-4.2.0.tgz", + "integrity": "sha512-94hK0Hh8rPqQl2xXc3HsaBoOXKV20MToPkcXvwbISWLEs+64sBq5kFgn2kJDHb1Pry9yrP0dxrCI9RRci7RXKg==", + "engines": { + "node": ">= 6.0.0", + "npm": ">= 3.0.0" } }, "node_modules/socks": { @@ -16097,19 +15578,21 @@ } }, "node_modules/source-map": { - "version": "0.6.1", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", - "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "version": "0.7.6", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.6.tgz", + "integrity": "sha512-i5uvt8C3ikiWeNZSVZNWcfZPItFQOsYTUAOkcUPGd8DqDy1uOUikjt5dG+uRlwyvR108Fb9DOd4GvXfT0N2/uQ==", "dev": true, + "license": "BSD-3-Clause", "engines": { - "node": ">=0.10.0" + "node": ">= 12" } }, "node_modules/source-map-js": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.0.tgz", - "integrity": "sha512-itJW8lvSA0TXEphiRoawsCksnlf8SyvmFzIhltqAHluXd88pkCd+cXJVHTDwdCr0IzwptSm035IHQktUu1QUMg==", + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/source-map-js/-/source-map-js-1.2.1.tgz", + "integrity": "sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==", "dev": true, + "license": "BSD-3-Clause", "engines": { "node": ">=0.10.0" } @@ -16146,20 +15629,14 @@ "node": ">=0.8.0" } }, - "node_modules/source-map-url": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/source-map-url/-/source-map-url-0.4.0.tgz", - "integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM= sha512-liJwHPI9x9d9w5WSIjM58MqGmmb7XzNqwdUA3kSBQ4lmDngexlKwawGzK3J1mKXi6+sysoMDlpVyZh9sv5vRfw==", - "deprecated": "See https://github.com/lydell/source-map-url#deprecated", - "dev": true - }, "node_modules/sparkles": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-1.0.1.tgz", - "integrity": "sha512-dSO0DDYUahUt/0/pD/Is3VIm5TGJjludZ0HVymmhYF6eNA53PVLhnUk0znSYbH8IYBuJdCE+1luR22jNLMaQdw==", + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/sparkles/-/sparkles-2.1.0.tgz", + "integrity": "sha512-r7iW1bDw8R/cFifrD3JnQJX0K1jqT0kprL48BiBpLZLJPmAm34zsVBsK5lc7HirZYZqMW65dOXZgbAGt/I6frg==", "dev": true, + "license": "MIT", "engines": { - "node": ">= 0.10" + "node": ">= 10.13.0" } }, "node_modules/spdx-correct": { @@ -16195,10 +15672,11 @@ "dev": true }, "node_modules/split": { - "version": "0.3.3", - "resolved": "https://registry.npmjs.org/split/-/split-0.3.3.tgz", - "integrity": "sha1-zQ7qXmOiEd//frDwkcQTPi0N0o8= sha512-wD2AeVmxXRBoX44wAycgjVpMhvbwdI2aZjCkvfNcH1YqHQvJVa1duWc73OyVGJUc05fhFaTZeQ/PYsrmyH0JVA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/split/-/split-1.0.1.tgz", + "integrity": "sha512-mTyOoPbrivtXnwnIxZRFYRrPNtEFKlpB2fvjSnCQUiAA6qAZzqwna5envK4uk6OIeP17CsdF3rSBGYVBsU0Tkg==", "dev": true, + "license": "MIT", "dependencies": { "through": "2" }, @@ -16206,18 +15684,6 @@ "node": "*" } }, - "node_modules/split-string": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/split-string/-/split-string-3.1.0.tgz", - "integrity": "sha512-NzNVhJDYpwceVVii8/Hu6DKfD2G+NrQHlS/V/qgv763EYudVwEcMQNxd2lh+0VrUByXN/oJkl5grOhYWvQUYiw==", - "dev": true, - "dependencies": { - "extend-shallow": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/sprintf-js": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.1.3.tgz", @@ -16230,108 +15696,10 @@ "deprecated": "Modern JS already guarantees Array#sort() is a stable sort, so this library is deprecated. See the compatibility table on MDN: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort#browser_compatibility", "dev": true }, - "node_modules/stack-trace": { - "version": "0.0.10", - "resolved": "https://registry.npmjs.org/stack-trace/-/stack-trace-0.0.10.tgz", - "integrity": "sha1-VHxws0fo0ytOEI6hoqFZ5f3eGcA= sha512-KGzahc7puUKkzyMt+IqAep+TVNbKP+k2Lmwhub39m1AsTSkaDutx56aDCo+HLDzf/D26BIHTJWNiTG1KAJiQCg==", - "dev": true, - "engines": { - "node": "*" - } - }, - "node_modules/static-extend": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/static-extend/-/static-extend-0.1.2.tgz", - "integrity": "sha1-YICcOcv/VTNyJv1eC1IPNB8ftcY= sha512-72E9+uLc27Mt718pMHt9VMNiAL4LMsmDbBva8mxWUCkT07fSzEGMYUCk0XWY6lp0j6RBAG4cJ3mWuZv2OE3s0g==", - "dev": true, - "dependencies": { - "define-property": "^0.2.5", - "object-copy": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/static-extend/node_modules/define-property": { - "version": "0.2.5", - "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz", - "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY= sha512-Rr7ADjQZenceVOAKop6ALkkRAmH1A4Gx9hV/7ZujPUN2rkATqFO0JZLZInbAjpZYoJ1gUx8MRMQVkYemcbMSTA==", - "dev": true, - "dependencies": { - "is-descriptor": "^0.1.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/static-extend/node_modules/is-accessor-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-0.1.6.tgz", - "integrity": "sha1-qeEss66Nh2cn7u84Q/igiXtcmNY= sha512-e1BM1qnDbMRG3ll2U9dSK0UMHuWOs3pY3AtcFsmvwPtKL3MML/Q86i+GilLfvqEs4GW+ExB91tQ3Ig9noDIZ+A==", - "deprecated": "Please upgrade to v0.1.7", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/static-extend/node_modules/is-accessor-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/static-extend/node_modules/is-data-descriptor": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-0.1.4.tgz", - "integrity": "sha1-C17mSDiOLIYCgueT8YVv7D8wG1Y= sha512-+w9D5ulSoBNlmw9OHn3U2v51SyoCd0he+bB3xMl62oijhrspxowjU+AIcDY0N3iEJbUEkB15IlMASQsxYigvXg==", - "deprecated": "Please upgrade to v0.1.5", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/static-extend/node_modules/is-data-descriptor/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", - "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/static-extend/node_modules/is-descriptor": { - "version": "0.1.6", - "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-0.1.6.tgz", - "integrity": "sha512-avDYr0SB3DwO9zsMov0gKCESFYqCnE4hq/4z3TdUlukEy5t9C0YRq7HLrsN52NAcqXKaepeCD0n+B0arnVG3Hg==", - "dev": true, - "dependencies": { - "is-accessor-descriptor": "^0.1.6", - "is-data-descriptor": "^0.1.4", - "kind-of": "^5.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/statuses": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.1.tgz", - "integrity": "sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-2.0.2.tgz", + "integrity": "sha512-DvEy55V3DB7uknRo+4iOGT5fP1slR8wQohVdknigZPMpMstaKJQWhwiYBACJE3Ul2pTnATihhBYnRhZQHGBiRw==", "dev": true, "license": "MIT", "engines": { @@ -16389,19 +15757,32 @@ } }, "node_modules/stream-combiner": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.0.4.tgz", - "integrity": "sha1-TV5DPBhSYd3mI8o/RMWGvPXErRQ= sha512-rT00SPnTVyRsaSz5zgSPma/aHSOic5U1prhYdRy5HS2kTZviFpmDgzilbtsJsxiroqACmayynDN/9VzIbX5DOw==", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/stream-combiner/-/stream-combiner-0.2.2.tgz", + "integrity": "sha512-6yHMqgLYDzQDcAkL+tjJDC5nSNuNIx0vZtRZeiPh7Saef7VHX9H5Ijn9l2VIol2zaNYlYEX6KyuT/237A58qEQ==", "dev": true, + "license": "MIT", "dependencies": { - "duplexer": "~0.1.1" + "duplexer": "~0.1.1", + "through": "~2.3.4" + } + }, + "node_modules/stream-composer": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/stream-composer/-/stream-composer-1.0.2.tgz", + "integrity": "sha512-bnBselmwfX5K10AH6L4c8+S5lgZMWI7ZYrz2rvYjCPB2DIMC4Ig8OpxGpNJSxRZ58oti7y1IcNvjBAz9vW5m4w==", + "dev": true, + "license": "MIT", + "dependencies": { + "streamx": "^2.13.2" } }, "node_modules/stream-exhaust": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/stream-exhaust/-/stream-exhaust-1.0.2.tgz", "integrity": "sha512-b/qaq/GlBK5xaq1yrK9/zFcyRSTNxmcZwFLGSTG0mXgZl/4Z6GgiyYOXOvY7N3eEvFRAG1bkDRz5EPGSvPYQlw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/stream-shift": { "version": "1.0.1", @@ -16452,13 +15833,13 @@ } }, "node_modules/streamx": { - "version": "2.18.0", - "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.18.0.tgz", - "integrity": "sha512-LLUC1TWdjVdn1weXGcSxyTR3T4+acB6tVGXT95y0nGbca4t4o/ng1wKAGTljm9VicuCVLvRlqFYXYy5GwgM7sQ==", + "version": "2.22.1", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.22.1.tgz", + "integrity": "sha512-znKXEBxfatz2GBNK02kRnCXjV+AA4kjZIUxeWSr3UGirZMJfTE9uiwKHobnbgxWyL/JWro8tTq+vOqAK1/qbSA==", "dev": true, + "license": "MIT", "dependencies": { "fast-fifo": "^1.3.2", - "queue-tick": "^1.0.1", "text-decoder": "^1.1.0" }, "optionalDependencies": { @@ -16682,12 +16063,13 @@ } }, "node_modules/supports-color": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.4.0.tgz", - "integrity": "sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", + "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==", "dev": true, + "license": "MIT", "engines": { - "node": ">=12" + "node": ">=18" }, "funding": { "url": "https://github.com/chalk/supports-color?sponsor=1" @@ -16705,14 +16087,25 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/sver-compat": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/sver-compat/-/sver-compat-1.5.0.tgz", - "integrity": "sha1-PPh9/rTQe0o/FIJ7wYaz/QxkXNg= sha512-aFTHfmjwizMNlNE6dsGmoAM4lHjL0CyiobWaFiXWSlD7cIxshW422Nb8KbXCmR6z+0ZEPY+daXJrDyh/vuwTyg==", + "node_modules/sver": { + "version": "1.8.4", + "resolved": "https://registry.npmjs.org/sver/-/sver-1.8.4.tgz", + "integrity": "sha512-71o1zfzyawLfIWBOmw8brleKyvnbn73oVHNCsu51uPMz/HWiKkkXsI31JjHW5zqXEqnPYkIiHd8ZmL7FCimLEA==", "dev": true, - "dependencies": { - "es6-iterator": "^2.0.1", - "es6-symbol": "^3.1.1" + "license": "MIT", + "optionalDependencies": { + "semver": "^6.3.0" + } + }, + "node_modules/sver/node_modules/semver": { + "version": "6.3.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", + "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", + "dev": true, + "license": "ISC", + "optional": true, + "bin": { + "semver": "bin/semver.js" } }, "node_modules/svgo": { @@ -16825,22 +16218,6 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, - "node_modules/synckit": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/synckit/-/synckit-0.9.1.tgz", - "integrity": "sha512-7gr8p9TQP6RAHusBOSLs46F4564ZrjV8xFmw5zCmgmhGUcw2hxsShhJ6CEiHQMgPDwAQ1fWHPM0ypc4RMAig4A==", - "dev": true, - "dependencies": { - "@pkgr/core": "^0.1.0", - "tslib": "^2.6.2" - }, - "engines": { - "node": "^14.18.0 || >=16.0.0" - }, - "funding": { - "url": "https://opencollective.com/unts" - } - }, "node_modules/tapable": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.0.tgz", @@ -16867,27 +16244,18 @@ } }, "node_modules/tar-fs": { - "version": "3.0.6", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.0.6.tgz", - "integrity": "sha512-iokBDQQkUyeXhgPYaZxmczGPhnhXZ0CmrqI+MOb/WFGS9DW5wnfrLgtjUJBvz50vQ3qfRwJ62QVoCFu8mPVu5w==", + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-3.1.1.tgz", + "integrity": "sha512-LZA0oaPOc2fVo82Txf3gw+AkEd38szODlptMYejQUhndHMLQ9M059uXR+AfS7DNo0NpINvSqDsvyaCrBVkptWg==", "dev": true, + "license": "MIT", "dependencies": { "pump": "^3.0.0", "tar-stream": "^3.1.5" }, "optionalDependencies": { - "bare-fs": "^2.1.1", - "bare-path": "^2.1.0" - } - }, - "node_modules/tar-fs/node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", - "dev": true, - "dependencies": { - "end-of-stream": "^1.1.0", - "once": "^1.3.1" + "bare-fs": "^4.0.1", + "bare-path": "^3.0.0" } }, "node_modules/tar-stream": { @@ -16901,11 +16269,6 @@ "streamx": "^2.15.0" } }, - "node_modules/tar/node_modules/yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" - }, "node_modules/tas-client-umd": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/tas-client-umd/-/tas-client-umd-0.2.0.tgz", @@ -16932,6 +16295,55 @@ "node": ">=6.0.0" } }, + "node_modules/temp/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/temp/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/temp/node_modules/rimraf": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.6.3.tgz", + "integrity": "sha512-mwqeW5XsA2qAejG46gYdENaxXjx9onRNCfn7L0duuP4hCuTIi/QO7PDK07KJfp1d+izWPrzEJDcSqBa0OZQriA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "license": "ISC", + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } + }, "node_modules/terser": { "version": "5.30.0", "resolved": "https://registry.npmjs.org/terser/-/terser-5.30.0.tgz", @@ -17008,6 +16420,16 @@ "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==", "dev": true }, + "node_modules/terser/node_modules/source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/terser/node_modules/source-map-support": { "version": "0.5.21", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.21.tgz", @@ -17019,40 +16441,93 @@ } }, "node_modules/test-exclude": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", - "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.1.tgz", + "integrity": "sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==", "dev": true, + "license": "ISC", "dependencies": { "@istanbuljs/schema": "^0.1.2", - "glob": "^7.1.4", - "minimatch": "^3.0.4" + "glob": "^10.4.1", + "minimatch": "^9.0.4" }, "engines": { - "node": ">=8" + "node": ">=18" + } + }, + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" } }, "node_modules/test-exclude/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", + "version": "10.5.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", + "integrity": "sha512-DfXN8DfhJ7NH3Oe7cFmu3NCu1wKbkReJ8TorzSAFbSKrlNaQSKfIzqYqVY8zlbs2NLBbWpRiU52GX2PbaBVNkg==", "dev": true, + "license": "ISC", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.1.1", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "foreground-child": "^3.1.0", + "jackspeak": "^3.1.2", + "minimatch": "^9.0.4", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^1.11.1" + }, + "bin": { + "glob": "dist/esm/bin.mjs" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/test-exclude/node_modules/jackspeak": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", + "integrity": "sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + }, + "optionalDependencies": { + "@pkgjs/parseargs": "^0.11.0" + } + }, + "node_modules/test-exclude/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" }, "engines": { - "node": "*" + "node": ">=16 || 14 >=14.17" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/test-exclude/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, "node_modules/text-decoder": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.1.0.tgz", @@ -17062,17 +16537,18 @@ "b4a": "^1.6.4" } }, - "node_modules/text-table": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ= sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", - "dev": true - }, "node_modules/textextensions": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-1.0.2.tgz", - "integrity": "sha1-ZUhjk+4fK7A5pgy7oFsLaL2VAdI= sha512-jm9KjEWiDmtGLBrTqXEduGzlYTTlPaoDKdq5YRQhD0rYjo61ZNTYKZ/x5J4ajPSBH9wIYY5qm9GNG5otIKjtOA==", - "dev": true + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-3.3.0.tgz", + "integrity": "sha512-mk82dS8eRABNbeVJrEiN5/UMSCliINAuz8mkUwH4SwslkNP//gbEzlWNS5au0z5Dpx40SQxzqZevZkn+WYJ9Dw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://bevry.me/fund" + } }, "node_modules/through": { "version": "2.3.8", @@ -17128,8 +16604,9 @@ "node_modules/time-stamp": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/time-stamp/-/time-stamp-1.1.0.tgz", - "integrity": "sha1-dkpaEa9QVhkhsTPztE5hhofg9cM= sha512-gLCeArryy2yNTRzTGKbZbloctj64jkZ57hj5zdraXue6aFgd6PmvVtEyiUU+hvU0v7q08oVv8r8ev0tRo6bvgw==", + "integrity": "sha512-gLCeArryy2yNTRzTGKbZbloctj64jkZ57hj5zdraXue6aFgd6PmvVtEyiUU+hvU0v7q08oVv8r8ev0tRo6bvgw==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -17150,62 +16627,44 @@ "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", "license": "MIT" }, - "node_modules/to-absolute-glob": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz", - "integrity": "sha1-GGX0PZ50sIItufFFt4z/fQ98hJs= sha512-rtwLUQEwT8ZeKQbyFJyomBRYXyE16U5VKuy0ftxLMK/PZb2fkOsg5r9kHdauuVDbsNdIBoC/HCthpidamQFXYA==", + "node_modules/tinyglobby": { + "version": "0.2.15", + "resolved": "https://registry.npmjs.org/tinyglobby/-/tinyglobby-0.2.15.tgz", + "integrity": "sha512-j2Zq4NyQYG5XMST4cbs02Ak8iJUdxRM0XI5QyxXuZOzKOINmWurp3smXu3y5wDcJrptwpSjgXHzIQxR0omXljQ==", "dev": true, + "license": "MIT", "dependencies": { - "is-absolute": "^1.0.0", - "is-negated-glob": "^1.0.0" + "fdir": "^6.5.0", + "picomatch": "^4.0.3" }, "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/to-fast-properties": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", - "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4= sha512-/OaKK0xYrs3DmxRYqL/yDc+FxFUVYhDlXMhRmv3z915w2HF1tnN1omB354j8VUGO/hbRzyD6Y3sA7v7GS/ceog==", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/to-object-path": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/to-object-path/-/to-object-path-0.3.0.tgz", - "integrity": "sha1-KXWIt7Dn4KwI4E5nL4XB9JmeF68= sha512-9mWHdnGRuh3onocaHzukyvCZhzvr6tiflAy/JRFXcJX0TjgfWA9pk9t8CMbzmBE4Jfw58pXbkngtBtqYxzNEyg==", - "dev": true, - "dependencies": { - "kind-of": "^3.0.2" + "node": ">=12.0.0" }, - "engines": { - "node": ">=0.10.0" + "funding": { + "url": "https://github.com/sponsors/SuperchupuDev" } }, - "node_modules/to-object-path/node_modules/kind-of": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-3.2.2.tgz", - "integrity": "sha1-MeohpzS6ubuw8yRm2JOupR5KPGQ= sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==", + "node_modules/tinyglobby/node_modules/picomatch": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, - "dependencies": { - "is-buffer": "^1.1.5" - }, + "license": "MIT", "engines": { - "node": ">=0.10.0" + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/to-regex": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/to-regex/-/to-regex-3.0.2.tgz", - "integrity": "sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==", + "node_modules/to-absolute-glob": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/to-absolute-glob/-/to-absolute-glob-2.0.2.tgz", + "integrity": "sha1-GGX0PZ50sIItufFFt4z/fQ98hJs= sha512-rtwLUQEwT8ZeKQbyFJyomBRYXyE16U5VKuy0ftxLMK/PZb2fkOsg5r9kHdauuVDbsNdIBoC/HCthpidamQFXYA==", "dev": true, "dependencies": { - "define-property": "^2.0.2", - "extend-shallow": "^3.0.2", - "regex-not": "^1.0.2", - "safe-regex": "^1.1.0" + "is-absolute": "^1.0.0", + "is-negated-glob": "^1.0.0" }, "engines": { "node": ">=0.10.0" @@ -17259,6 +16718,23 @@ "xtend": "~4.0.1" } }, + "node_modules/to-valid-identifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-valid-identifier/-/to-valid-identifier-1.0.0.tgz", + "integrity": "sha512-41wJyvKep3yT2tyPqX/4blcfybknGB4D+oETKLs7Q76UiPqRpUJK3hr1nxelyYO0PHKVzJwlu0aCeEAsGI6rpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@sindresorhus/base62": "^1.0.0", + "reserved-identifiers": "^1.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/toidentifier": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.1.tgz", @@ -17329,10 +16805,11 @@ } }, "node_modules/ts-loader": { - "version": "9.5.1", - "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.1.tgz", - "integrity": "sha512-rNH3sK9kGZcH9dYzC7CewQm4NtxJTjSEVRJ2DyBZR7f8/wcta+iV44UPCXc5+nzDzivKtlzV6c9P4e+oFhDLYg==", + "version": "9.5.4", + "resolved": "https://registry.npmjs.org/ts-loader/-/ts-loader-9.5.4.tgz", + "integrity": "sha512-nCz0rEwunlTZiy6rXFByQU1kVVpCIgUpc/psFiKVrUwrizdnIbRFu8w7bxhUF0X613DYwT4XzrZHpVyMe758hQ==", "dev": true, + "license": "MIT", "dependencies": { "chalk": "^4.1.0", "enhanced-resolve": "^5.0.0", @@ -17348,30 +16825,23 @@ "webpack": "^5.0.0" } }, - "node_modules/ts-loader/node_modules/source-map": { - "version": "0.7.4", - "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.4.tgz", - "integrity": "sha512-l3BikUxvPOcn5E74dZiq5BGsTb5yEwhaTSzccU6t4sDOH8NWJCstKO5QT2CvtFoK6F0saL7p9xHAqHOlCPJygA==", - "dev": true, - "engines": { - "node": ">= 8" - } - }, "node_modules/ts-morph": { - "version": "19.0.0", - "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-19.0.0.tgz", - "integrity": "sha512-D6qcpiJdn46tUqV45vr5UGM2dnIEuTGNxVhg0sk5NX11orcouwj6i1bMqZIz2mZTZB1Hcgy7C3oEVhAT+f6mbQ==", + "version": "25.0.1", + "resolved": "https://registry.npmjs.org/ts-morph/-/ts-morph-25.0.1.tgz", + "integrity": "sha512-QJEiTdnz1YjrB3JFhd626gX4rKHDLSjSVMvGGG4v7ONc3RBwa0Eei98G9AT9uNFDMtV54JyuXsFeC+OH0n6bXQ==", "dev": true, + "license": "MIT", "dependencies": { - "@ts-morph/common": "~0.20.0", - "code-block-writer": "^12.0.0" + "@ts-morph/common": "~0.26.0", + "code-block-writer": "^13.0.3" } }, "node_modules/ts-node": { - "version": "10.9.1", - "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.1.tgz", - "integrity": "sha512-NtVysVPkxxrwFGUUxGYhfux8k78pQB3JqYBXlLRZgdGUqTO5wU/UyHop5p70iEbGhB7q5KmiZiU0Y3KlJrScEw==", + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/ts-node/-/ts-node-10.9.2.tgz", + "integrity": "sha512-f0FFpIdcHgn8zcPSbf1dRevwt047YMnaiJM3u2w2RewrB+fob/zePZcrOyQoLMMO7aBIddLcQIEK5dYjkLnGrQ==", "dev": true, + "license": "MIT", "dependencies": { "@cspotcode/source-map-support": "^0.8.0", "@tsconfig/node10": "^1.0.7", @@ -17420,13 +16890,14 @@ } }, "node_modules/tsec": { - "version": "0.2.7", - "resolved": "https://registry.npmjs.org/tsec/-/tsec-0.2.7.tgz", - "integrity": "sha512-Pj9DuBBWLEo8p7QsbrEdXzW/u6QJBcib0ZGOTXkeSDx+PLXFY7hwyZE9Tfhp3TA3LQNpYouyT0WmzXRyUW4otQ==", + "version": "0.2.9", + "resolved": "https://registry.npmjs.org/tsec/-/tsec-0.2.9.tgz", + "integrity": "sha512-6YCqU5GYg5RBYuDqld74MB4DAwkwBT09gz5hUWS/l8bKthTJwoO62ND6txkhvfGNcmIpWIYVl6h0oqHPG7zqcA==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "glob": "^7.1.1", - "minimatch": "^3.0.3" + "glob": "^11.0.3", + "minimatch": "^10.0.3" }, "bin": { "tsec": "bin/tsec" @@ -17438,37 +16909,95 @@ } }, "node_modules/tsec/node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "deprecated": "Glob versions prior to v9 are no longer supported", + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.1.0.tgz", + "integrity": "sha512-vuNwKSaKiqm7g0THUBu2x7ckSs3XJLXE+2ssL7/MfTGPLLcrJQ/4Uq1CjPTtO5cCIiRxqvN6Twy1qOwhL0Xjcw==", "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "foreground-child": "^3.3.1", + "jackspeak": "^4.1.1", + "minimatch": "^10.1.1", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" }, "engines": { - "node": "*" + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/tsec/node_modules/jackspeak": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.1.1.tgz", + "integrity": "sha512-zptv57P3GpL+O0I7VdMJNBZCu+BPHVQUk55Ft8/QCJjTVxrnJHuVuX/0Bl2A6/+2oyR/ZMEuFKwmzqqZ/U5nPQ==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/tsec/node_modules/lru-cache": { + "version": "11.2.4", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.2.4.tgz", + "integrity": "sha512-B5Y16Jr9LB9dHVkh6ZevG+vAbOsNOYCX+sXvFWFu7B3Iz5mijW3zdbMyhsh8ANd2mSWBYdJgnqi+mL7/LrOPYg==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/tsec/node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "license": "ISC", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/tsec/node_modules/path-scurry": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.1.tgz", + "integrity": "sha512-oWyT4gICAu+kaA7QWk/jvCHWarMKNs6pXOGWKDTr7cw4IGcUbW+PeTfbaQiLGheFRpjo6O9J0PmyMfQPjH71oA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/tslib": { - "version": "2.6.3", - "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.3.tgz", - "integrity": "sha512-xNvxJEOUiWPGhUuUdQgAJPKOOJfGnIyKySOc09XkKsgdUV/3E2zvwZYdejjmRgPCgcym1juLH3226yA7sEFJKQ==", - "dev": true + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" }, "node_modules/tsscmp": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/tsscmp/-/tsscmp-1.0.6.tgz", "integrity": "sha512-LxhtAkPDTkVCMQjt2h6eBVY28KCjikZqZfMcC15YBeNjkgUpdCfBu5HoiOTDu86v6smE8yOjyEktJ8hlbANHQA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.6.x" } @@ -17516,33 +17045,70 @@ "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } }, + "node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/type-is": { - "version": "1.6.18", - "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", - "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-2.0.1.tgz", + "integrity": "sha512-OZs6gsjF4vMp32qrCbiVSkrFmXtG/AZhY3t0iAMrMBiAZyV9oALtXO8hsrHbMXF9x6L3grlFuwW2oAz7cav+Gw==", "dev": true, + "license": "MIT", "dependencies": { - "media-typer": "0.3.0", - "mime-types": "~2.1.24" + "content-type": "^1.0.5", + "media-typer": "^1.1.0", + "mime-types": "^3.0.0" }, "engines": { "node": ">= 0.6" } }, - "node_modules/typedarray": { - "version": "0.0.6", - "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", - "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c= sha512-/aCDEGatGvZ2BIk+HmLf4ifCJFwvKFNb9/JeZPMulfgFracn9QFcAf5GO8B/mweUjSoblS5In0cWhqpfs/5PQA==", - "dev": true + "node_modules/type-is/node_modules/mime-db": { + "version": "1.54.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.54.0.tgz", + "integrity": "sha512-aU5EJuIN2WDemCcAp2vFBfp/m4EAhWJnUNSSw0ixs7/kXbd6Pg64EmwJkNdFhB8aWt1sH2CTXrLxo/iAGV3oPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/type-is/node_modules/mime-types": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-3.0.2.tgz", + "integrity": "sha512-Lbgzdk0h4juoQ9fCKXW4by0UJqj+nOOrI9MJ1sSj4nI8aI2eo1qmvQEie4VD1glsS250n15LsWsYtCugiStS5A==", + "dev": true, + "license": "MIT", + "dependencies": { + "mime-db": "^1.54.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" + } }, "node_modules/typescript": { - "version": "5.8.0-dev.20250207", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.0-dev.20250207.tgz", - "integrity": "sha512-bRCO1GkVxTLd/UFJWOg9R1oRiSMidcfpICzuQlDJlHspv6hlcJvvIJP0BvQxrBYpu4dbzqp/Fh8rRYIkEjbSlQ==", + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", "dev": true, "license": "Apache-2.0", "bin": { @@ -17655,19 +17221,43 @@ "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, - "peerDependenciesMeta": { - "typescript": { - "optional": true - } + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/typescript-eslint/node_modules/@typescript-eslint/utils": { + "version": "8.8.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.8.0.tgz", + "integrity": "sha512-QE2MgfOTem00qrlPgyByaCHay9yb1+9BjnMFnSFkUKQfu7adBXDTnCAivURnuPPAG/qiB+kzKkZKmKfaMT0zVg==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.8.0", + "@typescript-eslint/types": "8.8.0", + "@typescript-eslint/typescript-estree": "8.8.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" } }, "node_modules/typical": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz", - "integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==", + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/typical/-/typical-7.3.0.tgz", + "integrity": "sha512-ya4mg/30vm+DOWfBg4YK3j2WD6TWtRkCbasOJr40CseYENzCUby/7rIvXA99JGsQHeNxLbnXdyLLxKSv3tauFw==", "dev": true, + "license": "MIT", "engines": { - "node": ">=8" + "node": ">=12.17" } }, "node_modules/uc.micro": { @@ -17686,79 +17276,56 @@ } }, "node_modules/undertaker": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-1.3.0.tgz", - "integrity": "sha512-/RXwi5m/Mu3H6IHQGww3GNt1PNXlbeCuclF2QYR14L/2CHPz3DFZkvB5hZ0N/QUkiXWCACML2jXViIQEQc2MLg==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/undertaker/-/undertaker-2.0.0.tgz", + "integrity": "sha512-tO/bf30wBbTsJ7go80j0RzA2rcwX6o7XPBpeFcb+jzoeb4pfMM2zUeSDIkY1AWqeZabWxaQZ/h8N9t35QKDLPQ==", "dev": true, + "license": "MIT", "dependencies": { - "arr-flatten": "^1.0.1", - "arr-map": "^2.0.0", - "bach": "^1.0.0", - "collection-map": "^1.0.0", - "es6-weak-map": "^2.0.1", - "fast-levenshtein": "^1.0.0", - "last-run": "^1.1.0", - "object.defaults": "^1.0.0", - "object.reduce": "^1.0.0", - "undertaker-registry": "^1.0.0" + "bach": "^2.0.1", + "fast-levenshtein": "^3.0.0", + "last-run": "^2.0.0", + "undertaker-registry": "^2.0.0" }, "engines": { - "node": ">= 0.10" + "node": ">=10.13.0" } }, "node_modules/undertaker-registry": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/undertaker-registry/-/undertaker-registry-1.0.1.tgz", - "integrity": "sha1-XkvaMI5KiirlhPm5pDWaSZglzFA= sha512-UR1khWeAjugW3548EfQmL9Z7pGMlBgXteQpr1IZeZBtnkCJQJIJ1Scj0mb9wQaPvUZ9Q17XqW6TIaPchJkyfqw==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/undertaker-registry/-/undertaker-registry-2.0.0.tgz", + "integrity": "sha512-+hhVICbnp+rlzZMgxXenpvTxpuvA67Bfgtt+O9WOE5jo7w/dyiF1VmoZVIHvP2EkUjsyKyTwYKlLhA+j47m1Ew==", "dev": true, + "license": "MIT", "engines": { - "node": ">= 0.10" + "node": ">= 10.13.0" } }, "node_modules/undertaker/node_modules/fast-levenshtein": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-1.1.4.tgz", - "integrity": "sha1-5qdUzI8V5YmHqpy9J69m/W9OWvk= sha512-Ia0sQNrMPXXkqVFt6w6M1n1oKo3NfKs+mvaV811Jwir7vAk9a6PVV9VPYf6X3BU97QiLEmuW3uXH9u87zDFfdw==", - "dev": true + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-3.0.0.tgz", + "integrity": "sha512-hKKNajm46uNmTlhHSyZkmToAc56uZJwYq7yrciZjqOxnlfQwERDQJmHPUp7m1m9wx8vgOe8IaCKZ5Kv2k1DdCQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "fastest-levenshtein": "^1.0.7" + } }, "node_modules/undici": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.2.3.tgz", - "integrity": "sha512-2oSLHaDalSt2/O/wHA9M+/ZPAOcU2yrSP/cdBYJ+YxZskiPYDSqHbysLSlD7gq3JMqOoJI5O31RVU3BxX/MnAA==", + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.18.2.tgz", + "integrity": "sha512-y+8YjDFzWdQlSE9N5nzKMT3g4a5UBX1HKowfdXh0uvAnTaqqwqB92Jt4UXBAeKekDs5IaDKyJFR4X1gYVCgXcw==", "license": "MIT", "engines": { "node": ">=20.18.1" } }, "node_modules/undici-types": { - "version": "5.26.5", - "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", - "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", - "dev": true - }, - "node_modules/union-value": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/union-value/-/union-value-1.0.1.tgz", - "integrity": "sha512-tJfXmxMeWYnczCVs7XAEvIV7ieppALdyepWMkHkwciRpZraG/xwT+s2JN8+pr1+8jCRf80FFzvr+MpQeeoF4Xg==", - "dev": true, - "dependencies": { - "arr-union": "^3.1.0", - "get-value": "^2.0.6", - "is-extendable": "^0.1.1", - "set-value": "^2.0.1" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/union-value/node_modules/is-extendable": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz", - "integrity": "sha1-YrEQ4omkcUGOPsNqYX1HLjAd/Ik= sha512-5BMULNob1vgFX6EjQw5izWDxrecWK9AM72rugNr0TFldMOi0fj6Jk+zeKIt0xGj4cEfQIJth4w3OKWOJ4f+AFw==", + "version": "7.16.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.16.0.tgz", + "integrity": "sha512-Zz+aZWSj8LE6zoxD+xrjh4VfkIG8Ya6LvYkZqtUQGJPZjYl53ypCaUwWqo7eI0x66KBGeRo+mlBEkMSeSZ38Nw==", "dev": true, - "engines": { - "node": ">=0.10.0" - } + "license": "MIT" }, "node_modules/unique-stream": { "version": "2.3.1", @@ -17771,10 +17338,11 @@ } }, "node_modules/universal-user-agent": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-6.0.0.tgz", - "integrity": "sha512-isyNax3wXoKaulPDZWHQqbmIx1k2tb9fb3GGDBRxCscfYV2Ch7WxPArBsFEG8s/safwXTT7H4QGhaIkTp9447w==", - "dev": true + "version": "7.0.3", + "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz", + "integrity": "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==", + "dev": true, + "license": "ISC" }, "node_modules/universalify": { "version": "2.0.1", @@ -17784,64 +17352,6 @@ "node": ">= 10.0.0" } }, - "node_modules/unset-value": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/unset-value/-/unset-value-1.0.0.tgz", - "integrity": "sha1-g3aHP30jNRef+x5vw6jtDfyKtVk= sha512-PcA2tsuGSF9cnySLHTLSh2qrQiJ70mn+r+Glzxv2TWZblxsxCC52BDlZoPCsz7STd9pN7EZetkWZBAvk4cgZdQ==", - "dev": true, - "dependencies": { - "has-value": "^0.3.1", - "isobject": "^3.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/unset-value/node_modules/has-value": { - "version": "0.3.1", - "resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz", - "integrity": "sha1-ex9YutpiyoJ+wKIHgCVlSEWZXh8= sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==", - "dev": true, - "dependencies": { - "get-value": "^2.0.3", - "has-values": "^0.1.4", - "isobject": "^2.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/unset-value/node_modules/has-value/node_modules/isobject": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz", - "integrity": "sha1-8GVWEJaj8dou9GJy+BXIQNh+DIk= sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==", - "dev": true, - "dependencies": { - "isarray": "1.0.0" - }, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/unset-value/node_modules/has-values": { - "version": "0.1.4", - "resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz", - "integrity": "sha1-bWHeldkd/Km5oCCJrThL/49it3E= sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, - "node_modules/upath": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/upath/-/upath-1.2.0.tgz", - "integrity": "sha512-aZwGpamFO61g3OlfT7OQCHqhGnW43ieH9WZeP7QxN/G/jS4jfqUkZxoryvJgVPEcrl5NL/ggHsSmLMHuH64Lhg==", - "dev": true, - "engines": { - "node": ">=4", - "yarn": "*" - } - }, "node_modules/update-browserslist-db": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.1.tgz", @@ -17891,13 +17401,6 @@ "node": ">=6" } }, - "node_modules/urix": { - "version": "0.1.0", - "resolved": "https://registry.npmjs.org/urix/-/urix-0.1.0.tgz", - "integrity": "sha1-2pN/emLiH+wf0Y1Js1wpNQZ6bHI= sha512-Am1ousAhSLBeB9cG/7k7r2R0zj50uDRlZHPGbazid5s9rlF1F/QKYObEKSIunSjIOkJZqwRRLpvewjEkM7pSqg==", - "deprecated": "Please see https://github.com/lydell/urix#deprecated", - "dev": true - }, "node_modules/url-parse": { "version": "1.5.10", "resolved": "https://registry.npmjs.org/url-parse/-/url-parse-1.5.10.tgz", @@ -17908,15 +17411,6 @@ "requires-port": "^1.0.0" } }, - "node_modules/use": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/use/-/use-3.1.1.tgz", - "integrity": "sha512-cwESVXlO3url9YWlFW/TA9cshCEhtu7IKJ/p5soJ/gGpj7vbvFrAY/eIioQ6Dw23KjZhYgiIo8HOs1nQ2vr/oQ==", - "dev": true, - "engines": { - "node": ">=0.10.0" - } - }, "node_modules/util": { "version": "0.12.5", "resolved": "https://registry.npmjs.org/util/-/util-0.12.5.tgz", @@ -17962,10 +17456,11 @@ } }, "node_modules/v8-to-istanbul": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz", - "integrity": "sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==", + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", "dev": true, + "license": "ISC", "dependencies": { "@jridgewell/trace-mapping": "^0.3.12", "@types/istanbul-lib-coverage": "^2.0.1", @@ -17979,18 +17474,17 @@ "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/v8flags": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-3.2.0.tgz", - "integrity": "sha512-mH8etigqMfiGWdeXpaaqGfs6BndypxusHHcv2qSHyZkGEznCd/qAXCWWRzeowtL54147cktFOC4P5y+kl8d8Jg==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/v8flags/-/v8flags-4.0.1.tgz", + "integrity": "sha512-fcRLaS4H/hrZk9hYwbdRM35D0U8IYMfEClhXxCivOojl+yTRAZH3Zy2sSy6qVCiGbV9YAtPssP6jaChqC9vPCg==", "dev": true, - "dependencies": { - "homedir-polyfill": "^1.0.1" - }, + "license": "MIT", "engines": { - "node": ">= 0.10" + "node": ">= 10.13.0" } }, "node_modules/validate-npm-package-license": { @@ -18022,13 +17516,13 @@ } }, "node_modules/vinyl": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-3.0.0.tgz", - "integrity": "sha512-rC2VRfAVVCGEgjnxHUnpIVh3AGuk62rP3tqVrn+yab0YH7UULisC085+NYH+mnqf3Wx4SpSi1RQMwudL89N03g==", + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-3.0.1.tgz", + "integrity": "sha512-0QwqXteBNXgnLCdWdvPQBX6FXRHtIH3VhJPTd5Lwn28tJXc34YqSCWUmkOvtJHBmB3gGoPtrOKk3Ts8/kEZ9aA==", "dev": true, + "license": "MIT", "dependencies": { "clone": "^2.1.2", - "clone-stats": "^1.0.0", "remove-trailing-separator": "^1.1.0", "replace-ext": "^2.0.0", "teex": "^1.0.1" @@ -18037,6 +17531,57 @@ "node": ">=10.13.0" } }, + "node_modules/vinyl-contents": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/vinyl-contents/-/vinyl-contents-2.0.0.tgz", + "integrity": "sha512-cHq6NnGyi2pZ7xwdHSW1v4Jfnho4TEGtxZHw01cmnc8+i7jgR6bRnED/LbrKan/Q7CvVLbnvA5OepnhbpjBZ5Q==", + "dev": true, + "license": "MIT", + "dependencies": { + "bl": "^5.0.0", + "vinyl": "^3.0.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/vinyl-contents/node_modules/bl": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-5.1.0.tgz", + "integrity": "sha512-tv1ZJHLfTDnXE6tMHv73YgSJaWR2AFuPwMntBe7XL/GBFHnT0CLnsHMogfk5+GzCDC5ZWarSCYaIGATZt9dNsQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "buffer": "^6.0.3", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/vinyl-contents/node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, "node_modules/vinyl-fs": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/vinyl-fs/-/vinyl-fs-3.0.3.tgz", @@ -18173,9 +17718,10 @@ } }, "node_modules/vscode-oniguruma": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", - "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-2.0.1.tgz", + "integrity": "sha512-poJU8iHIWnC3vgphJnrLZyI3YdqRlR27xzqDmpPXYzA93R4Gk8z7T6oqDzDoHjoikA2aS82crdXFkjELCdJsjQ==", + "license": "MIT" }, "node_modules/vscode-regexpp": { "version": "3.1.0", @@ -18195,10 +17741,11 @@ "license": "MIT" }, "node_modules/vscode-uri": { - "version": "3.0.8", - "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.0.8.tgz", - "integrity": "sha512-AyFQ0EVmsOZOlAnxoFOGOq1SQDWAB7C6aqMGS23svWAllfOaxbuFvcT8D1i8z3Gyn8fraVeZNNmN6e9bxxXkKw==", - "dev": true + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/vscode-uri/-/vscode-uri-3.1.0.tgz", + "integrity": "sha512-/BpdSx+yCQGnCvecbyXdxHDkuk55/G3xwnC0GqY4gmQ3j+A+g8kzzgB4Nk/SINjqn6+waqw3EgbVF2QKExkRxQ==", + "dev": true, + "license": "MIT" }, "node_modules/watchpack": { "version": "2.4.1", @@ -18272,42 +17819,40 @@ } }, "node_modules/webpack-cli": { - "version": "5.1.4", - "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-5.1.4.tgz", - "integrity": "sha512-pIDJHIEI9LR0yxHXQ+Qh95k2EvXpWzZ5l+d+jIo+RdSm9MiHfzazIxwwni/p7+x4eJZuvG1AJwgC4TNQ7NRgsg==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/webpack-cli/-/webpack-cli-6.0.1.tgz", + "integrity": "sha512-MfwFQ6SfwinsUVi0rNJm7rHZ31GyTcpVE5pgVA3hwFRb7COD4TzjUUwhGWKfO50+xdc2MQPuEBBJoqIMGt3JDw==", "dev": true, + "license": "MIT", "dependencies": { - "@discoveryjs/json-ext": "^0.5.0", - "@webpack-cli/configtest": "^2.1.1", - "@webpack-cli/info": "^2.0.2", - "@webpack-cli/serve": "^2.0.5", + "@discoveryjs/json-ext": "^0.6.1", + "@webpack-cli/configtest": "^3.0.1", + "@webpack-cli/info": "^3.0.1", + "@webpack-cli/serve": "^3.0.1", "colorette": "^2.0.14", - "commander": "^10.0.1", + "commander": "^12.1.0", "cross-spawn": "^7.0.3", - "envinfo": "^7.7.3", + "envinfo": "^7.14.0", "fastest-levenshtein": "^1.0.12", "import-local": "^3.0.2", "interpret": "^3.1.1", "rechoir": "^0.8.0", - "webpack-merge": "^5.7.3" + "webpack-merge": "^6.0.1" }, "bin": { "webpack-cli": "bin/cli.js" }, "engines": { - "node": ">=14.15.0" + "node": ">=18.12.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/webpack" }, "peerDependencies": { - "webpack": "5.x.x" + "webpack": "^5.82.0" }, "peerDependenciesMeta": { - "@webpack-cli/generators": { - "optional": true - }, "webpack-bundle-analyzer": { "optional": true }, @@ -18317,46 +17862,28 @@ } }, "node_modules/webpack-cli/node_modules/commander": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz", - "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==", - "dev": true, - "engines": { - "node": ">=14" - } - }, - "node_modules/webpack-cli/node_modules/interpret": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/interpret/-/interpret-3.1.1.tgz", - "integrity": "sha512-6xwYfHbajpoF0xLW+iwLkhwgvLoZDfjYfoFNu8ftMoXINzwuymNLd9u/KmwtdT2GbR+/Cz66otEGEVVUHX9QLQ==", - "dev": true, - "engines": { - "node": ">=10.13.0" - } - }, - "node_modules/webpack-cli/node_modules/rechoir": { - "version": "0.8.0", - "resolved": "https://registry.npmjs.org/rechoir/-/rechoir-0.8.0.tgz", - "integrity": "sha512-/vxpCXddiX8NGfGO/mTafwjq4aFa/71pvamip0++IQk3zG8cbCj0fifNPrjjF1XMXUne91jL9OoxmdykoEtifQ==", + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", "dev": true, - "dependencies": { - "resolve": "^1.20.0" - }, + "license": "MIT", "engines": { - "node": ">= 10.13.0" + "node": ">=18" } }, "node_modules/webpack-merge": { - "version": "5.8.0", - "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-5.8.0.tgz", - "integrity": "sha512-/SaI7xY0831XwP6kzuwhKWVKDP9t1QY1h65lAFLbZqMPIuYcD9QAW4u9STIbU9kaJbPBB/geU/gLr1wDjOhQ+Q==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/webpack-merge/-/webpack-merge-6.0.1.tgz", + "integrity": "sha512-hXXvrjtx2PLYx4qruKl+kyRSLc52V+cCvMxRjmKwoA+CBbbF5GfIBtR6kCvl0fYGqTUPKB+1ktVmTHqMOzgCBg==", "dev": true, + "license": "MIT", "dependencies": { "clone-deep": "^4.0.1", - "wildcard": "^2.0.0" + "flat": "^5.0.2", + "wildcard": "^2.0.1" }, "engines": { - "node": ">=10.0.0" + "node": ">=18.0.0" } }, "node_modules/webpack-sources": { @@ -18390,6 +17917,22 @@ "webpack": "^5.21.2" } }, + "node_modules/webpack-stream/node_modules/fancy-log": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/fancy-log/-/fancy-log-1.3.3.tgz", + "integrity": "sha512-k9oEhlyc0FrVh25qYuSELjr8oxsCoc4/LEZfg2iJJrfEk/tZL9bCoJE47gqAvI2m/AUjluCS4+3I0eTx8n3AEw==", + "dev": true, + "license": "MIT", + "dependencies": { + "ansi-gray": "^0.1.1", + "color-support": "^1.1.3", + "parse-node-version": "^1.0.0", + "time-stamp": "^1.0.0" + }, + "engines": { + "node": ">= 0.10" + } + }, "node_modules/webpack-stream/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -18531,10 +18074,11 @@ } }, "node_modules/wildcard": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.0.tgz", - "integrity": "sha512-JcKqAHLPxcdb9KM49dufGXn2x3ssnfjbcaQdLlfZsL9rH9wgDQjUtDxbo8NE0F6SFvydeu1VhZe7hZuHsB2/pw==", - "dev": true + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/wildcard/-/wildcard-2.0.1.tgz", + "integrity": "sha512-CC1bOL87PIWSBhDcTrdeLo6eGT7mCFtrg0uIJtqJUFyK+eJnzl8A1niH56uu7KMa5XFrtiV+AQuHO3n7DsHnLQ==", + "dev": true, + "license": "MIT" }, "node_modules/windows-foreground-love": { "version": "0.5.0", @@ -18553,9 +18097,9 @@ } }, "node_modules/workerpool": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", - "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", + "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", "dev": true, "license": "Apache-2.0" }, @@ -18639,6 +18183,37 @@ "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" }, + "node_modules/wsl-utils": { + "version": "0.3.1", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.3.1.tgz", + "integrity": "sha512-g/eziiSUNBSsdDJtCLB8bdYEUMj4jR7AGeUo96p/3dTafgjHhpF4RiCFPiRILwjQoDXx5MqkBr4fwWtR3Ky4Wg==", + "license": "MIT", + "dependencies": { + "is-wsl": "^3.1.0", + "powershell-utils": "^0.1.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/wsl-utils/node_modules/is-wsl": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.0.tgz", + "integrity": "sha512-UcVfVfaK4Sc4m7X3dUSoHoozQGBEFeDC+zVo06t98xe8CzHSZZBekNXH+tu0NalHolcJ/QAGqS46Hef7QXBIMw==", + "license": "MIT", + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/xml": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/xml/-/xml-1.0.1.tgz", @@ -18646,10 +18221,11 @@ "dev": true }, "node_modules/xml2js": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", - "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.6.2.tgz", + "integrity": "sha512-T4rieHaC1EXcES0Kxxj4JWgaUQHDk+qwHcYOCFHfiwKz7tOVPLq7Hjq9dM1WCMhylqMEfP7hMcOIChvotiZegA==", "dev": true, + "license": "MIT", "dependencies": { "sax": ">=0.6.0", "xmlbuilder": "~11.0.0" @@ -18658,11 +18234,12 @@ "node": ">=4.0.0" } }, - "node_modules/xml2js/node_modules/xmlbuilder": { + "node_modules/xmlbuilder": { "version": "11.0.1", "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", "dev": true, + "license": "MIT", "engines": { "node": ">=4.0" } @@ -18686,10 +18263,10 @@ } }, "node_modules/yallist": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-2.1.2.tgz", - "integrity": "sha1-HBH5IY8HYImkfdUS+TxmmaaoHVI= sha512-ncTzHV7NvsQZkYe1DW7cbDLm0YpzHmZF5r/iyP3ZnQtMiJ+pjzisCiMNI+Sj+xQF5pXhSHxSB3uDbsBTzY/c2A==", - "dev": true + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" }, "node_modules/yargs": { "version": "17.7.2", @@ -18791,15 +18368,6 @@ "buffer-crc32": "~0.2.3" } }, - "node_modules/ylru": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/ylru/-/ylru-1.2.1.tgz", - "integrity": "sha512-faQrqNMzcPCHGVC2aaOINk13K+aaBDUPjGWl0teOXywElLjyVAB6Oe2jj62jHYtwsU49jXhScYbvPENK+6zAvQ==", - "dev": true, - "engines": { - "node": ">= 4.0.0" - } - }, "node_modules/yn": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yn/-/yn-3.1.1.tgz", diff --git a/package.json b/package.json index 3f736d096b46..f8bd5abf5c1e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", - "version": "1.98.0", - "distro": "b37092a45bb95e5098cefcf6809fa6973cfd2538", + "version": "1.99.0", + "distro": "c3ec5ba4852b5682b94358c92bf31484d2739db9", "author": { "name": "Microsoft Corporation" }, @@ -45,6 +45,7 @@ "tsec-compile-check": "node node_modules/tsec/bin/tsec -p src/tsconfig.tsec.json", "vscode-dts-compile-check": "tsc -p src/tsconfig.vscode-dts.json && tsc -p src/tsconfig.vscode-proposed-dts.json", "valid-layers-check": "node build/lib/layersChecker.js", + "property-init-order-check": "node build/lib/propertyInitOrderChecker.js", "update-distro": "node build/npm/update-distro.mjs", "web": "echo 'npm run web' is replaced by './scripts/code-server' or './scripts/code-web'", "compile-cli": "gulp compile-cli", @@ -68,157 +69,158 @@ "update-build-ts-version": "npm install typescript@next && tsc -p ./build/tsconfig.build.json" }, "dependencies": { + "@c4312/eventsource-umd": "^3.0.5", "@microsoft/1ds-core-js": "^3.2.13", - "@microsoft/1ds-post-js": "^3.2.13", + "@microsoft/1ds-post-js": "^4.3.11", "@parcel/watcher": "2.5.1", "@types/semver": "^7.5.8", "@vscode/deviceid": "^0.1.1", "@vscode/iconv-lite-umd": "0.7.0", - "@vscode/policy-watcher": "^1.1.10", - "@vscode/proxy-agent": "^0.31.0", - "@vscode/ripgrep": "^1.15.10", - "@vscode/spdlog": "^0.15.0", + "@vscode/policy-watcher": "^1.3.0", + "@vscode/proxy-agent": "^0.32.0", + "@vscode/ripgrep": "^1.17.0", + "@vscode/spdlog": "^0.15.7", "@vscode/sqlite3": "5.1.8-vscode", "@vscode/sudo-prompt": "9.3.1", - "@vscode/tree-sitter-wasm": "^0.1.2", - "@vscode/vscode-languagedetection": "1.0.21", - "@vscode/windows-mutex": "^0.5.0", - "@vscode/windows-process-tree": "^0.6.0", + "@vscode/tree-sitter-wasm": "^0.1.3", + "@vscode/vscode-languagedetection": "1.0.23", + "@vscode/windows-mutex": "^0.5.3", + "@vscode/windows-process-tree": "^0.6.3", "@vscode/windows-registry": "^1.1.0", - "@xterm/addon-clipboard": "^0.2.0-beta.81", - "@xterm/addon-image": "^0.9.0-beta.98", - "@xterm/addon-ligatures": "^0.10.0-beta.98", - "@xterm/addon-progress": "^0.2.0-beta.4", - "@xterm/addon-search": "^0.16.0-beta.98", - "@xterm/addon-serialize": "^0.14.0-beta.98", - "@xterm/addon-unicode11": "^0.9.0-beta.98", - "@xterm/addon-webgl": "^0.19.0-beta.98", - "@xterm/headless": "^5.6.0-beta.98", - "@xterm/xterm": "^5.6.0-beta.98", + "@xterm/addon-clipboard": "^0.2.0-beta.82", + "@xterm/addon-image": "^0.9.0-beta.99", + "@xterm/addon-ligatures": "^0.11.0-beta.107", + "@xterm/addon-progress": "^0.2.0-beta.5", + "@xterm/addon-search": "^0.17.0-beta.105", + "@xterm/addon-serialize": "^0.14.0-beta.99", + "@xterm/addon-unicode11": "^0.10.0-beta.107", + "@xterm/addon-webgl": "^0.19.0-beta.99", + "@xterm/headless": "^5.6.0-beta.99", + "@xterm/xterm": "^6.0.0", "http-proxy-agent": "^7.0.0", - "https-proxy-agent": "^7.0.2", + "https-proxy-agent": "^7.0.6", "jschardet": "3.1.4", "kerberos": "2.1.1", "minimist": "^1.2.6", "native-is-elevated": "0.7.0", "native-keymap": "^3.3.5", "native-watchdog": "^1.4.1", - "node-pty": "1.1.0-beta30", - "open": "^8.4.2", + "node-pty": "1.1.0-beta31", + "open": "^11.0.0", "tas-client-umd": "0.2.0", "v8-inspect-profiler": "^0.1.1", - "vscode-oniguruma": "1.7.0", + "vscode-oniguruma": "2.0.1", "vscode-regexpp": "^3.1.0", "vscode-textmate": "9.2.0", "yauzl": "^3.0.0", "yazl": "^2.4.3" }, "devDependencies": { - "@playwright/test": "^1.50.0", - "@stylistic/eslint-plugin-ts": "^2.8.0", - "@types/cookie": "^0.3.3", + "@playwright/test": "^1.56.1", + "@stylistic/eslint-plugin-ts": "^4.4.1", + "@types/cookie": "^1.0.0", "@types/debug": "^4.1.5", "@types/eslint": "^9.6.1", - "@types/gulp-svgmin": "^1.2.1", - "@types/http-proxy-agent": "^2.0.1", + "@types/gulp-svgmin": "^1.2.5", + "@types/http-proxy-agent": "^4.0.1", "@types/kerberos": "^1.1.2", - "@types/minimist": "^1.2.1", - "@types/mocha": "^9.1.1", - "@types/node": "20.x", - "@types/sinon": "^10.0.2", + "@types/minimist": "^1.2.5", + "@types/mocha": "^10.0.10", + "@types/node": "25.x", + "@types/sinon": "^21.0.0", "@types/sinon-test": "^2.4.2", - "@types/trusted-types": "^1.0.6", - "@types/vscode-notebook-renderer": "^1.72.0", + "@types/trusted-types": "^2.0.7", + "@types/vscode-notebook-renderer": "^1.72.4", "@types/webpack": "^5.28.5", "@types/wicg-file-system-access": "^2020.9.6", "@types/windows-foreground-love": "^0.3.0", "@types/winreg": "^1.2.30", "@types/yauzl": "^2.10.0", "@types/yazl": "^2.4.2", - "@typescript-eslint/utils": "^8.8.0", - "@vscode/gulp-electron": "^1.36.0", + "@typescript-eslint/utils": "^8.53.1", + "@vscode/gulp-electron": "^1.38.2", "@vscode/l10n-dev": "0.0.35", - "@vscode/telemetry-extractor": "^1.10.2", - "@vscode/test-cli": "^0.0.6", + "@vscode/telemetry-extractor": "^1.18.0", + "@vscode/test-cli": "^0.0.12", "@vscode/test-electron": "^2.4.0", - "@vscode/test-web": "^0.0.62", + "@vscode/test-web": "^0.0.77", "@vscode/v8-heap-parser": "^0.1.0", - "@vscode/vscode-perf": "^0.0.19", - "@webgpu/types": "^0.1.44", + "@vscode/vscode-perf": "^0.0.25", + "@webgpu/types": "^0.1.69", "ansi-colors": "^3.2.3", "asar": "^3.0.3", "chromium-pickle-js": "^0.2.0", - "cookie": "^0.7.2", - "copy-webpack-plugin": "^11.0.0", - "css-loader": "^6.9.1", + "cookie": "^1.1.1", + "copy-webpack-plugin": "^13.0.1", + "css-loader": "^7.1.2", "cssnano": "^6.0.3", - "debounce": "^1.0.0", - "deemon": "^1.8.0", - "electron": "34.1.0", - "eslint": "^9.11.1", - "eslint-formatter-compact": "^8.40.0", + "debounce": "^3.0.0", + "deemon": "^1.13.6", + "electron": "40.0.0", + "eslint": "^9.39.2", + "eslint-formatter-compact": "^9.0.1", "eslint-plugin-header": "3.1.1", - "eslint-plugin-jsdoc": "^50.3.1", + "eslint-plugin-jsdoc": "^61.5.0", "eslint-plugin-local": "^6.0.0", - "event-stream": "3.3.4", - "fancy-log": "^1.3.3", + "event-stream": "4.0.1", + "fancy-log": "^2.0.0", "file-loader": "^6.2.0", - "glob": "^5.0.13", - "gulp": "^4.0.0", + "glob": "^9.0.1", + "gulp": "^5.0.1", "gulp-azure-storage": "^0.12.1", - "gulp-bom": "^3.0.0", + "gulp-bom": "^5.0.0", "gulp-buffer": "0.0.2", "gulp-filter": "^5.1.0", "gulp-flatmap": "^1.0.2", "gulp-gunzip": "^1.0.0", "gulp-gzip": "^1.4.2", - "gulp-json-editor": "^2.5.0", - "gulp-plumber": "^1.2.0", - "gulp-rename": "^1.2.0", - "gulp-replace": "^0.5.4", + "gulp-json-editor": "^2.6.0", + "gulp-plumber": "^1.2.1", + "gulp-rename": "^2.1.0", + "gulp-replace": "^1.1.4", "gulp-sourcemaps": "^3.0.0", "gulp-svgmin": "^4.1.0", - "gulp-untar": "^0.0.7", - "husky": "^0.13.1", + "gulp-untar": "^0.0.8", + "husky": "^9.1.7", "innosetup": "^6.4.1", - "istanbul-lib-coverage": "^3.2.0", - "istanbul-lib-instrument": "^6.0.1", + "istanbul-lib-coverage": "^3.2.2", + "istanbul-lib-instrument": "^6.0.3", "istanbul-lib-report": "^3.0.0", - "istanbul-lib-source-maps": "^4.0.1", - "istanbul-reports": "^3.1.5", - "lazy.js": "^0.4.2", + "istanbul-lib-source-maps": "^5.0.6", + "istanbul-reports": "^3.2.0", + "lazy.js": "^0.5.1", "merge-options": "^1.0.1", "mime": "^1.4.1", - "minimatch": "^3.0.4", + "minimatch": "^10.1.1", "minimist": "^1.2.6", - "mocha": "^10.8.2", + "mocha": "^11.7.5", "mocha-junit-reporter": "^2.2.1", "mocha-multi-reporters": "^1.5.1", "npm-run-all": "^4.1.5", "os-browserify": "^0.3.0", - "p-all": "^1.0.0", + "p-all": "^5.0.1", "path-browserify": "^1.0.1", - "postcss": "^8.4.33", + "postcss": "^8.5.3", "postcss-nesting": "^12.0.2", - "pump": "^1.0.1", - "rcedit": "^1.1.0", - "rimraf": "^2.2.8", - "sinon": "^12.0.1", + "pump": "^3.0.3", + "rcedit": "^5.0.2", + "rimraf": "^6.1.2", + "sinon": "^21.0.0", "sinon-test": "^3.1.3", - "source-map": "0.6.1", + "source-map": "0.7.6", "source-map-support": "^0.3.2", "style-loader": "^3.3.2", - "ts-loader": "^9.5.1", - "ts-node": "^10.9.1", - "tsec": "0.2.7", - "tslib": "^2.6.3", - "typescript": "^5.8.0-dev.20250207", + "ts-loader": "^9.5.4", + "ts-node": "^10.9.2", + "tsec": "0.2.9", + "tslib": "^2.8.1", + "typescript": "^5.9.3", "typescript-eslint": "^8.8.0", "util": "^0.12.4", "webpack": "^5.94.0", - "webpack-cli": "^5.1.4", + "webpack-cli": "^6.0.1", "webpack-stream": "^7.0.0", - "xml2js": "^0.5.0", + "xml2js": "^0.6.2", "yaserver": "^0.4.0" }, "overrides": { diff --git a/product.json b/product.json index 873ea0eaa13d..1d4dca8fc3fc 100644 --- a/product.json +++ b/product.json @@ -25,6 +25,8 @@ "win32TunnelServiceMutex": "vscodeoss-tunnelservice", "win32TunnelMutex": "vscodeoss-tunnel", "darwinBundleIdentifier": "com.visualstudio.code.oss", + "darwinProfileUUID": "47827DD9-4734-49A0-AF80-7E19B11495CC", + "darwinProfilePayloadUUID": "CF808BE7-53F3-46C6-A7E2-7EDB98A5E959", "linuxIconName": "code-oss", "licenseFileName": "LICENSE.txt", "reportIssueUrl": "https://github.com/microsoft/vscode/issues/new", diff --git a/remote/.npmrc b/remote/.npmrc index b74c59d2b8f3..e2c53927b15e 100644 --- a/remote/.npmrc +++ b/remote/.npmrc @@ -1,6 +1,6 @@ disturl="https://nodejs.org/dist" -target="20.18.1" -ms_build_id="307485" +target="20.18.3" +ms_build_id="323695" runtime="node" build_from_source="true" legacy-peer-deps="true" diff --git a/remote/package-lock.json b/remote/package-lock.json index ea100d67bb40..e1cc5b253b61 100644 --- a/remote/package-lock.json +++ b/remote/package-lock.json @@ -8,97 +8,129 @@ "name": "vscode-reh", "version": "0.0.0", "dependencies": { - "@microsoft/1ds-core-js": "^3.2.13", - "@microsoft/1ds-post-js": "^3.2.13", - "@parcel/watcher": "2.5.1", + "@microsoft/1ds-core-js": "^4.3.11", + "@microsoft/1ds-post-js": "^4.3.11", + "@parcel/watcher": "2.5.4", "@vscode/deviceid": "^0.1.1", - "@vscode/iconv-lite-umd": "0.7.0", - "@vscode/proxy-agent": "^0.31.0", - "@vscode/ripgrep": "^1.15.10", - "@vscode/spdlog": "^0.15.0", - "@vscode/tree-sitter-wasm": "^0.1.2", - "@vscode/vscode-languagedetection": "1.0.21", + "@vscode/iconv-lite-umd": "0.7.1", + "@vscode/proxy-agent": "^0.37.0", + "@vscode/ripgrep": "^1.15.11", + "@vscode/spdlog": "^0.15.7", + "@vscode/tree-sitter-wasm": "^0.3.0", + "@vscode/vscode-languagedetection": "1.0.23", "@vscode/windows-process-tree": "^0.6.0", "@vscode/windows-registry": "^1.1.0", - "@xterm/addon-clipboard": "^0.2.0-beta.81", - "@xterm/addon-image": "^0.9.0-beta.98", - "@xterm/addon-ligatures": "^0.10.0-beta.98", - "@xterm/addon-progress": "^0.2.0-beta.4", - "@xterm/addon-search": "^0.16.0-beta.98", - "@xterm/addon-serialize": "^0.14.0-beta.98", - "@xterm/addon-unicode11": "^0.9.0-beta.98", - "@xterm/addon-webgl": "^0.19.0-beta.98", - "@xterm/headless": "^5.6.0-beta.98", - "@xterm/xterm": "^5.6.0-beta.98", - "cookie": "^0.7.0", + "@xterm/addon-clipboard": "^0.3.0-beta.107", + "@xterm/addon-image": "^0.10.0-beta.107", + "@xterm/addon-ligatures": "^0.11.0-beta.107", + "@xterm/addon-progress": "^0.3.0-beta.107", + "@xterm/addon-search": "^0.17.0-beta.107", + "@xterm/addon-serialize": "^0.15.0-beta.107", + "@xterm/addon-unicode11": "^0.10.0-beta.107", + "@xterm/addon-webgl": "^0.20.0-beta.106", + "@xterm/headless": "^6.0.0", + "@xterm/xterm": "^6.0.0", + "cookie": "^1.1.1", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.2", "jschardet": "3.1.4", - "kerberos": "2.1.1", - "minimist": "^1.2.6", + "kerberos": "7.0.0", + "minimist": "^1.2.8", "native-watchdog": "^1.4.1", - "node-pty": "1.1.0-beta30", + "node-pty": "1.1.0", "tas-client-umd": "0.2.0", - "vscode-oniguruma": "1.7.0", + "vscode-oniguruma": "2.0.1", "vscode-regexpp": "^3.1.0", - "vscode-textmate": "9.2.0", - "yauzl": "^3.0.0", - "yazl": "^2.4.3" + "vscode-textmate": "9.3.2", + "yauzl": "^3.2.0", + "yazl": "^3.3.1" } }, "node_modules/@microsoft/1ds-core-js": { - "version": "3.2.13", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-3.2.13.tgz", - "integrity": "sha512-CluYTRWcEk0ObG5EWFNWhs87e2qchJUn0p2D21ZUa3PWojPZfPSBs4//WIE0MYV8Qg1Hdif2ZTwlM7TbYUjfAg==", + "version": "4.3.11", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.11.tgz", + "integrity": "sha512-QyQE/YzFYB+31WEpX9hvDoXZOIXA7308Z5uuL1mSsyDSkNPl24hBWz9O3vZL+/p9shy756eKLI2nFLwwIAhXyw==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "2.8.15", - "@microsoft/applicationinsights-shims": "^2.0.2", - "@microsoft/dynamicproto-js": "^1.1.7" + "@microsoft/applicationinsights-core-js": "3.3.11", + "@microsoft/applicationinsights-shims": "3.0.1", + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" } }, "node_modules/@microsoft/1ds-post-js": { - "version": "3.2.13", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-3.2.13.tgz", - "integrity": "sha512-HgS574fdD19Bo2vPguyznL4eDw7Pcm1cVNpvbvBLWiW3x4e1FCQ3VMXChWnAxCae8Hb0XqlA2sz332ZobBavTA==", + "version": "4.3.11", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.11.tgz", + "integrity": "sha512-V0ZeeALy/Pj8HWgNHDsK+yDeCYnJ9bCgTWhcrna/ZiAT+sGfWs6mDBjAVcG03uP7TDjdWLf8w79lgbXJ3+s3DA==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "3.2.13", - "@microsoft/applicationinsights-shims": "^2.0.2", - "@microsoft/dynamicproto-js": "^1.1.7" + "@microsoft/1ds-core-js": "4.3.11", + "@microsoft/applicationinsights-shims": "3.0.1", + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" } }, "node_modules/@microsoft/applicationinsights-core-js": { - "version": "2.8.15", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.15.tgz", - "integrity": "sha512-yYAs9MyjGr2YijQdUSN9mVgT1ijI1FPMgcffpaPmYbHAVbQmF7bXudrBWHxmLzJlwl5rfep+Zgjli2e67lwUqQ==", + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.11.tgz", + "integrity": "sha512-WlBY1sKDNL62T++NifgFCyDuOoNUNrVILfnHubOzgU/od7MFEQYWU8EZyDcBC/+Z8e3TD6jfixurYtWoUC+6Eg==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-shims": "2.0.2", - "@microsoft/dynamicproto-js": "^1.1.9" + "@microsoft/applicationinsights-shims": "3.0.1", + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-shims": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-shims/-/applicationinsights-shims-2.0.2.tgz", - "integrity": "sha512-PoHEgsnmcqruLNHZ/amACqdJ6YYQpED0KSRe6J7gIJTtpZC1FfFU9b1fmDKDKtFoUSrPzEh1qzO3kmRZP0betg==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-shims/-/applicationinsights-shims-3.0.1.tgz", + "integrity": "sha512-DKwboF47H1nb33rSUfjqI6ryX29v+2QWcTrRvcQDA32AZr5Ilkr7whOOSsD1aBzwqX0RJEIP1Z81jfE3NBm/Lg==", + "license": "MIT", + "dependencies": { + "@nevware21/ts-utils": ">= 0.9.4 < 2.x" + } }, "node_modules/@microsoft/dynamicproto-js": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.9.tgz", - "integrity": "sha512-n1VPsljTSkthsAFYdiWfC+DKzK2WwcRp83Y1YAqdX552BstvsDjft9YXppjUzp11BPsapDoO1LDgrDB0XVsfNQ==" + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.3.tgz", + "integrity": "sha512-JTWTU80rMy3mdxOjjpaiDQsTLZ6YSGGqsjURsY6AUQtIj0udlF/jYmhdLZu8693ZIC0T1IwYnFa0+QeiMnziBA==", + "license": "MIT", + "dependencies": { + "@nevware21/ts-utils": ">= 0.10.4 < 2.x" + } + }, + "node_modules/@nevware21/ts-async": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@nevware21/ts-async/-/ts-async-0.5.5.tgz", + "integrity": "sha512-vwqaL05iJPjLeh5igPi8MeeAu10i+Aq7xko1fbo9F5Si6MnVN5505qaV7AhSdk5MCBJVT/UYMk3kgInNjDb4Ig==", + "license": "MIT", + "dependencies": { + "@nevware21/ts-utils": ">= 0.12.2 < 2.x" + } + }, + "node_modules/@nevware21/ts-utils": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.12.5.tgz", + "integrity": "sha512-JPQZWPKQJjj7kAftdEZL0XDFfbMgXCGiUAZe0d7EhLC3QlXTlZdSckGqqRIQ2QNl0VTEZyZUvRBw6Ednw089Fw==", + "license": "MIT" }, "node_modules/@parcel/watcher": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", - "integrity": "sha512-dfUnCxiN9H4ap84DvD2ubjw+3vUNpstxa0TneY/Paat8a3R4uQZDLSvWjmznAY/DoahqTHl9V46HF/Zs3F29pg==", + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.4.tgz", + "integrity": "sha512-WYa2tUVV5HiArWPB3ydlOc4R2ivq0IDrlqhMi3l7mVsFEXNcTfxYFPIHXHXIh/ca/y/V5N4E1zecyxdIBjYnkQ==", "hasInstallScript": true, "license": "MIT", "dependencies": { - "detect-libc": "^1.0.3", + "detect-libc": "^2.0.3", "is-glob": "^4.0.3", - "micromatch": "^4.0.5", - "node-addon-api": "^7.0.0" + "node-addon-api": "^7.0.0", + "picomatch": "^4.0.3" }, "engines": { "node": ">= 10.0.0" @@ -108,25 +140,25 @@ "url": "https://opencollective.com/parcel" }, "optionalDependencies": { - "@parcel/watcher-android-arm64": "2.5.1", - "@parcel/watcher-darwin-arm64": "2.5.1", - "@parcel/watcher-darwin-x64": "2.5.1", - "@parcel/watcher-freebsd-x64": "2.5.1", - "@parcel/watcher-linux-arm-glibc": "2.5.1", - "@parcel/watcher-linux-arm-musl": "2.5.1", - "@parcel/watcher-linux-arm64-glibc": "2.5.1", - "@parcel/watcher-linux-arm64-musl": "2.5.1", - "@parcel/watcher-linux-x64-glibc": "2.5.1", - "@parcel/watcher-linux-x64-musl": "2.5.1", - "@parcel/watcher-win32-arm64": "2.5.1", - "@parcel/watcher-win32-ia32": "2.5.1", - "@parcel/watcher-win32-x64": "2.5.1" + "@parcel/watcher-android-arm64": "2.5.4", + "@parcel/watcher-darwin-arm64": "2.5.4", + "@parcel/watcher-darwin-x64": "2.5.4", + "@parcel/watcher-freebsd-x64": "2.5.4", + "@parcel/watcher-linux-arm-glibc": "2.5.4", + "@parcel/watcher-linux-arm-musl": "2.5.4", + "@parcel/watcher-linux-arm64-glibc": "2.5.4", + "@parcel/watcher-linux-arm64-musl": "2.5.4", + "@parcel/watcher-linux-x64-glibc": "2.5.4", + "@parcel/watcher-linux-x64-musl": "2.5.4", + "@parcel/watcher-win32-arm64": "2.5.4", + "@parcel/watcher-win32-ia32": "2.5.4", + "@parcel/watcher-win32-x64": "2.5.4" } }, "node_modules/@parcel/watcher-android-arm64": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.1.tgz", - "integrity": "sha512-KF8+j9nNbUN8vzOFDpRMsaKBHZ/mcjEjMToVMJOhTozkDonQFFrRcfdLWn6yWKCmJKmdVxSgHiYvTCef4/qcBA==", + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@parcel/watcher-android-arm64/-/watcher-android-arm64-2.5.4.tgz", + "integrity": "sha512-hoh0vx4v+b3BNI7Cjoy2/B0ARqcwVNrzN/n7DLq9ZB4I3lrsvhrkCViJyfTj/Qi5xM9YFiH4AmHGK6pgH1ss7g==", "cpu": [ "arm64" ], @@ -144,9 +176,9 @@ } }, "node_modules/@parcel/watcher-darwin-arm64": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.1.tgz", - "integrity": "sha512-eAzPv5osDmZyBhou8PoF4i6RQXAfeKL9tjb3QzYuccXFMQU0ruIc/POh30ePnaOyD1UXdlKguHBmsTs53tVoPw==", + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-arm64/-/watcher-darwin-arm64-2.5.4.tgz", + "integrity": "sha512-kphKy377pZiWpAOyTgQYPE5/XEKVMaj6VUjKT5VkNyUJlr2qZAn8gIc7CPzx+kbhvqHDT9d7EqdOqRXT6vk0zw==", "cpu": [ "arm64" ], @@ -164,9 +196,9 @@ } }, "node_modules/@parcel/watcher-darwin-x64": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.1.tgz", - "integrity": "sha512-1ZXDthrnNmwv10A0/3AJNZ9JGlzrF82i3gNQcWOzd7nJ8aj+ILyW1MTxVk35Db0u91oD5Nlk9MBiujMlwmeXZg==", + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@parcel/watcher-darwin-x64/-/watcher-darwin-x64-2.5.4.tgz", + "integrity": "sha512-UKaQFhCtNJW1A9YyVz3Ju7ydf6QgrpNQfRZ35wNKUhTQ3dxJ/3MULXN5JN/0Z80V/KUBDGa3RZaKq1EQT2a2gg==", "cpu": [ "x64" ], @@ -184,9 +216,9 @@ } }, "node_modules/@parcel/watcher-freebsd-x64": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.1.tgz", - "integrity": "sha512-SI4eljM7Flp9yPuKi8W0ird8TI/JK6CSxju3NojVI6BjHsTyK7zxA9urjVjEKJ5MBYC+bLmMcbAWlZ+rFkLpJQ==", + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@parcel/watcher-freebsd-x64/-/watcher-freebsd-x64-2.5.4.tgz", + "integrity": "sha512-Dib0Wv3Ow/m2/ttvLdeI2DBXloO7t3Z0oCp4bAb2aqyqOjKPPGrg10pMJJAQ7tt8P4V2rwYwywkDhUia/FgS+Q==", "cpu": [ "x64" ], @@ -204,9 +236,9 @@ } }, "node_modules/@parcel/watcher-linux-arm-glibc": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.1.tgz", - "integrity": "sha512-RCdZlEyTs8geyBkkcnPWvtXLY44BCeZKmGYRtSgtwwnHR4dxfHRG3gR99XdMEdQ7KeiDdasJwwvNSF5jKtDwdA==", + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-glibc/-/watcher-linux-arm-glibc-2.5.4.tgz", + "integrity": "sha512-I5Vb769pdf7Q7Sf4KNy8Pogl/URRCKu9ImMmnVKYayhynuyGYMzuI4UOWnegQNa2sGpsPSbzDsqbHNMyeyPCgw==", "cpu": [ "arm" ], @@ -224,9 +256,9 @@ } }, "node_modules/@parcel/watcher-linux-arm-musl": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.1.tgz", - "integrity": "sha512-6E+m/Mm1t1yhB8X412stiKFG3XykmgdIOqhjWj+VL8oHkKABfu/gjFj8DvLrYVHSBNC+/u5PeNrujiSQ1zwd1Q==", + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm-musl/-/watcher-linux-arm-musl-2.5.4.tgz", + "integrity": "sha512-kGO8RPvVrcAotV4QcWh8kZuHr9bXi9a3bSZw7kFarYR0+fGliU7hd/zevhjw8fnvIKG3J9EO5G6sXNGCSNMYPQ==", "cpu": [ "arm" ], @@ -244,9 +276,9 @@ } }, "node_modules/@parcel/watcher-linux-arm64-glibc": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.1.tgz", - "integrity": "sha512-LrGp+f02yU3BN9A+DGuY3v3bmnFUggAITBGriZHUREfNEzZh/GO06FF5u2kx8x+GBEUYfyTGamol4j3m9ANe8w==", + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-glibc/-/watcher-linux-arm64-glibc-2.5.4.tgz", + "integrity": "sha512-KU75aooXhqGFY2W5/p8DYYHt4hrjHZod8AhcGAmhzPn/etTa+lYCDB2b1sJy3sWJ8ahFVTdy+EbqSBvMx3iFlw==", "cpu": [ "arm64" ], @@ -264,9 +296,9 @@ } }, "node_modules/@parcel/watcher-linux-arm64-musl": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.1.tgz", - "integrity": "sha512-cFOjABi92pMYRXS7AcQv9/M1YuKRw8SZniCDw0ssQb/noPkRzA+HBDkwmyOJYp5wXcsTrhxO0zq1U11cK9jsFg==", + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-arm64-musl/-/watcher-linux-arm64-musl-2.5.4.tgz", + "integrity": "sha512-Qx8uNiIekVutnzbVdrgSanM+cbpDD3boB1f8vMtnuG5Zau4/bdDbXyKwIn0ToqFhIuob73bcxV9NwRm04/hzHQ==", "cpu": [ "arm64" ], @@ -284,9 +316,9 @@ } }, "node_modules/@parcel/watcher-linux-x64-glibc": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.1.tgz", - "integrity": "sha512-GcESn8NZySmfwlTsIur+49yDqSny2IhPeZfXunQi48DMugKeZ7uy1FX83pO0X22sHntJ4Ub+9k34XQCX+oHt2A==", + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-glibc/-/watcher-linux-x64-glibc-2.5.4.tgz", + "integrity": "sha512-UYBQvhYmgAv61LNUn24qGQdjtycFBKSK3EXr72DbJqX9aaLbtCOO8+1SkKhD/GNiJ97ExgcHBrukcYhVjrnogA==", "cpu": [ "x64" ], @@ -304,9 +336,9 @@ } }, "node_modules/@parcel/watcher-linux-x64-musl": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.1.tgz", - "integrity": "sha512-n0E2EQbatQ3bXhcH2D1XIAANAcTZkQICBPVaxMeaCVBtOpBZpWJuf7LwyWPSBDITb7In8mqQgJ7gH8CILCURXg==", + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@parcel/watcher-linux-x64-musl/-/watcher-linux-x64-musl-2.5.4.tgz", + "integrity": "sha512-YoRWCVgxv8akZrMhdyVi6/TyoeeMkQ0PGGOf2E4omODrvd1wxniXP+DBynKoHryStks7l+fDAMUBRzqNHrVOpg==", "cpu": [ "x64" ], @@ -324,9 +356,9 @@ } }, "node_modules/@parcel/watcher-win32-arm64": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.1.tgz", - "integrity": "sha512-RFzklRvmc3PkjKjry3hLF9wD7ppR4AKcWNzH7kXR7GUe0Igb3Nz8fyPwtZCSquGrhU5HhUNDr/mKBqj7tqA2Vw==", + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-arm64/-/watcher-win32-arm64-2.5.4.tgz", + "integrity": "sha512-iby+D/YNXWkiQNYcIhg8P5hSjzXEHaQrk2SLrWOUD7VeC4Ohu0WQvmV+HDJokZVJ2UjJ4AGXW3bx7Lls9Ln4TQ==", "cpu": [ "arm64" ], @@ -344,9 +376,9 @@ } }, "node_modules/@parcel/watcher-win32-ia32": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.1.tgz", - "integrity": "sha512-c2KkcVN+NJmuA7CGlaGD1qJh1cLfDnQsHjE89E60vUEMlqduHGCdCLJCID5geFVM0dOtA3ZiIO8BoEQmzQVfpQ==", + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-ia32/-/watcher-win32-ia32-2.5.4.tgz", + "integrity": "sha512-vQN+KIReG0a2ZDpVv8cgddlf67J8hk1WfZMMP7sMeZmJRSmEax5xNDNWKdgqSe2brOKTQQAs3aCCUal2qBHAyg==", "cpu": [ "ia32" ], @@ -364,9 +396,9 @@ } }, "node_modules/@parcel/watcher-win32-x64": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.1.tgz", - "integrity": "sha512-9lHBdJITeNR++EvSQVUcaZoWupyHfXe1jZvGZ06O/5MflPcuPLtEphScIBL+AiCWBO46tDSHzWyD0uDmmZqsgA==", + "version": "2.5.4", + "resolved": "https://registry.npmjs.org/@parcel/watcher-win32-x64/-/watcher-win32-x64-2.5.4.tgz", + "integrity": "sha512-3A6efb6BOKwyw7yk9ro2vus2YTt2nvcd56AuzxdMiVOxL9umDyN5PKkKfZ/gZ9row41SjVmTVQNWQhaRRGpOKw==", "cpu": [ "x64" ], @@ -383,18 +415,6 @@ "url": "https://opencollective.com/parcel" } }, - "node_modules/@parcel/watcher/node_modules/detect-libc": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-1.0.3.tgz", - "integrity": "sha512-pGjwhsmsp4kL2RTz08wcOlGN83otlqHeD/Z5T8GXZB+/YcpQ/dgo+lbU8ZsGxV0HIvqqxo9l7mqYwyYMD9bKDg==", - "license": "Apache-2.0", - "bin": { - "detect-libc": "bin/detect-libc.js" - }, - "engines": { - "node": ">=0.10" - } - }, "node_modules/@tootallnate/once": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-3.0.0.tgz", @@ -404,24 +424,26 @@ } }, "node_modules/@vscode/deviceid": { - "version": "0.1.1", - "resolved": "https://registry.npmjs.org/@vscode/deviceid/-/deviceid-0.1.1.tgz", - "integrity": "sha512-ErpoMeKKNYAkR1IT3zxB5RtiTqEECdh8fxggupWvzuxpTAX77hwOI2NdJ7um+vupnXRBZVx4ugo0+dVHJWUkag==", + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/@vscode/deviceid/-/deviceid-0.1.4.tgz", + "integrity": "sha512-3u705VptsQhKMcHvUMJzaOn9fBrKEQHsl7iibRRVQ8kUNV+cptki7bQXACPNsGtJ5Dh4/7A7W1uKtP3z39GUQg==", "hasInstallScript": true, + "license": "MIT", "dependencies": { "fs-extra": "^11.2.0", "uuid": "^9.0.1" } }, "node_modules/@vscode/iconv-lite-umd": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@vscode/iconv-lite-umd/-/iconv-lite-umd-0.7.0.tgz", - "integrity": "sha512-bRRFxLfg5dtAyl5XyiVWz/ZBPahpOpPrNYnnHpOpUZvam4tKH35wdhP4Kj6PbM0+KdliOsPzbGWpkxcdpNB/sg==" + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@vscode/iconv-lite-umd/-/iconv-lite-umd-0.7.1.tgz", + "integrity": "sha512-tK6k0DXFHW7q5+GGuGZO+phpAqpxO4WXl+BLc/8/uOk3RsM2ssAL3CQUQDb1TGfwltjsauhN6S4ghYZzs4sPFw==", + "license": "MIT" }, "node_modules/@vscode/proxy-agent": { - "version": "0.31.0", - "resolved": "https://registry.npmjs.org/@vscode/proxy-agent/-/proxy-agent-0.31.0.tgz", - "integrity": "sha512-HY6UaJ9mKsBxHGnbj9EKigH5vczvB+9wTa8csNqPIjE8i2XjinKcGTBa+ky2+Eb+LGQNvyV/v56+XF6P11xRaQ==", + "version": "0.37.0", + "resolved": "https://registry.npmjs.org/@vscode/proxy-agent/-/proxy-agent-0.37.0.tgz", + "integrity": "sha512-FDBc/3qf7fLMp4fmdRBav2dy3UZ/Vao4PN6a5IeTYvcgh9erd9HfOcVoU3ogy2uwCii6vZNvmEeF9+gr64spVQ==", "license": "MIT", "dependencies": { "@tootallnate/once": "^3.0.0", @@ -432,14 +454,17 @@ "socks-proxy-agent": "^8.0.1", "undici": "^7.2.0" }, + "engines": { + "node": ">=22.15.0" + }, "optionalDependencies": { "@vscode/windows-ca-certs": "^0.3.1" } }, "node_modules/@vscode/ripgrep": { - "version": "1.15.10", - "resolved": "https://registry.npmjs.org/@vscode/ripgrep/-/ripgrep-1.15.10.tgz", - "integrity": "sha512-83Q6qFrELpFgf88bPOcwSWDegfY2r/cb6bIfdLTSZvN73Dg1wviSfO+1v6lTFMd0mAvUYYcTUu+Mn5xMroZMxA==", + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/@vscode/ripgrep/-/ripgrep-1.17.0.tgz", + "integrity": "sha512-mBRKm+ASPkUcw4o9aAgfbusIu6H4Sdhw09bjeP1YOBFTJEZAnrnk6WZwzv8NEjgC82f7ILvhmb1WIElSugea6g==", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -458,9 +483,9 @@ } }, "node_modules/@vscode/spdlog": { - "version": "0.15.1", - "resolved": "https://registry.npmjs.org/@vscode/spdlog/-/spdlog-0.15.1.tgz", - "integrity": "sha512-tVPeXmyz3/4NKqtNfNQxqcrBSSEZVIbF4lVDuDh9Nik5xhuHVceCU6cTpwmJ6yVBs+jv51SGF622txOQv95sZA==", + "version": "0.15.7", + "resolved": "https://registry.npmjs.org/@vscode/spdlog/-/spdlog-0.15.7.tgz", + "integrity": "sha512-xpHAtw0IESD6wmjqLr6LbpYAmr8ZYm8AT7hGE7oM7AojNeOBngXLOqmzpXbTNTAvXBq1KHy8PwbMmY24uYR/oQ==", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -470,15 +495,16 @@ } }, "node_modules/@vscode/tree-sitter-wasm": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@vscode/tree-sitter-wasm/-/tree-sitter-wasm-0.1.2.tgz", - "integrity": "sha512-nXFrHey/TdhN2v15Qurs+trTSQB2ad0O8I8R7RMAjvrZi9qXAE+H+l9ZHHsA14fjZxKIyGg+nFmNpdEAuQALpw==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@vscode/tree-sitter-wasm/-/tree-sitter-wasm-0.3.0.tgz", + "integrity": "sha512-4kjB1jgLyG9VimGfyJb1F8/GFdrx55atsBCH/9r2D/iZHAUDCvZ5zhWXB7sRQ2z2WkkuNYm/0pgQtUm1jhdf7A==", "license": "MIT" }, "node_modules/@vscode/vscode-languagedetection": { - "version": "1.0.21", - "resolved": "https://registry.npmjs.org/@vscode/vscode-languagedetection/-/vscode-languagedetection-1.0.21.tgz", - "integrity": "sha512-zSUH9HYCw5qsCtd7b31yqkpaCU6jhtkKLkvOOA8yTrIRfBSOFb8PPhgmMicD7B/m+t4PwOJXzU1XDtrM9Fd3/g==", + "version": "1.0.23", + "resolved": "https://registry.npmjs.org/@vscode/vscode-languagedetection/-/vscode-languagedetection-1.0.23.tgz", + "integrity": "sha512-Ywk6vXC81nUHvc9WX3uFJG/UHFLXDYJgNeUVirBLJGvmchXHdapsGeAYclNqO1thQLmykmJhSouIZV+JJS8o1A==", + "license": "MIT", "bin": { "vscode-languagedetection": "cli/index.js" } @@ -508,121 +534,127 @@ } }, "node_modules/@vscode/windows-process-tree": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/@vscode/windows-process-tree/-/windows-process-tree-0.6.0.tgz", - "integrity": "sha512-7/DjBKKUtlmKNiAet2GRbdvfjgMKmfBeWVClIgONv8aqxGnaKca5N85eIDxh6rLMy2hKvFqIIsqgxs1Q26TWwg==", + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/@vscode/windows-process-tree/-/windows-process-tree-0.6.3.tgz", + "integrity": "sha512-mjirLbtgjv7P6fwD8gx7iaY961EfGqUExGvfzsKl3spLfScg57ejlMi+7O1jfJqpM2Zly9DTSxyY4cFsDN6c9Q==", "hasInstallScript": true, + "license": "MIT", "dependencies": { "node-addon-api": "7.1.0" } }, "node_modules/@vscode/windows-registry": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@vscode/windows-registry/-/windows-registry-1.1.0.tgz", - "integrity": "sha512-5AZzuWJpGscyiMOed0IuyEwt6iKmV5Us7zuwCDCFYMIq7tsvooO9BUiciywsvuthGz6UG4LSpeDeCxvgMVhnIw==", - "hasInstallScript": true + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/@vscode/windows-registry/-/windows-registry-1.1.3.tgz", + "integrity": "sha512-si8+b+2Wh0x2X6W2+kgDyLJD9hyGIrjUo1X/7RWlvsxyI5+Pg+bpdHJrVYtIW4cHOPVB0FYFaN1UZndbUbU5lQ==", + "hasInstallScript": true, + "license": "MIT" }, "node_modules/@xterm/addon-clipboard": { - "version": "0.2.0-beta.81", - "resolved": "https://registry.npmjs.org/@xterm/addon-clipboard/-/addon-clipboard-0.2.0-beta.81.tgz", - "integrity": "sha512-vDxRyBO9VHzsl+gUQsDlUM9o2ZxSJKzE2eYQtuILKcf5D0EXYI86aMwKT/1iguX41vcMg42WXQBQ0TLWZ2MyTQ==", + "version": "0.3.0-beta.107", + "resolved": "https://registry.npmjs.org/@xterm/addon-clipboard/-/addon-clipboard-0.3.0-beta.107.tgz", + "integrity": "sha512-sSdnJUqaY9xFnJCwAefXEZ7rQOODtY6YtdX1LFXqClBMbw/WMLfuvUeRjnzQeWKUAgU+QUVlIPTT059H+0ETFQ==", "license": "MIT", "dependencies": { "js-base64": "^3.7.5" }, "peerDependencies": { - "@xterm/xterm": "^5.6.0-beta.98" + "@xterm/xterm": "^6.1.0-beta.107" } }, "node_modules/@xterm/addon-image": { - "version": "0.9.0-beta.98", - "resolved": "https://registry.npmjs.org/@xterm/addon-image/-/addon-image-0.9.0-beta.98.tgz", - "integrity": "sha512-yJaezwUc1Y3QYCmYSpjFW9IzMTLPSqrRCgdPnQ0JbCAVASRVmH6DLRfeikheJCvoXW6VUwMmGkb3fSplTxiV1w==", + "version": "0.10.0-beta.107", + "resolved": "https://registry.npmjs.org/@xterm/addon-image/-/addon-image-0.10.0-beta.107.tgz", + "integrity": "sha512-scYxbEmtZUeJHV6nz1l4DYtgYkQLUnE61mwBSZPSjy0cmYoKA8uUAaxucoJIBRtYPuA9RtotBueyatfp/HEy5Q==", "license": "MIT", "peerDependencies": { - "@xterm/xterm": "^5.6.0-beta.98" + "@xterm/xterm": "^6.1.0-beta.107" } }, "node_modules/@xterm/addon-ligatures": { - "version": "0.10.0-beta.98", - "resolved": "https://registry.npmjs.org/@xterm/addon-ligatures/-/addon-ligatures-0.10.0-beta.98.tgz", - "integrity": "sha512-1zYeS9OUBR2ThG7dsxsGKOqeSlUo+DNTd5aaV5ZFbKQsQ1w+sQCaL73ZrXp1kuVK7pBiP5NCo4ffcXfzcztztA==", + "version": "0.11.0-beta.107", + "resolved": "https://registry.npmjs.org/@xterm/addon-ligatures/-/addon-ligatures-0.11.0-beta.107.tgz", + "integrity": "sha512-M+sf5/SvLFRo7LifIzF1hRCtSHr0aL/g1dQGYPpz5ceuiVgGvPJhRM/N9QHFIS9axPx4RCfEGl5wcSp+CTWEnA==", "license": "MIT", "dependencies": { - "font-finder": "^1.1.0", - "font-ligatures": "^1.4.1" + "lru-cache": "^6.0.0", + "opentype.js": "^0.8.0" }, "engines": { "node": ">8.0.0" }, "peerDependencies": { - "@xterm/xterm": "^5.6.0-beta.98" + "@xterm/xterm": "^6.1.0-beta.107" } }, "node_modules/@xterm/addon-progress": { - "version": "0.2.0-beta.4", - "resolved": "https://registry.npmjs.org/@xterm/addon-progress/-/addon-progress-0.2.0-beta.4.tgz", - "integrity": "sha512-7t1nlaANdEjUBVvuTTs5gw6UQgB+unFLwSGGnYXIvdQroYdkXQXSSATSqpYKVCd/6QFhBerzdB2VwPM5L5lxIw==", + "version": "0.3.0-beta.107", + "resolved": "https://registry.npmjs.org/@xterm/addon-progress/-/addon-progress-0.3.0-beta.107.tgz", + "integrity": "sha512-8SB5U6DXrGQPIBfh8uGW+06JayU4ECsMKcUmxZj9GESy1T/7o1H5UsTZwl5+ZtczxmOCN7ydrowb1XGWXS0+Kg==", "license": "MIT", "peerDependencies": { - "@xterm/xterm": "^5.6.0-beta.98" + "@xterm/xterm": "^6.1.0-beta.107" } }, "node_modules/@xterm/addon-search": { - "version": "0.16.0-beta.98", - "resolved": "https://registry.npmjs.org/@xterm/addon-search/-/addon-search-0.16.0-beta.98.tgz", - "integrity": "sha512-7gtx7eYvwFLxlb5q2IKxa7jG1KEinVwTlT3ijnSsPawwn7rGi6nR135rGiR8DAjEV0GUO406ICeoYyVbgiFNwQ==", + "version": "0.17.0-beta.107", + "resolved": "https://registry.npmjs.org/@xterm/addon-search/-/addon-search-0.17.0-beta.107.tgz", + "integrity": "sha512-8hbPJABGkNvzYdHFOMwIUOW09PVeJb2yQj+b6LZtc4f1XyOOViXckJAT75aVfAXBdTWcW0241y5rlWz22GK1mA==", "license": "MIT", "peerDependencies": { - "@xterm/xterm": "^5.6.0-beta.98" + "@xterm/xterm": "^6.1.0-beta.107" } }, "node_modules/@xterm/addon-serialize": { - "version": "0.14.0-beta.98", - "resolved": "https://registry.npmjs.org/@xterm/addon-serialize/-/addon-serialize-0.14.0-beta.98.tgz", - "integrity": "sha512-Tsr8j3wnun2raYR1DgsNClQP/I5a85u/uW/5EiYH+/iPPua6EWJvPlr5Q6TCU/cdIKW1o27Z3L5/mw0pfMUXrQ==", + "version": "0.15.0-beta.107", + "resolved": "https://registry.npmjs.org/@xterm/addon-serialize/-/addon-serialize-0.15.0-beta.107.tgz", + "integrity": "sha512-ONNGwlonwK7mTriI3s8tc7VGSELAA7P42Z5rSbup7XF5z2MX2vNJCxrqnp2PjjAKxG++5r3qug+v1e/M8b6PjQ==", "license": "MIT", "peerDependencies": { - "@xterm/xterm": "^5.6.0-beta.98" + "@xterm/xterm": "^6.1.0-beta.107" } }, "node_modules/@xterm/addon-unicode11": { - "version": "0.9.0-beta.98", - "resolved": "https://registry.npmjs.org/@xterm/addon-unicode11/-/addon-unicode11-0.9.0-beta.98.tgz", - "integrity": "sha512-3GjjEYAPWAEGE1CaTFDjQxKcY5NiHHPmeufTsRp3IL5850ZiaMMq9bIL2WSdoFIbVgfbxh5mRy2cJPdu9m0uRQ==", + "version": "0.10.0-beta.107", + "resolved": "https://registry.npmjs.org/@xterm/addon-unicode11/-/addon-unicode11-0.10.0-beta.107.tgz", + "integrity": "sha512-aWnjjJGGAOUxuWRl+kF7EyD2qQ3TqRdb0u0L1aB1MlUk04qG5Ctv3uwLu8Moh/uAyJ7tAqgU7GQQP6yWDwvnJQ==", "license": "MIT", "peerDependencies": { - "@xterm/xterm": "^5.6.0-beta.98" + "@xterm/xterm": "^6.1.0-beta.107" } }, "node_modules/@xterm/addon-webgl": { - "version": "0.19.0-beta.98", - "resolved": "https://registry.npmjs.org/@xterm/addon-webgl/-/addon-webgl-0.19.0-beta.98.tgz", - "integrity": "sha512-09FbNHgN2ad/8JI+AEyg8C3msyk04ET1FihQIpTeWPfd2LJIAdps7G4St2+qzZbhlFkR6m9Dgrgh/AC2uegh8A==", + "version": "0.20.0-beta.106", + "resolved": "https://registry.npmjs.org/@xterm/addon-webgl/-/addon-webgl-0.20.0-beta.106.tgz", + "integrity": "sha512-QbVWbYmCk5w4W7k6P7Q5uaiOn4FOqxdvNFtAvFyO4zLtH9t22cEfihooIqmZzksxWafPIe9US55o9gu1DHUzLQ==", "license": "MIT", "peerDependencies": { - "@xterm/xterm": "^5.6.0-beta.98" + "@xterm/xterm": "^6.1.0-beta.107" } }, "node_modules/@xterm/headless": { - "version": "5.6.0-beta.98", - "resolved": "https://registry.npmjs.org/@xterm/headless/-/headless-5.6.0-beta.98.tgz", - "integrity": "sha512-nRl5NyNHajvxNy1N/h0+vEO9tgBgYU8kN/SApBzezGYjcKhs81MYVr5uO1uMKb2eq5eTXBW8BtcyD3KqGoqkEA==", - "license": "MIT" + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@xterm/headless/-/headless-6.0.0.tgz", + "integrity": "sha512-5Yj1QINYCyzrZtf8OFIHi47iQtI+0qYFPHmouEfG8dHNxbZ9Tb9YGSuLcsEwj9Z+OL75GJqPyJbyoFer80a2Hw==", + "license": "MIT", + "workspaces": [ + "addons/*" + ] }, "node_modules/@xterm/xterm": { - "version": "5.6.0-beta.98", - "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.6.0-beta.98.tgz", - "integrity": "sha512-fJexj3XKDAMGsR8KKaiEhGrtJD1eRANbT3094E3KSgvbHRa3524tSFvDCx5+5KRE/hYaECmi0knAUIWJCvSPTg==", - "license": "MIT" + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-6.0.0.tgz", + "integrity": "sha512-TQwDdQGtwwDt+2cgKDLn0IRaSxYu1tSUjgKarSDkUM0ZNiSRXFpjxEsvc/Zgc5kq5omJ+V0a8/kIM2WD3sMOYg==", + "license": "MIT", + "workspaces": [ + "addons/*" + ] }, "node_modules/agent-base": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.1.tgz", - "integrity": "sha512-H0TSyFNDMomMNJQBn8wFV5YC/2eJ+VXECwOadZJT554xP6cODZHPX3H9QMQECxvrgiSOP1pHjy1sMWQVYJOUOA==", - "dependencies": { - "debug": "^4.3.4" - }, + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "license": "MIT", "engines": { "node": ">= 14" } @@ -644,7 +676,8 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/bindings": { "version": "1.5.0", @@ -658,23 +691,13 @@ "version": "4.1.0", "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "license": "MIT", "dependencies": { "buffer": "^5.5.0", "inherits": "^2.0.4", "readable-stream": "^3.4.0" } }, - "node_modules/braces": { - "version": "3.0.3", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", - "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", - "dependencies": { - "fill-range": "^7.1.1" - }, - "engines": { - "node": ">=8" - } - }, "node_modules/buffer": { "version": "5.7.1", "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", @@ -693,6 +716,7 @@ "url": "https://feross.org/support" } ], + "license": "MIT", "dependencies": { "base64-js": "^1.3.1", "ieee754": "^1.1.13" @@ -709,15 +733,20 @@ "node_modules/chownr": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", - "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "license": "ISC" }, "node_modules/cookie": { - "version": "0.7.2", - "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.7.2.tgz", - "integrity": "sha512-yki5XnKuf750l50uGTllt6kKILY4nQ1eNIQatoXEByZ5dWgnKqbnqmTrBE5B4N7lrMJKQ2ytWMiTO2o0v6Ew/w==", + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-1.1.1.tgz", + "integrity": "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ==", "license": "MIT", "engines": { - "node": ">= 0.6" + "node": ">=18" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/express" } }, "node_modules/debug": { @@ -740,6 +769,7 @@ "version": "6.0.0", "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "license": "MIT", "dependencies": { "mimic-response": "^3.1.0" }, @@ -754,22 +784,25 @@ "version": "0.6.0", "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "license": "MIT", "engines": { "node": ">=4.0.0" } }, "node_modules/detect-libc": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.0.1.tgz", - "integrity": "sha512-463v3ZeIrcWtdgIg6vI6XUncguvr2TnGl4SzDXinkt9mSLpBJKXT3mW6xT3VQdDN11+WVs29pgvivTc4Lp8v+w==", + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "license": "Apache-2.0", "engines": { "node": ">=8" } }, "node_modules/end-of-stream": { - "version": "1.4.4", - "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", - "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "license": "MIT", "dependencies": { "once": "^1.4.0" } @@ -778,6 +811,7 @@ "version": "2.0.3", "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "license": "(MIT OR WTFPL)", "engines": { "node": ">=6" } @@ -795,48 +829,11 @@ "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" }, - "node_modules/fill-range": { - "version": "7.1.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", - "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", - "dependencies": { - "to-regex-range": "^5.0.1" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/font-finder": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/font-finder/-/font-finder-1.1.0.tgz", - "integrity": "sha512-wpCL2uIbi6GurJbU7ZlQ3nGd61Ho+dSU6U83/xJT5UPFfN35EeCW/rOtS+5k+IuEZu2SYmHzDIPL9eA5tSYRAw==", - "license": "MIT", - "dependencies": { - "get-system-fonts": "^2.0.0", - "promise-stream-reader": "^1.0.1" - }, - "engines": { - "node": ">8.0.0" - } - }, - "node_modules/font-ligatures": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/font-ligatures/-/font-ligatures-1.4.1.tgz", - "integrity": "sha512-7W6zlfyhvCqShZ5ReUWqmSd9vBaUudW0Hxis+tqUjtHhsPU+L3Grf8mcZAtCiXHTzorhwdRTId2WeH/88gdFkw==", - "license": "MIT", - "dependencies": { - "font-finder": "^1.0.3", - "lru-cache": "^6.0.0", - "opentype.js": "^0.8.0" - }, - "engines": { - "node": ">8.0.0" - } - }, "node_modules/fs-constants": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", - "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "license": "MIT" }, "node_modules/fs-extra": { "version": "11.2.0", @@ -851,19 +848,11 @@ "node": ">=14.14" } }, - "node_modules/get-system-fonts": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/get-system-fonts/-/get-system-fonts-2.0.2.tgz", - "integrity": "sha512-zzlgaYnHMIEgHRrfC7x0Qp0Ylhw/sHpM6MHXeVBTYIsvGf5GpbnClB+Q6rAPdn+0gd2oZZIo6Tj3EaWrt4VhDQ==", - "license": "MIT", - "engines": { - "node": ">8.0.0" - } - }, "node_modules/github-from-package": { "version": "0.0.0", "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", - "integrity": "sha1-l/tdlr/eiXMxPyDoKI75oWf6ZM4= sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==" + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "license": "MIT" }, "node_modules/graceful-fs": { "version": "4.2.11", @@ -871,9 +860,10 @@ "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==" }, "node_modules/http-proxy-agent": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.0.tgz", - "integrity": "sha512-+ZT+iBxVUQ1asugqnD6oWoRiS25AkjNfG085dKJGtGxkdwLQrMKU5wJr2bOOFAXzKcTuqq+7fZlTMgG3SRfIYQ==", + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "license": "MIT", "dependencies": { "agent-base": "^7.1.0", "debug": "^4.3.4" @@ -883,11 +873,12 @@ } }, "node_modules/https-proxy-agent": { - "version": "7.0.2", - "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.2.tgz", - "integrity": "sha512-NmLNjm6ucYwtcUmL7JQC1ZQ57LmHP4lT15FQ8D61nak1rO6DH+fz5qNK2Ap5UN4ZapYICE3/0KodcLYSPsPbaA==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "license": "MIT", "dependencies": { - "agent-base": "^7.0.2", + "agent-base": "^7.1.2", "debug": "4" }, "engines": { @@ -911,17 +902,20 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "BSD-3-Clause" }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", - "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "license": "ISC" }, "node_modules/ini": { "version": "1.3.8", "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", - "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==" + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "license": "ISC" }, "node_modules/ip-address": { "version": "9.0.5", @@ -954,14 +948,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-number": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", - "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "engines": { - "node": ">=0.12.0" - } - }, "node_modules/js-base64": { "version": "3.7.7", "resolved": "https://registry.npmjs.org/js-base64/-/js-base64-3.7.7.tgz", @@ -993,23 +979,33 @@ } }, "node_modules/kerberos": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/kerberos/-/kerberos-2.1.1.tgz", - "integrity": "sha512-414s1G/qgK2T60cXnZsHbtRj8Ynjg0DBlQWeY99tkyqQ2e8vGgFHvxRdvjTlLHg/SxBA0zLQcGE6Pk6Dfq/BCA==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/kerberos/-/kerberos-7.0.0.tgz", + "integrity": "sha512-Q8yUNeCM5fSXkURaa05WugXFsH6c57hDHDmsupMFCPaQEPym9FGwFp/2XSTcMuLldtEeBOsQ/9VGQ55lfHTT3Q==", "hasInstallScript": true, + "license": "Apache-2.0", "dependencies": { - "bindings": "^1.5.0", - "node-addon-api": "^6.1.0", - "prebuild-install": "^7.1.2" + "node-addon-api": "^8.5.0", + "prebuild-install": "^7.1.3" }, "engines": { - "node": ">=12.9.0" + "node": ">=20.19.0" + } + }, + "node_modules/kerberos/node_modules/node-addon-api": { + "version": "8.5.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-8.5.0.tgz", + "integrity": "sha512-/bRZty2mXUIFY/xU5HLvveNHlswNJej+RnxBjOMkidWfwZzgTbPG1E3K5TOxRLOR+5hX7bSofy8yf1hZevMS8A==", + "license": "MIT", + "engines": { + "node": "^18 || ^20 || >= 21" } }, "node_modules/lru-cache": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, @@ -1017,22 +1013,11 @@ "node": ">=10" } }, - "node_modules/micromatch": { - "version": "4.0.8", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", - "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", - "dependencies": { - "braces": "^3.0.3", - "picomatch": "^2.3.1" - }, - "engines": { - "node": ">=8.6" - } - }, "node_modules/mimic-response": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "license": "MIT", "engines": { "node": ">=10" }, @@ -1041,9 +1026,13 @@ } }, "node_modules/minimist": { - "version": "1.2.6", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.6.tgz", - "integrity": "sha512-Jsjnk4bw3YJqYzbdyBiNsPWHPfO++UGG749Cxs6peCu5Xg4nrena6OVxOYxrQTqww0Jmwt+Ref8rggumkTLz9Q==" + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } }, "node_modules/mkdirp": { "version": "1.0.4", @@ -1059,7 +1048,8 @@ "node_modules/mkdirp-classic": { "version": "0.5.3", "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", - "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==" + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "license": "MIT" }, "node_modules/ms": { "version": "2.1.2", @@ -1067,9 +1057,10 @@ "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==" }, "node_modules/napi-build-utils": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-1.0.2.tgz", - "integrity": "sha512-ONmRUqK7zj7DWX0D9ADe03wbwOBZxNAfF20PlGfCWQcD3+/MakShIHrMqx9YwPTfxDdF1zLeL+RGZiR9kGMLdg==" + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "license": "MIT" }, "node_modules/native-watchdog": { "version": "1.4.2", @@ -1078,9 +1069,10 @@ "hasInstallScript": true }, "node_modules/node-abi": { - "version": "3.8.0", - "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.8.0.tgz", - "integrity": "sha512-tzua9qWWi7iW4I42vUPKM+SfaF0vQSLAm4yO5J83mSwB7GeoWrDKC/K+8YCnYNwqP5duwazbw2X9l4m8SC2cUw==", + "version": "3.85.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.85.0.tgz", + "integrity": "sha512-zsFhmbkAzwhTft6nd3VxcG0cvJsT70rL+BIGHWVq5fi6MwGrHwzqKaxXE+Hl2GmnGItnDKPPkO5/LQqjVkIdFg==", + "license": "MIT", "dependencies": { "semver": "^7.3.5" }, @@ -1098,9 +1090,9 @@ } }, "node_modules/node-pty": { - "version": "1.1.0-beta30", - "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.1.0-beta30.tgz", - "integrity": "sha512-cmNYVWfbf961aOqnxIFXssvw6Fp6/78BQBNlwYRWUHBenJjUhCJ1wMZpJy+SegoLC07P9D6HTtq39Kd89rpv/w==", + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/node-pty/-/node-pty-1.1.0.tgz", + "integrity": "sha512-20JqtutY6JPXTUnL0ij1uad7Qe1baT46lyolh2sSENDd4sTzKZ4nmAFkeAARDKwmlLjPx6XKRlwRUxwjOy+lUg==", "hasInstallScript": true, "license": "MIT", "dependencies": { @@ -1110,7 +1102,8 @@ "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E= sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "license": "ISC", "dependencies": { "wrappy": "1" } @@ -1133,27 +1126,29 @@ "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA= sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==" }, "node_modules/picomatch": { - "version": "2.3.1", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", - "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", + "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", + "license": "MIT", "engines": { - "node": ">=8.6" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" } }, "node_modules/prebuild-install": { - "version": "7.1.2", - "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.2.tgz", - "integrity": "sha512-UnNke3IQb6sgarcZIDU3gbMeTp/9SSU1DAIkil7PrqG1vZlBtY5msYccSKSHDqa3hNg436IXK+SNImReuA1wEQ==", + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "license": "MIT", "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", + "napi-build-utils": "^2.0.0", "node-abi": "^3.3.0", "pump": "^3.0.0", "rc": "^1.2.7", @@ -1168,24 +1163,16 @@ "node": ">=10" } }, - "node_modules/promise-stream-reader": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-stream-reader/-/promise-stream-reader-1.0.1.tgz", - "integrity": "sha512-Tnxit5trUjBAqqZCGWwjyxhmgMN4hGrtpW3Oc/tRI4bpm/O2+ej72BB08l6JBnGQgVDGCLvHFGjGgQS6vzhwXg==", - "license": "MIT", - "engines": { - "node": ">8.0.0" - } - }, "node_modules/proxy-from-env": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/proxy-from-env/-/proxy-from-env-1.1.0.tgz", "integrity": "sha512-D+zkORCbA9f1tdWRK0RaCR3GPv50cMxcrz4X8k5LTSUD1Dkw47mKJEZQNunItRTkWwgtaUSo1RVFRIG9ZXiFYg==" }, "node_modules/pump": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", - "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.3.tgz", + "integrity": "sha512-todwxLMY7/heScKmntwQG8CXVkWUOdYxIvY2s0VWAAMh/nd8SoYiRaKjlr7+iCs984f2P8zvrfWcDDYVb73NfA==", + "license": "MIT", "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" @@ -1195,6 +1182,7 @@ "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", "dependencies": { "deep-extend": "^0.6.0", "ini": "~1.3.0", @@ -1206,9 +1194,10 @@ } }, "node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -1235,15 +1224,14 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/semver": { - "version": "7.5.4", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.5.4.tgz", - "integrity": "sha512-1bCSESV6Pv+i21Hvpxp3Dx+pSD8lIPt8uVjRrxAUt/nbswYc+tK6Y2btiULjd4+fnq15PX+nqQDC7Oft7WkwcA==", - "dependencies": { - "lru-cache": "^6.0.0" - }, + "version": "7.7.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.3.tgz", + "integrity": "sha512-SdsKMrI9TdgjdweUSR9MweHA4EJ8YxHn8DFaDisvhVlUOe4BF1tLD7GAj0lIqWVl+dPb/rExr0Btby5loQm20Q==", + "license": "ISC", "bin": { "semver": "bin/semver.js" }, @@ -1268,7 +1256,8 @@ "type": "consulting", "url": "https://feross.org/support" } - ] + ], + "license": "MIT" }, "node_modules/simple-get": { "version": "4.0.1", @@ -1288,6 +1277,7 @@ "url": "https://feross.org/support" } ], + "license": "MIT", "dependencies": { "decompress-response": "^6.0.0", "once": "^1.3.1", @@ -1338,6 +1328,7 @@ "version": "1.3.0", "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "license": "MIT", "dependencies": { "safe-buffer": "~5.2.0" } @@ -1345,15 +1336,17 @@ "node_modules/strip-json-comments": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", - "integrity": "sha1-PFMZQukIwml8DsNEhYwobHygpgo= sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/tar-fs": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.1.tgz", - "integrity": "sha512-V0r2Y9scmbDRLCNex/+hYzvp/zyYjvFbHPNgVTKfQvVrb6guiE/fxP+XblDNR011utopbkex2nM4dHNV6GDsng==", + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", + "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", + "license": "MIT", "dependencies": { "chownr": "^1.1.1", "mkdirp-classic": "^0.5.2", @@ -1365,6 +1358,7 @@ "version": "2.2.0", "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "license": "MIT", "dependencies": { "bl": "^4.0.3", "end-of-stream": "^1.4.1", @@ -1387,21 +1381,11 @@ "integrity": "sha512-pkY1fj1cKHb2seWDy0B16HeWyczlJA9/WW3u3c4z/NiWDsO3DOU5D7nhTLE9CF0yXv/QZFY7sEJmj24dK+Rrqw==", "license": "MIT" }, - "node_modules/to-regex-range": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", - "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", - "dependencies": { - "is-number": "^7.0.0" - }, - "engines": { - "node": ">=8.0" - } - }, "node_modules/tunnel-agent": { "version": "0.6.0", "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", - "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0= sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "license": "Apache-2.0", "dependencies": { "safe-buffer": "^5.0.1" }, @@ -1410,9 +1394,9 @@ } }, "node_modules/undici": { - "version": "7.3.0", - "resolved": "https://registry.npmjs.org/undici/-/undici-7.3.0.tgz", - "integrity": "sha512-Qy96NND4Dou5jKoSJ2gm8ax8AJM/Ey9o9mz7KN1bb9GP+G0l20Zw8afxTnY2f4b7hmhn/z8aC2kfArVQlAhFBw==", + "version": "7.18.2", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.18.2.tgz", + "integrity": "sha512-y+8YjDFzWdQlSE9N5nzKMT3g4a5UBX1HKowfdXh0uvAnTaqqwqB92Jt4UXBAeKekDs5IaDKyJFR4X1gYVCgXcw==", "license": "MIT", "engines": { "node": ">=20.18.1" @@ -1429,7 +1413,8 @@ "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", - "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8= sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "license": "MIT" }, "node_modules/uuid": { "version": "9.0.1", @@ -1444,9 +1429,10 @@ } }, "node_modules/vscode-oniguruma": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", - "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-2.0.1.tgz", + "integrity": "sha512-poJU8iHIWnC3vgphJnrLZyI3YdqRlR27xzqDmpPXYzA93R4Gk8z7T6oqDzDoHjoikA2aS82crdXFkjELCdJsjQ==", + "license": "MIT" }, "node_modules/vscode-regexpp": { "version": "3.1.0", @@ -1460,25 +1446,28 @@ } }, "node_modules/vscode-textmate": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-9.2.0.tgz", - "integrity": "sha512-rkvG4SraZQaPSN/5XjwKswdU0OP9MF28QjrYzUBbhb8QyG3ljB1Ky996m++jiI7KdiAP2CkBiQZd9pqEDTClqA==", + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-9.3.2.tgz", + "integrity": "sha512-n2uGbUcrjhUEBH16uGA0TvUfhWwliFZ1e3+pTjrkim1Mt7ydB41lV08aUvsi70OlzDWp6X7Bx3w/x3fAXIsN0Q==", "license": "MIT" }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8= sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==" + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "license": "ISC" }, "node_modules/yallist": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "license": "ISC" }, "node_modules/yauzl": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.1.1.tgz", - "integrity": "sha512-MPxA7oN5cvGV0wzfkeHKF2/+Q4TkMpHSWGRy/96I4Cozljmx0ph91+Muxh6HegEtDC4GftJ8qYDE51vghFiEYA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.2.0.tgz", + "integrity": "sha512-Ow9nuGZE+qp1u4JIPvg+uCiUr7xGQWdff7JQSk5VGYTAZMDe2q8lxJ10ygv10qmSj031Ty/6FNJpLO4o1Sgc+w==", + "license": "MIT", "dependencies": { "buffer-crc32": "~0.2.3", "pend": "~1.2.0" @@ -1488,11 +1477,21 @@ } }, "node_modules/yazl": { - "version": "2.4.3", - "resolved": "https://registry.npmjs.org/yazl/-/yazl-2.4.3.tgz", - "integrity": "sha1-7CblzIfVYBud+EMtvdPNLlFzoHE= sha512-cIUrm3/81iF/BzuyORI7ppz1vGHAhA62JYzAFFC+rwJ2jQF1LcYxY9UXx4XyUXojkCnol0SvPuc+Toc7FO4W8g==", + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/yazl/-/yazl-3.3.1.tgz", + "integrity": "sha512-BbETDVWG+VcMUle37k5Fqp//7SDOK2/1+T7X8TD96M3D9G8jK5VLUdQVdVjGi8im7FGkazX7kk5hkU8X4L5Bng==", + "license": "MIT", "dependencies": { - "buffer-crc32": "~0.2.3" + "buffer-crc32": "^1.0.0" + } + }, + "node_modules/yazl/node_modules/buffer-crc32": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-1.0.0.tgz", + "integrity": "sha512-Db1SbgBS/fg/392AblrMJk97KggmvYhr4pB5ZIMTWtaivCPMWLkmb7m21cJvpvgK+J3nsU2CmmixNBZx4vFj/w==", + "license": "MIT", + "engines": { + "node": ">=8.0.0" } } } diff --git a/remote/package.json b/remote/package.json index 0733c0bb042d..24b905292cfd 100644 --- a/remote/package.json +++ b/remote/package.json @@ -3,42 +3,42 @@ "version": "0.0.0", "private": true, "dependencies": { - "@microsoft/1ds-core-js": "^3.2.13", - "@microsoft/1ds-post-js": "^3.2.13", - "@parcel/watcher": "2.5.1", + "@microsoft/1ds-core-js": "^4.3.11", + "@microsoft/1ds-post-js": "^4.3.11", + "@parcel/watcher": "2.5.4", "@vscode/deviceid": "^0.1.1", - "@vscode/iconv-lite-umd": "0.7.0", - "@vscode/proxy-agent": "^0.31.0", - "@vscode/ripgrep": "^1.15.10", - "@vscode/spdlog": "^0.15.0", - "@vscode/tree-sitter-wasm": "^0.1.2", - "@vscode/vscode-languagedetection": "1.0.21", + "@vscode/iconv-lite-umd": "0.7.1", + "@vscode/proxy-agent": "^0.37.0", + "@vscode/ripgrep": "^1.15.11", + "@vscode/spdlog": "^0.15.7", + "@vscode/tree-sitter-wasm": "^0.3.0", + "@vscode/vscode-languagedetection": "1.0.23", "@vscode/windows-process-tree": "^0.6.0", "@vscode/windows-registry": "^1.1.0", - "@xterm/addon-clipboard": "^0.2.0-beta.81", - "@xterm/addon-image": "^0.9.0-beta.98", - "@xterm/addon-ligatures": "^0.10.0-beta.98", - "@xterm/addon-progress": "^0.2.0-beta.4", - "@xterm/addon-search": "^0.16.0-beta.98", - "@xterm/addon-serialize": "^0.14.0-beta.98", - "@xterm/addon-unicode11": "^0.9.0-beta.98", - "@xterm/addon-webgl": "^0.19.0-beta.98", - "@xterm/headless": "^5.6.0-beta.98", - "@xterm/xterm": "^5.6.0-beta.98", - "cookie": "^0.7.0", + "@xterm/addon-clipboard": "^0.3.0-beta.107", + "@xterm/addon-image": "^0.10.0-beta.107", + "@xterm/addon-ligatures": "^0.11.0-beta.107", + "@xterm/addon-progress": "^0.3.0-beta.107", + "@xterm/addon-search": "^0.17.0-beta.107", + "@xterm/addon-serialize": "^0.15.0-beta.107", + "@xterm/addon-unicode11": "^0.10.0-beta.107", + "@xterm/addon-webgl": "^0.20.0-beta.106", + "@xterm/headless": "^6.0.0", + "@xterm/xterm": "^6.0.0", + "cookie": "^1.1.1", "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.2", "jschardet": "3.1.4", - "kerberos": "2.1.1", - "minimist": "^1.2.6", + "kerberos": "7.0.0", + "minimist": "^1.2.8", "native-watchdog": "^1.4.1", - "node-pty": "1.1.0-beta30", + "node-pty": "1.1.0", "tas-client-umd": "0.2.0", - "vscode-oniguruma": "1.7.0", + "vscode-oniguruma": "2.0.1", "vscode-regexpp": "^3.1.0", - "vscode-textmate": "9.2.0", - "yauzl": "^3.0.0", - "yazl": "^2.4.3" + "vscode-textmate": "9.3.2", + "yauzl": "^3.2.0", + "yazl": "^3.3.1" }, "overrides": { "node-gyp-build": "4.8.1", diff --git a/remote/web/package-lock.json b/remote/web/package-lock.json index 6bde53d629ba..0fd0c4ed0988 100644 --- a/remote/web/package-lock.json +++ b/remote/web/package-lock.json @@ -8,210 +8,211 @@ "name": "vscode-web", "version": "0.0.0", "dependencies": { - "@microsoft/1ds-core-js": "^3.2.13", - "@microsoft/1ds-post-js": "^3.2.13", - "@vscode/iconv-lite-umd": "0.7.0", - "@vscode/tree-sitter-wasm": "^0.1.2", - "@vscode/vscode-languagedetection": "1.0.21", - "@xterm/addon-clipboard": "^0.2.0-beta.81", - "@xterm/addon-image": "^0.9.0-beta.98", - "@xterm/addon-ligatures": "^0.10.0-beta.98", - "@xterm/addon-progress": "^0.2.0-beta.4", - "@xterm/addon-search": "^0.16.0-beta.98", - "@xterm/addon-serialize": "^0.14.0-beta.98", - "@xterm/addon-unicode11": "^0.9.0-beta.98", - "@xterm/addon-webgl": "^0.19.0-beta.98", - "@xterm/xterm": "^5.6.0-beta.98", + "@microsoft/1ds-core-js": "^4.3.11", + "@microsoft/1ds-post-js": "^4.3.11", + "@vscode/iconv-lite-umd": "0.7.1", + "@vscode/tree-sitter-wasm": "^0.3.0", + "@vscode/vscode-languagedetection": "1.0.23", + "@xterm/addon-clipboard": "^0.3.0-beta.107", + "@xterm/addon-image": "^0.10.0-beta.107", + "@xterm/addon-ligatures": "^0.11.0-beta.107", + "@xterm/addon-progress": "^0.3.0-beta.107", + "@xterm/addon-search": "^0.17.0-beta.107", + "@xterm/addon-serialize": "^0.15.0-beta.107", + "@xterm/addon-unicode11": "^0.10.0-beta.107", + "@xterm/addon-webgl": "^0.20.0-beta.106", + "@xterm/xterm": "^6.0.0", "jschardet": "3.1.4", "tas-client-umd": "0.2.0", - "vscode-oniguruma": "1.7.0", - "vscode-textmate": "9.2.0" + "vscode-oniguruma": "2.0.1", + "vscode-textmate": "9.3.2" } }, "node_modules/@microsoft/1ds-core-js": { - "version": "3.2.13", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-3.2.13.tgz", - "integrity": "sha512-CluYTRWcEk0ObG5EWFNWhs87e2qchJUn0p2D21ZUa3PWojPZfPSBs4//WIE0MYV8Qg1Hdif2ZTwlM7TbYUjfAg==", + "version": "4.3.11", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-core-js/-/1ds-core-js-4.3.11.tgz", + "integrity": "sha512-QyQE/YzFYB+31WEpX9hvDoXZOIXA7308Z5uuL1mSsyDSkNPl24hBWz9O3vZL+/p9shy756eKLI2nFLwwIAhXyw==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-core-js": "2.8.15", - "@microsoft/applicationinsights-shims": "^2.0.2", - "@microsoft/dynamicproto-js": "^1.1.7" + "@microsoft/applicationinsights-core-js": "3.3.11", + "@microsoft/applicationinsights-shims": "3.0.1", + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" } }, "node_modules/@microsoft/1ds-post-js": { - "version": "3.2.13", - "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-3.2.13.tgz", - "integrity": "sha512-HgS574fdD19Bo2vPguyznL4eDw7Pcm1cVNpvbvBLWiW3x4e1FCQ3VMXChWnAxCae8Hb0XqlA2sz332ZobBavTA==", + "version": "4.3.11", + "resolved": "https://registry.npmjs.org/@microsoft/1ds-post-js/-/1ds-post-js-4.3.11.tgz", + "integrity": "sha512-V0ZeeALy/Pj8HWgNHDsK+yDeCYnJ9bCgTWhcrna/ZiAT+sGfWs6mDBjAVcG03uP7TDjdWLf8w79lgbXJ3+s3DA==", + "license": "MIT", "dependencies": { - "@microsoft/1ds-core-js": "3.2.13", - "@microsoft/applicationinsights-shims": "^2.0.2", - "@microsoft/dynamicproto-js": "^1.1.7" + "@microsoft/1ds-core-js": "4.3.11", + "@microsoft/applicationinsights-shims": "3.0.1", + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" } }, "node_modules/@microsoft/applicationinsights-core-js": { - "version": "2.8.15", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-2.8.15.tgz", - "integrity": "sha512-yYAs9MyjGr2YijQdUSN9mVgT1ijI1FPMgcffpaPmYbHAVbQmF7bXudrBWHxmLzJlwl5rfep+Zgjli2e67lwUqQ==", + "version": "3.3.11", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-core-js/-/applicationinsights-core-js-3.3.11.tgz", + "integrity": "sha512-WlBY1sKDNL62T++NifgFCyDuOoNUNrVILfnHubOzgU/od7MFEQYWU8EZyDcBC/+Z8e3TD6jfixurYtWoUC+6Eg==", + "license": "MIT", "dependencies": { - "@microsoft/applicationinsights-shims": "2.0.2", - "@microsoft/dynamicproto-js": "^1.1.9" + "@microsoft/applicationinsights-shims": "3.0.1", + "@microsoft/dynamicproto-js": "^2.0.3", + "@nevware21/ts-async": ">= 0.5.4 < 2.x", + "@nevware21/ts-utils": ">= 0.11.8 < 2.x" }, "peerDependencies": { - "tslib": "*" + "tslib": ">= 1.0.0" } }, "node_modules/@microsoft/applicationinsights-shims": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-shims/-/applicationinsights-shims-2.0.2.tgz", - "integrity": "sha512-PoHEgsnmcqruLNHZ/amACqdJ6YYQpED0KSRe6J7gIJTtpZC1FfFU9b1fmDKDKtFoUSrPzEh1qzO3kmRZP0betg==" + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/@microsoft/applicationinsights-shims/-/applicationinsights-shims-3.0.1.tgz", + "integrity": "sha512-DKwboF47H1nb33rSUfjqI6ryX29v+2QWcTrRvcQDA32AZr5Ilkr7whOOSsD1aBzwqX0RJEIP1Z81jfE3NBm/Lg==", + "license": "MIT", + "dependencies": { + "@nevware21/ts-utils": ">= 0.9.4 < 2.x" + } }, "node_modules/@microsoft/dynamicproto-js": { - "version": "1.1.9", - "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-1.1.9.tgz", - "integrity": "sha512-n1VPsljTSkthsAFYdiWfC+DKzK2WwcRp83Y1YAqdX552BstvsDjft9YXppjUzp11BPsapDoO1LDgrDB0XVsfNQ==" + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@microsoft/dynamicproto-js/-/dynamicproto-js-2.0.3.tgz", + "integrity": "sha512-JTWTU80rMy3mdxOjjpaiDQsTLZ6YSGGqsjURsY6AUQtIj0udlF/jYmhdLZu8693ZIC0T1IwYnFa0+QeiMnziBA==", + "license": "MIT", + "dependencies": { + "@nevware21/ts-utils": ">= 0.10.4 < 2.x" + } + }, + "node_modules/@nevware21/ts-async": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/@nevware21/ts-async/-/ts-async-0.5.5.tgz", + "integrity": "sha512-vwqaL05iJPjLeh5igPi8MeeAu10i+Aq7xko1fbo9F5Si6MnVN5505qaV7AhSdk5MCBJVT/UYMk3kgInNjDb4Ig==", + "license": "MIT", + "dependencies": { + "@nevware21/ts-utils": ">= 0.12.2 < 2.x" + } + }, + "node_modules/@nevware21/ts-utils": { + "version": "0.12.5", + "resolved": "https://registry.npmjs.org/@nevware21/ts-utils/-/ts-utils-0.12.5.tgz", + "integrity": "sha512-JPQZWPKQJjj7kAftdEZL0XDFfbMgXCGiUAZe0d7EhLC3QlXTlZdSckGqqRIQ2QNl0VTEZyZUvRBw6Ednw089Fw==", + "license": "MIT" }, "node_modules/@vscode/iconv-lite-umd": { - "version": "0.7.0", - "resolved": "https://registry.npmjs.org/@vscode/iconv-lite-umd/-/iconv-lite-umd-0.7.0.tgz", - "integrity": "sha512-bRRFxLfg5dtAyl5XyiVWz/ZBPahpOpPrNYnnHpOpUZvam4tKH35wdhP4Kj6PbM0+KdliOsPzbGWpkxcdpNB/sg==" + "version": "0.7.1", + "resolved": "https://registry.npmjs.org/@vscode/iconv-lite-umd/-/iconv-lite-umd-0.7.1.tgz", + "integrity": "sha512-tK6k0DXFHW7q5+GGuGZO+phpAqpxO4WXl+BLc/8/uOk3RsM2ssAL3CQUQDb1TGfwltjsauhN6S4ghYZzs4sPFw==", + "license": "MIT" }, "node_modules/@vscode/tree-sitter-wasm": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/@vscode/tree-sitter-wasm/-/tree-sitter-wasm-0.1.2.tgz", - "integrity": "sha512-nXFrHey/TdhN2v15Qurs+trTSQB2ad0O8I8R7RMAjvrZi9qXAE+H+l9ZHHsA14fjZxKIyGg+nFmNpdEAuQALpw==", + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@vscode/tree-sitter-wasm/-/tree-sitter-wasm-0.3.0.tgz", + "integrity": "sha512-4kjB1jgLyG9VimGfyJb1F8/GFdrx55atsBCH/9r2D/iZHAUDCvZ5zhWXB7sRQ2z2WkkuNYm/0pgQtUm1jhdf7A==", "license": "MIT" }, "node_modules/@vscode/vscode-languagedetection": { - "version": "1.0.21", - "resolved": "https://registry.npmjs.org/@vscode/vscode-languagedetection/-/vscode-languagedetection-1.0.21.tgz", - "integrity": "sha512-zSUH9HYCw5qsCtd7b31yqkpaCU6jhtkKLkvOOA8yTrIRfBSOFb8PPhgmMicD7B/m+t4PwOJXzU1XDtrM9Fd3/g==", + "version": "1.0.23", + "resolved": "https://registry.npmjs.org/@vscode/vscode-languagedetection/-/vscode-languagedetection-1.0.23.tgz", + "integrity": "sha512-Ywk6vXC81nUHvc9WX3uFJG/UHFLXDYJgNeUVirBLJGvmchXHdapsGeAYclNqO1thQLmykmJhSouIZV+JJS8o1A==", + "license": "MIT", "bin": { "vscode-languagedetection": "cli/index.js" } }, "node_modules/@xterm/addon-clipboard": { - "version": "0.2.0-beta.81", - "resolved": "https://registry.npmjs.org/@xterm/addon-clipboard/-/addon-clipboard-0.2.0-beta.81.tgz", - "integrity": "sha512-vDxRyBO9VHzsl+gUQsDlUM9o2ZxSJKzE2eYQtuILKcf5D0EXYI86aMwKT/1iguX41vcMg42WXQBQ0TLWZ2MyTQ==", + "version": "0.3.0-beta.107", + "resolved": "https://registry.npmjs.org/@xterm/addon-clipboard/-/addon-clipboard-0.3.0-beta.107.tgz", + "integrity": "sha512-sSdnJUqaY9xFnJCwAefXEZ7rQOODtY6YtdX1LFXqClBMbw/WMLfuvUeRjnzQeWKUAgU+QUVlIPTT059H+0ETFQ==", "license": "MIT", "dependencies": { "js-base64": "^3.7.5" }, "peerDependencies": { - "@xterm/xterm": "^5.6.0-beta.98" + "@xterm/xterm": "^6.1.0-beta.107" } }, "node_modules/@xterm/addon-image": { - "version": "0.9.0-beta.98", - "resolved": "https://registry.npmjs.org/@xterm/addon-image/-/addon-image-0.9.0-beta.98.tgz", - "integrity": "sha512-yJaezwUc1Y3QYCmYSpjFW9IzMTLPSqrRCgdPnQ0JbCAVASRVmH6DLRfeikheJCvoXW6VUwMmGkb3fSplTxiV1w==", + "version": "0.10.0-beta.107", + "resolved": "https://registry.npmjs.org/@xterm/addon-image/-/addon-image-0.10.0-beta.107.tgz", + "integrity": "sha512-scYxbEmtZUeJHV6nz1l4DYtgYkQLUnE61mwBSZPSjy0cmYoKA8uUAaxucoJIBRtYPuA9RtotBueyatfp/HEy5Q==", "license": "MIT", "peerDependencies": { - "@xterm/xterm": "^5.6.0-beta.98" + "@xterm/xterm": "^6.1.0-beta.107" } }, "node_modules/@xterm/addon-ligatures": { - "version": "0.10.0-beta.98", - "resolved": "https://registry.npmjs.org/@xterm/addon-ligatures/-/addon-ligatures-0.10.0-beta.98.tgz", - "integrity": "sha512-1zYeS9OUBR2ThG7dsxsGKOqeSlUo+DNTd5aaV5ZFbKQsQ1w+sQCaL73ZrXp1kuVK7pBiP5NCo4ffcXfzcztztA==", + "version": "0.11.0-beta.107", + "resolved": "https://registry.npmjs.org/@xterm/addon-ligatures/-/addon-ligatures-0.11.0-beta.107.tgz", + "integrity": "sha512-M+sf5/SvLFRo7LifIzF1hRCtSHr0aL/g1dQGYPpz5ceuiVgGvPJhRM/N9QHFIS9axPx4RCfEGl5wcSp+CTWEnA==", "license": "MIT", "dependencies": { - "font-finder": "^1.1.0", - "font-ligatures": "^1.4.1" + "lru-cache": "^6.0.0", + "opentype.js": "^0.8.0" }, "engines": { "node": ">8.0.0" }, "peerDependencies": { - "@xterm/xterm": "^5.6.0-beta.98" + "@xterm/xterm": "^6.1.0-beta.107" } }, "node_modules/@xterm/addon-progress": { - "version": "0.2.0-beta.4", - "resolved": "https://registry.npmjs.org/@xterm/addon-progress/-/addon-progress-0.2.0-beta.4.tgz", - "integrity": "sha512-7t1nlaANdEjUBVvuTTs5gw6UQgB+unFLwSGGnYXIvdQroYdkXQXSSATSqpYKVCd/6QFhBerzdB2VwPM5L5lxIw==", + "version": "0.3.0-beta.107", + "resolved": "https://registry.npmjs.org/@xterm/addon-progress/-/addon-progress-0.3.0-beta.107.tgz", + "integrity": "sha512-8SB5U6DXrGQPIBfh8uGW+06JayU4ECsMKcUmxZj9GESy1T/7o1H5UsTZwl5+ZtczxmOCN7ydrowb1XGWXS0+Kg==", "license": "MIT", "peerDependencies": { - "@xterm/xterm": "^5.6.0-beta.98" + "@xterm/xterm": "^6.1.0-beta.107" } }, "node_modules/@xterm/addon-search": { - "version": "0.16.0-beta.98", - "resolved": "https://registry.npmjs.org/@xterm/addon-search/-/addon-search-0.16.0-beta.98.tgz", - "integrity": "sha512-7gtx7eYvwFLxlb5q2IKxa7jG1KEinVwTlT3ijnSsPawwn7rGi6nR135rGiR8DAjEV0GUO406ICeoYyVbgiFNwQ==", + "version": "0.17.0-beta.107", + "resolved": "https://registry.npmjs.org/@xterm/addon-search/-/addon-search-0.17.0-beta.107.tgz", + "integrity": "sha512-8hbPJABGkNvzYdHFOMwIUOW09PVeJb2yQj+b6LZtc4f1XyOOViXckJAT75aVfAXBdTWcW0241y5rlWz22GK1mA==", "license": "MIT", "peerDependencies": { - "@xterm/xterm": "^5.6.0-beta.98" + "@xterm/xterm": "^6.1.0-beta.107" } }, "node_modules/@xterm/addon-serialize": { - "version": "0.14.0-beta.98", - "resolved": "https://registry.npmjs.org/@xterm/addon-serialize/-/addon-serialize-0.14.0-beta.98.tgz", - "integrity": "sha512-Tsr8j3wnun2raYR1DgsNClQP/I5a85u/uW/5EiYH+/iPPua6EWJvPlr5Q6TCU/cdIKW1o27Z3L5/mw0pfMUXrQ==", + "version": "0.15.0-beta.107", + "resolved": "https://registry.npmjs.org/@xterm/addon-serialize/-/addon-serialize-0.15.0-beta.107.tgz", + "integrity": "sha512-ONNGwlonwK7mTriI3s8tc7VGSELAA7P42Z5rSbup7XF5z2MX2vNJCxrqnp2PjjAKxG++5r3qug+v1e/M8b6PjQ==", "license": "MIT", "peerDependencies": { - "@xterm/xterm": "^5.6.0-beta.98" + "@xterm/xterm": "^6.1.0-beta.107" } }, "node_modules/@xterm/addon-unicode11": { - "version": "0.9.0-beta.98", - "resolved": "https://registry.npmjs.org/@xterm/addon-unicode11/-/addon-unicode11-0.9.0-beta.98.tgz", - "integrity": "sha512-3GjjEYAPWAEGE1CaTFDjQxKcY5NiHHPmeufTsRp3IL5850ZiaMMq9bIL2WSdoFIbVgfbxh5mRy2cJPdu9m0uRQ==", + "version": "0.10.0-beta.107", + "resolved": "https://registry.npmjs.org/@xterm/addon-unicode11/-/addon-unicode11-0.10.0-beta.107.tgz", + "integrity": "sha512-aWnjjJGGAOUxuWRl+kF7EyD2qQ3TqRdb0u0L1aB1MlUk04qG5Ctv3uwLu8Moh/uAyJ7tAqgU7GQQP6yWDwvnJQ==", "license": "MIT", "peerDependencies": { - "@xterm/xterm": "^5.6.0-beta.98" + "@xterm/xterm": "^6.1.0-beta.107" } }, "node_modules/@xterm/addon-webgl": { - "version": "0.19.0-beta.98", - "resolved": "https://registry.npmjs.org/@xterm/addon-webgl/-/addon-webgl-0.19.0-beta.98.tgz", - "integrity": "sha512-09FbNHgN2ad/8JI+AEyg8C3msyk04ET1FihQIpTeWPfd2LJIAdps7G4St2+qzZbhlFkR6m9Dgrgh/AC2uegh8A==", + "version": "0.20.0-beta.106", + "resolved": "https://registry.npmjs.org/@xterm/addon-webgl/-/addon-webgl-0.20.0-beta.106.tgz", + "integrity": "sha512-QbVWbYmCk5w4W7k6P7Q5uaiOn4FOqxdvNFtAvFyO4zLtH9t22cEfihooIqmZzksxWafPIe9US55o9gu1DHUzLQ==", "license": "MIT", "peerDependencies": { - "@xterm/xterm": "^5.6.0-beta.98" + "@xterm/xterm": "^6.1.0-beta.107" } }, "node_modules/@xterm/xterm": { - "version": "5.6.0-beta.98", - "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.6.0-beta.98.tgz", - "integrity": "sha512-fJexj3XKDAMGsR8KKaiEhGrtJD1eRANbT3094E3KSgvbHRa3524tSFvDCx5+5KRE/hYaECmi0knAUIWJCvSPTg==", - "license": "MIT" - }, - "node_modules/font-finder": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/font-finder/-/font-finder-1.1.0.tgz", - "integrity": "sha512-wpCL2uIbi6GurJbU7ZlQ3nGd61Ho+dSU6U83/xJT5UPFfN35EeCW/rOtS+5k+IuEZu2SYmHzDIPL9eA5tSYRAw==", - "license": "MIT", - "dependencies": { - "get-system-fonts": "^2.0.0", - "promise-stream-reader": "^1.0.1" - }, - "engines": { - "node": ">8.0.0" - } - }, - "node_modules/font-ligatures": { - "version": "1.4.1", - "resolved": "https://registry.npmjs.org/font-ligatures/-/font-ligatures-1.4.1.tgz", - "integrity": "sha512-7W6zlfyhvCqShZ5ReUWqmSd9vBaUudW0Hxis+tqUjtHhsPU+L3Grf8mcZAtCiXHTzorhwdRTId2WeH/88gdFkw==", - "license": "MIT", - "dependencies": { - "font-finder": "^1.0.3", - "lru-cache": "^6.0.0", - "opentype.js": "^0.8.0" - }, - "engines": { - "node": ">8.0.0" - } - }, - "node_modules/get-system-fonts": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/get-system-fonts/-/get-system-fonts-2.0.2.tgz", - "integrity": "sha512-zzlgaYnHMIEgHRrfC7x0Qp0Ylhw/sHpM6MHXeVBTYIsvGf5GpbnClB+Q6rAPdn+0gd2oZZIo6Tj3EaWrt4VhDQ==", + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-6.0.0.tgz", + "integrity": "sha512-TQwDdQGtwwDt+2cgKDLn0IRaSxYu1tSUjgKarSDkUM0ZNiSRXFpjxEsvc/Zgc5kq5omJ+V0a8/kIM2WD3sMOYg==", "license": "MIT", - "engines": { - "node": ">8.0.0" - } + "workspaces": [ + "addons/*" + ] }, "node_modules/js-base64": { "version": "3.7.7", @@ -251,15 +252,6 @@ "ot": "bin/ot" } }, - "node_modules/promise-stream-reader": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/promise-stream-reader/-/promise-stream-reader-1.0.1.tgz", - "integrity": "sha512-Tnxit5trUjBAqqZCGWwjyxhmgMN4hGrtpW3Oc/tRI4bpm/O2+ej72BB08l6JBnGQgVDGCLvHFGjGgQS6vzhwXg==", - "license": "MIT", - "engines": { - "node": ">8.0.0" - } - }, "node_modules/tas-client-umd": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/tas-client-umd/-/tas-client-umd-0.2.0.tgz", @@ -272,14 +264,15 @@ "license": "MIT" }, "node_modules/vscode-oniguruma": { - "version": "1.7.0", - "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-1.7.0.tgz", - "integrity": "sha512-L9WMGRfrjOhgHSdOYgCt/yRMsXzLDJSL7BPrOZt73gU0iWO4mpqzqQzOz5srxqTvMBaR0XZTSrVWo4j55Rc6cA==" + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/vscode-oniguruma/-/vscode-oniguruma-2.0.1.tgz", + "integrity": "sha512-poJU8iHIWnC3vgphJnrLZyI3YdqRlR27xzqDmpPXYzA93R4Gk8z7T6oqDzDoHjoikA2aS82crdXFkjELCdJsjQ==", + "license": "MIT" }, "node_modules/vscode-textmate": { - "version": "9.2.0", - "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-9.2.0.tgz", - "integrity": "sha512-rkvG4SraZQaPSN/5XjwKswdU0OP9MF28QjrYzUBbhb8QyG3ljB1Ky996m++jiI7KdiAP2CkBiQZd9pqEDTClqA==", + "version": "9.3.2", + "resolved": "https://registry.npmjs.org/vscode-textmate/-/vscode-textmate-9.3.2.tgz", + "integrity": "sha512-n2uGbUcrjhUEBH16uGA0TvUfhWwliFZ1e3+pTjrkim1Mt7ydB41lV08aUvsi70OlzDWp6X7Bx3w/x3fAXIsN0Q==", "license": "MIT" }, "node_modules/yallist": { diff --git a/remote/web/package.json b/remote/web/package.json index 3e3b5d5ed1e9..a34adca40bb4 100644 --- a/remote/web/package.json +++ b/remote/web/package.json @@ -3,23 +3,23 @@ "version": "0.0.0", "private": true, "dependencies": { - "@microsoft/1ds-core-js": "^3.2.13", - "@microsoft/1ds-post-js": "^3.2.13", - "@vscode/iconv-lite-umd": "0.7.0", - "@vscode/tree-sitter-wasm": "^0.1.2", - "@vscode/vscode-languagedetection": "1.0.21", - "@xterm/addon-clipboard": "^0.2.0-beta.81", - "@xterm/addon-image": "^0.9.0-beta.98", - "@xterm/addon-ligatures": "^0.10.0-beta.98", - "@xterm/addon-progress": "^0.2.0-beta.4", - "@xterm/addon-search": "^0.16.0-beta.98", - "@xterm/addon-serialize": "^0.14.0-beta.98", - "@xterm/addon-unicode11": "^0.9.0-beta.98", - "@xterm/addon-webgl": "^0.19.0-beta.98", - "@xterm/xterm": "^5.6.0-beta.98", + "@microsoft/1ds-core-js": "^4.3.11", + "@microsoft/1ds-post-js": "^4.3.11", + "@vscode/iconv-lite-umd": "0.7.1", + "@vscode/tree-sitter-wasm": "^0.3.0", + "@vscode/vscode-languagedetection": "1.0.23", + "@xterm/addon-clipboard": "^0.3.0-beta.107", + "@xterm/addon-image": "^0.10.0-beta.107", + "@xterm/addon-ligatures": "^0.11.0-beta.107", + "@xterm/addon-progress": "^0.3.0-beta.107", + "@xterm/addon-search": "^0.17.0-beta.107", + "@xterm/addon-serialize": "^0.15.0-beta.107", + "@xterm/addon-unicode11": "^0.10.0-beta.107", + "@xterm/addon-webgl": "^0.20.0-beta.106", + "@xterm/xterm": "^6.0.0", "jschardet": "3.1.4", "tas-client-umd": "0.2.0", - "vscode-oniguruma": "1.7.0", - "vscode-textmate": "9.2.0" + "vscode-oniguruma": "2.0.1", + "vscode-textmate": "9.3.2" } } diff --git a/resources/server/bin/code-server-linux.sh b/resources/server/bin/code-server-linux.sh index 3df32dfd43c7..20df7cda4df4 100644 --- a/resources/server/bin/code-server-linux.sh +++ b/resources/server/bin/code-server-linux.sh @@ -9,4 +9,14 @@ esac ROOT="$(dirname "$(dirname "$(readlink -f "$0")")")" +# Set rpath before changing the interpreter path +# Refs https://github.com/NixOS/patchelf/issues/524 +if [ -n "$VSCODE_SERVER_CUSTOM_GLIBC_LINKER" ] && [ -n "$VSCODE_SERVER_CUSTOM_GLIBC_PATH" ] && [ -n "$VSCODE_SERVER_PATCHELF_PATH" ]; then + echo "Patching glibc from $VSCODE_SERVER_CUSTOM_GLIBC_PATH with $VSCODE_SERVER_PATCHELF_PATH..." + "$VSCODE_SERVER_PATCHELF_PATH" --set-rpath "$VSCODE_SERVER_CUSTOM_GLIBC_PATH" "$ROOT/node" + echo "Patching linker from $VSCODE_SERVER_CUSTOM_GLIBC_LINKER with $VSCODE_SERVER_PATCHELF_PATH..." + "$VSCODE_SERVER_PATCHELF_PATH" --set-interpreter "$VSCODE_SERVER_CUSTOM_GLIBC_LINKER" "$ROOT/node" + echo "Patching complete." +fi + "$ROOT/node" ${INSPECT:-} "$ROOT/out/server-main.js" "$@" diff --git a/resources/server/bin/helpers/check-requirements-linux-legacy.sh b/resources/server/bin/helpers/check-requirements-linux-legacy.sh deleted file mode 100755 index 0db776769650..000000000000 --- a/resources/server/bin/helpers/check-requirements-linux-legacy.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/usr/bin/env sh -# -# Copyright (c) Microsoft Corporation. All rights reserved. -# - -set -e - -echo "!!! WARNING: Using legacy server, please check https://aka.ms/vscode-remote/faq/old-linux for additional information !!!" -exit 0 diff --git a/resources/server/bin/helpers/check-requirements-linux.sh b/resources/server/bin/helpers/check-requirements-linux.sh index 8ef07a2fb1f0..8ea4c0b5adbf 100644 --- a/resources/server/bin/helpers/check-requirements-linux.sh +++ b/resources/server/bin/helpers/check-requirements-linux.sh @@ -7,21 +7,20 @@ set -e # The script checks necessary server requirements for the classic server # scenarios. Currently, the script can exit with any of the following -# 3 exit codes and should be handled accordingly on the extension side. +# 2 exit codes and should be handled accordingly on the extension side. # # 0: All requirements are met, use the default server. # 99: Unsupported OS, abort server startup with appropriate error message. -# 100: Use legacy server. # # Do not remove this check. # Provides a way to skip the server requirements check from # outside the install flow. A system process can create this # file before the server is downloaded and installed. -if [ -f "/tmp/vscode-skip-server-requirements-check" ]; then - echo "!!! WARNING: Skipping server pre-requisite check !!!" - echo "!!! Server stability is not guaranteed. Proceed at your own risk. !!!" - exit 0 +if [ -f "/tmp/vscode-skip-server-requirements-check" ] || [ -n "$VSCODE_SERVER_CUSTOM_GLIBC_LINKER" ]; then + echo "!!! WARNING: Skipping server pre-requisite check !!!" + echo "!!! Server stability is not guaranteed. Proceed at your own risk. !!!" + exit 0 fi ARCH=$(uname -m) @@ -156,7 +155,7 @@ else fi if [ "$found_required_glibc" = "0" ] || [ "$found_required_glibcxx" = "0" ]; then - echo "Warning: Missing required dependencies. Please refer to our FAQ https://aka.ms/vscode-remote/faq/old-linux for additional information." + echo "Error: Missing required dependencies. Please refer to our FAQ https://aka.ms/vscode-remote/faq/old-linux for additional information." # Custom exit code based on https://tldp.org/LDP/abs/html/exitcodes.html - exit 100 + exit 99 fi diff --git a/src/main.ts b/src/main.ts index 7e2ab209c01a..fdc424e10875 100644 --- a/src/main.ts +++ b/src/main.ts @@ -149,10 +149,35 @@ if (process.platform === 'win32' || process.platform === 'linux') { // Load our code once ready app.once('ready', function () { if (args['trace']) { - const traceOptions = { - categoryFilter: args['trace-category-filter'] || '*', - traceOptions: args['trace-options'] || 'record-until-full,enable-sampling' - }; + let traceOptions: Electron.TraceConfig | Electron.TraceCategoriesAndOptions; + if (args['trace-memory-infra']) { + const customCategories = args['trace-category-filter']?.split(',') || []; + customCategories.push('disabled-by-default-memory-infra', 'disabled-by-default-memory-infra.v8.code_stats'); + traceOptions = { + included_categories: customCategories, + excluded_categories: ['*'], + memory_dump_config: { + allowed_dump_modes: ['light', 'detailed'], + triggers: [ + { + type: 'periodic_interval', + mode: 'detailed', + min_time_between_dumps_ms: 10000 + }, + { + type: 'periodic_interval', + mode: 'light', + min_time_between_dumps_ms: 1000 + } + ] + } + }; + } else { + traceOptions = { + categoryFilter: args['trace-category-filter'] || '*', + traceOptions: args['trace-options'] || 'record-until-full,enable-sampling' + }; + } contentTracing.startRecording(traceOptions).finally(() => onReady()); } else { @@ -497,6 +522,18 @@ function getJSFlags(cliArgs: NativeParsedArgs): string | null { jsFlags.push(cliArgs['js-flags']); } + if (process.platform === 'linux') { + // Fix cppgc crash on Linux with 16KB page size. + // Refs https://issues.chromium.org/issues/378017037 + // The fix from https://github.com/electron/electron/commit/6c5b2ef55e08dc0bede02384747549c1eadac0eb + // only affects non-renderer process. + // The following will ensure that the flag will be + // applied to the renderer process as well. + // TODO(deepak1556): Remove this once we update to + // Chromium >= 134. + jsFlags.push('--nodecommit_pooled_pages'); + } + return jsFlags.length > 0 ? jsFlags.join(' ') : null; } diff --git a/src/vs/base/browser/dnd.ts b/src/vs/base/browser/dnd.ts index 879426cfb0cc..6ba1f767ae61 100644 --- a/src/vs/base/browser/dnd.ts +++ b/src/vs/base/browser/dnd.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { addDisposableListener, getWindow } from './dom.js'; +import { addDisposableListener } from './dom.js'; import { Disposable } from '../common/lifecycle.js'; import { Mimes } from '../common/mime.js'; @@ -81,29 +81,6 @@ export const DataTransfers = { INTERNAL_URI_LIST: 'application/vnd.code.uri-list', }; -export function applyDragImage(event: DragEvent, label: string | null, clazz: string, backgroundColor?: string | null, foregroundColor?: string | null): void { - const dragImage = document.createElement('div'); - dragImage.className = clazz; - dragImage.textContent = label; - - if (foregroundColor) { - dragImage.style.color = foregroundColor; - } - - if (backgroundColor) { - dragImage.style.background = backgroundColor; - } - - if (event.dataTransfer) { - const ownerDocument = getWindow(event).document; - ownerDocument.body.appendChild(dragImage); - event.dataTransfer.setDragImage(dragImage, -10, -10); - - // Removes the element when the DND operation is done - setTimeout(() => dragImage.remove(), 0); - } -} - export interface IDragAndDropData { update(dataTransfer: DataTransfer): void; getData(): unknown; diff --git a/src/vs/base/browser/fonts.ts b/src/vs/base/browser/fonts.ts index 73d028e78639..33f6a7af7b3b 100644 --- a/src/vs/base/browser/fonts.ts +++ b/src/vs/base/browser/fonts.ts @@ -3,7 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { isMacintosh, isWindows } from '../common/platform.js'; +import { mainWindow } from './window.js'; +import type { IJSONSchemaSnippet } from '../common/jsonSchema.js'; +import { isElectron, isMacintosh, isWindows } from '../common/platform.js'; /** * The best font-family to be used in CSS based on the platform: @@ -14,3 +16,34 @@ import { isMacintosh, isWindows } from '../common/platform.js'; * Note: this currently does not adjust for different locales. */ export const DEFAULT_FONT_FAMILY = isWindows ? '"Segoe WPC", "Segoe UI", sans-serif' : isMacintosh ? '-apple-system, BlinkMacSystemFont, sans-serif' : 'system-ui, "Ubuntu", "Droid Sans", sans-serif'; + +interface FontData { + readonly family: string; +} + +export const getFonts = async (): Promise => { + try { + // @ts-ignore + const fonts = await mainWindow.queryLocalFonts() as FontData[]; + const fontsArray = [...fonts]; + const families = fontsArray.map(font => font.family); + return families; + } catch (error) { + console.error(`Failed to query fonts: ${error}`); + return []; + } +}; + + +export const getFontSnippets = async (): Promise => { + if (!isElectron) { + return []; + } + const fonts = await getFonts(); + const snippets: IJSONSchemaSnippet[] = fonts.map(font => { + return { + body: `${font}` + }; + }); + return snippets; +}; diff --git a/src/vs/base/browser/markdownRenderer.ts b/src/vs/base/browser/markdownRenderer.ts index 71af77b8f254..cf15d43b0abb 100644 --- a/src/vs/base/browser/markdownRenderer.ts +++ b/src/vs/base/browser/markdownRenderer.ts @@ -585,7 +585,7 @@ const unescapeInfo = new Map([ ['>', '>'], ]); -function createRenderer(): marked.Renderer { +function createPlainTextRenderer(): marked.Renderer { const renderer = new marked.Renderer(); renderer.code = ({ text }: marked.Tokens.Code): string => { @@ -647,10 +647,10 @@ function createRenderer(): marked.Renderer { }; return renderer; } -const plainTextRenderer = new Lazy(createRenderer); +const plainTextRenderer = new Lazy(createPlainTextRenderer); const plainTextWithCodeBlocksRenderer = new Lazy(() => { - const renderer = createRenderer(); + const renderer = createPlainTextRenderer(); renderer.code = ({ text }: marked.Tokens.Code): string => { return `\n\`\`\`\n${escape(text)}\n\`\`\`\n`; }; diff --git a/src/vs/base/browser/ui/codicons/codicon/codicon.ttf b/src/vs/base/browser/ui/codicons/codicon/codicon.ttf index 7f16295a0f47..872f328858f8 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/dialog/dialog.css b/src/vs/base/browser/ui/dialog/dialog.css index 60dba786a8ab..bdca8bcfd742 100644 --- a/src/vs/base/browser/ui/dialog/dialog.css +++ b/src/vs/base/browser/ui/dialog/dialog.css @@ -71,6 +71,10 @@ white-space: normal; } +.monaco-dialog-box .dialog-message-row .dialog-message-container ul { + padding-inline-start: 20px; /* reduce excessive indent of list items in the dialog */ +} + /** Dialog: Message */ .monaco-dialog-box .dialog-message-row .dialog-message-container .dialog-message { line-height: 22px; @@ -153,3 +157,18 @@ margin: 4px 5px; /* allows button focus outline to be visible */ outline-offset: 2px !important; } + +/** Dialog: Dropdown */ +.monaco-dialog-box > .dialog-buttons-row > .dialog-buttons > .monaco-button-dropdown { + margin: 4px 5px; +} + +.monaco-dialog-box > .dialog-buttons-row > .dialog-buttons > .monaco-button-dropdown > .monaco-text-button { + padding-left: 10px; + padding-right: 10px; +} + +.monaco-dialog-box > .dialog-buttons-row > .dialog-buttons > .monaco-button-dropdown > .monaco-dropdown-button { + padding-left: 5px; + padding-right: 5px; +} diff --git a/src/vs/base/browser/ui/dialog/dialog.ts b/src/vs/base/browser/ui/dialog/dialog.ts index 0c357a671e78..bed93a10859a 100644 --- a/src/vs/base/browser/ui/dialog/dialog.ts +++ b/src/vs/base/browser/ui/dialog/dialog.ts @@ -3,21 +3,22 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import './dialog.css'; +import { localize } from '../../../../nls.js'; import { $, addDisposableListener, clearNode, EventHelper, EventType, getWindow, hide, isActiveElement, isAncestor, show } from '../../dom.js'; import { StandardKeyboardEvent } from '../../keyboardEvent.js'; import { ActionBar } from '../actionbar/actionbar.js'; -import { ButtonBar, ButtonWithDescription, IButtonStyles } from '../button/button.js'; +import { ButtonBar, ButtonWithDescription, IButton, IButtonStyles, IButtonWithDropdownOptions } from '../button/button.js'; import { ICheckboxStyles, Checkbox } from '../toggle/toggle.js'; import { IInputBoxStyles, InputBox } from '../inputbox/inputBox.js'; -import { Action } from '../../../common/actions.js'; +import { Action, toAction } from '../../../common/actions.js'; import { Codicon } from '../../../common/codicons.js'; import { ThemeIcon } from '../../../common/themables.js'; import { KeyCode, KeyMod } from '../../../common/keyCodes.js'; import { mnemonicButtonLabel } from '../../../common/labels.js'; -import { Disposable } from '../../../common/lifecycle.js'; +import { Disposable, toDisposable } from '../../../common/lifecycle.js'; import { isLinux, isMacintosh, isWindows } from '../../../common/platform.js'; -import './dialog.css'; -import * as nls from '../../../../nls.js'; +import { isActionProvider } from '../dropdown/dropdown.js'; export interface IDialogInputOptions { readonly placeholder?: string; @@ -36,6 +37,7 @@ export interface IDialogOptions { readonly renderBody?: (container: HTMLElement) => void; readonly icon?: ThemeIcon; readonly buttonDetails?: string[]; + readonly primaryButtonDropdown?: IButtonWithDropdownOptions; readonly disableCloseAction?: boolean; readonly disableDefaultAction?: boolean; readonly buttonStyles: IButtonStyles; @@ -97,7 +99,7 @@ export class Dialog extends Disposable { if (Array.isArray(buttons) && buttons.length > 0) { this.buttons = buttons; } else if (!this.options.disableDefaultAction) { - this.buttons = [nls.localize('ok', "OK")]; + this.buttons = [localize('ok', "OK")]; } else { this.buttons = []; } @@ -172,16 +174,16 @@ export class Dialog extends Disposable { } private getIconAriaLabel(): string { - let typeLabel = nls.localize('dialogInfoMessage', 'Info'); + let typeLabel = localize('dialogInfoMessage', 'Info'); switch (this.options.type) { case 'error': - typeLabel = nls.localize('dialogErrorMessage', 'Error'); + typeLabel = localize('dialogErrorMessage', 'Error'); break; case 'warning': - typeLabel = nls.localize('dialogWarningMessage', 'Warning'); + typeLabel = localize('dialogWarningMessage', 'Warning'); break; case 'pending': - typeLabel = nls.localize('dialogPendingMessage', 'In Progress'); + typeLabel = localize('dialogPendingMessage', 'In Progress'); break; case 'none': case 'info': @@ -200,7 +202,7 @@ export class Dialog extends Disposable { async show(): Promise { this.focusToReturn = this.container.ownerDocument.activeElement as HTMLElement; - return new Promise((resolve) => { + return new Promise(resolve => { clearNode(this.buttonsContainer); const close = () => { @@ -210,14 +212,44 @@ export class Dialog extends Disposable { }); return; }; + this._register(toDisposable(close)); const buttonBar = this.buttonBar = this._register(new ButtonBar(this.buttonsContainer)); const buttonMap = this.rearrangeButtons(this.buttons, this.options.cancelId); + const onButtonClick = (index: number) => { + resolve({ + button: buttonMap[index].index, + checkboxChecked: this.checkbox ? this.checkbox.checked : undefined, + values: this.inputs.length > 0 ? this.inputs.map(input => input.value) : undefined + }); + }; + // Handle button clicks - buttonMap.forEach((entry, index) => { + buttonMap.forEach((_, index) => { const primary = buttonMap[index].index === 0; - const button = this.options.buttonDetails ? this._register(buttonBar.addButtonWithDescription({ secondary: !primary, ...this.buttonStyles })) : this._register(buttonBar.addButton({ secondary: !primary, ...this.buttonStyles })); + + let button: IButton; + if (primary && this.options?.primaryButtonDropdown) { + const actions = isActionProvider(this.options.primaryButtonDropdown.actions) ? this.options.primaryButtonDropdown.actions.getActions() : this.options.primaryButtonDropdown.actions; + button = this._register(buttonBar.addButtonWithDropdown({ + ...this.options.primaryButtonDropdown, + ...this.buttonStyles, + actions: actions.map(action => toAction({ + ...action, + run: async () => { + await action.run(); + + onButtonClick(index); + } + })) + })); + } else if (this.options.buttonDetails) { + button = this._register(buttonBar.addButtonWithDescription({ secondary: !primary, ...this.buttonStyles })); + } else { + button = this._register(buttonBar.addButton({ secondary: !primary, ...this.buttonStyles })); + } + button.label = mnemonicButtonLabel(buttonMap[index].label, true); if (button instanceof ButtonWithDescription) { button.description = this.options.buttonDetails![buttonMap[index].index]; @@ -227,11 +259,7 @@ export class Dialog extends Disposable { EventHelper.stop(e); } - resolve({ - button: buttonMap[index].index, - checkboxChecked: this.checkbox ? this.checkbox.checked : undefined, - values: this.inputs.length > 0 ? this.inputs.map(input => input.value) : undefined - }); + onButtonClick(index); })); }); @@ -407,7 +435,7 @@ export class Dialog extends Disposable { if (!this.options.disableCloseAction) { const actionBar = this._register(new ActionBar(this.toolbarContainer, {})); - const action = this._register(new Action('dialog.close', nls.localize('dialogClose', "Close Dialog"), ThemeIcon.asClassName(Codicon.dialogClose), true, async () => { + const action = this._register(new Action('dialog.close', localize('dialogClose', "Close Dialog"), ThemeIcon.asClassName(Codicon.dialogClose), true, async () => { resolve({ button: this.options.cancelId || 0, checkboxChecked: this.checkbox ? this.checkbox.checked : undefined diff --git a/src/vs/base/browser/ui/dnd/dnd.css b/src/vs/base/browser/ui/dnd/dnd.css new file mode 100644 index 000000000000..3dbd92dce256 --- /dev/null +++ b/src/vs/base/browser/ui/dnd/dnd.css @@ -0,0 +1,28 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.monaco-drag-image { + display: inline-block; + padding: 1px 7px; + border-radius: 10px; + font-size: 12px; + position: absolute; + z-index: 1000; + + /* Default styles */ + background-color: var(--vscode-list-activeSelectionBackground); + color: var(--vscode-list-activeSelectionForeground); + outline: 1px solid var(--vscode-list-focusOutline); + outline-offset: -1px; + + /* + * Browsers apply an effect to the drag image when the div becomes too + * large which makes them unreadable. Use max width so it does not happen + */ + max-width: 120px; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} diff --git a/src/vs/base/browser/ui/dnd/dnd.ts b/src/vs/base/browser/ui/dnd/dnd.ts new file mode 100644 index 000000000000..39ce04a13a80 --- /dev/null +++ b/src/vs/base/browser/ui/dnd/dnd.ts @@ -0,0 +1,31 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { $ } from '../../dom.js'; +import './dnd.css'; + +export function applyDragImage(event: DragEvent, container: HTMLElement, label: string, extraClasses: string[] = []): void { + if (!event.dataTransfer) { + return; + } + + const dragImage = $('.monaco-drag-image'); + dragImage.textContent = label; + dragImage.classList.add(...extraClasses); + + const getDragImageContainer = (e: HTMLElement | null) => { + while (e && !e.classList.contains('monaco-workbench')) { + e = e.parentElement; + } + return e || container.ownerDocument.body; + }; + + const dragContainer = getDragImageContainer(container); + dragContainer.appendChild(dragImage); + event.dataTransfer.setDragImage(dragImage, -10, -10); + + // Removes the element when the DND operation is done + setTimeout(() => dragImage.remove(), 0); +} diff --git a/src/vs/base/browser/ui/dropdown/dropdown.ts b/src/vs/base/browser/ui/dropdown/dropdown.ts index dabab279dab7..b4a7d38a6fb4 100644 --- a/src/vs/base/browser/ui/dropdown/dropdown.ts +++ b/src/vs/base/browser/ui/dropdown/dropdown.ts @@ -161,6 +161,12 @@ export interface IActionProvider { getActions(): readonly IAction[]; } +export function isActionProvider(obj: unknown): obj is IActionProvider { + const candidate = obj as IActionProvider | undefined; + + return typeof candidate?.getActions === 'function'; +} + export interface IDropdownMenuOptions extends IBaseDropdownOptions { contextMenuProvider: IContextMenuProvider; readonly actions?: IAction[]; diff --git a/src/vs/base/browser/ui/hover/hover.ts b/src/vs/base/browser/ui/hover/hover.ts index ec7408607080..6b877ec17fdb 100644 --- a/src/vs/base/browser/ui/hover/hover.ts +++ b/src/vs/base/browser/ui/hover/hover.ts @@ -13,32 +13,6 @@ import type { IDisposable } from '../../../common/lifecycle.js'; * Enables the convenient display of rich markdown-based hovers in the workbench. */ export interface IHoverDelegate2 { - /** - * Shows a hover immediately, provided a hover with the same {@link options} object is not - * already visible. - * - * Use this method when you want to: - * - * - Control showing the hover yourself. - * - Show the hover immediately. - * - * @param options A set of options defining the characteristics of the hover. - * @param focus Whether to focus the hover (useful for keyboard accessibility). - * - * @example A simple usage with a single element target. - * - * ```typescript - * showHover({ - * text: new MarkdownString('Hello world'), - * target: someElement - * }); - * ``` - */ - showHover( - options: IHoverOptions, - focus?: boolean - ): IHoverWidget | undefined; - /** * Shows a hover after a delay, or immediately if the {@link groupId} matches the currently * shown hover. @@ -100,11 +74,37 @@ export interface IHoverDelegate2 { lifecycleOptions?: IHoverLifecycleOptions, ): IDisposable; + /** + * Shows a hover immediately, provided a hover with the same {@link options} object is not + * already visible. + * + * Use this method when you want to: + * + * - Control showing the hover yourself. + * - Show the hover immediately. + * + * @param options A set of options defining the characteristics of the hover. + * @param focus Whether to focus the hover (useful for keyboard accessibility). + * + * @example A simple usage with a single element target. + * + * ```typescript + * showInstantHover({ + * text: new MarkdownString('Hello world'), + * target: someElement + * }); + * ``` + */ + showInstantHover( + options: IHoverOptions, + focus?: boolean + ): IHoverWidget | undefined; + /** * Hides the hover if it was visible. This call will be ignored if the hover is currently - * "locked" via the alt/option key. + * "locked" via the alt/option key unless `force` is set. */ - hideHover(): void; + hideHover(force?: boolean): void; /** * This should only be used until we have the ability to show multiple context views @@ -116,8 +116,8 @@ export interface IHoverDelegate2 { * Sets up a managed hover for the given element. A managed hover will set up listeners for * mouse events, show the hover after a delay and provide hooks to easily update the content. * - * This should be used over {@link showHover} when fine-grained control is not needed. The - * managed hover also does not scale well, consider using {@link showHover} when showing hovers + * This should be used over {@link showInstantHover} when fine-grained control is not needed. The + * managed hover also does not scale well, consider using {@link showInstantHover} when showing hovers * for many elements. * * @param hoverDelegate The hover delegate containing hooks and configuration for the hover. @@ -382,7 +382,21 @@ export interface IManagedHoverTooltipMarkdownString { markdownNotSupportedFallback: string | undefined; } -export type IManagedHoverContent = string | IManagedHoverTooltipMarkdownString | HTMLElement | undefined; +export function isManagedHoverTooltipMarkdownString(obj: unknown): obj is IManagedHoverTooltipMarkdownString { + const candidate = obj as IManagedHoverTooltipMarkdownString; + return typeof candidate === 'object' && 'markdown' in candidate && 'markdownNotSupportedFallback' in candidate; +} + +export interface IManagedHoverTooltipHTMLElement { + element: (token: CancellationToken) => HTMLElement | Promise; +} + +export function isManagedHoverTooltipHTMLElement(obj: unknown): obj is IManagedHoverTooltipHTMLElement { + const candidate = obj as IManagedHoverTooltipHTMLElement; + return typeof candidate === 'object' && 'element' in candidate; +} + +export type IManagedHoverContent = string | IManagedHoverTooltipMarkdownString | IManagedHoverTooltipHTMLElement | HTMLElement | undefined; export type IManagedHoverContentOrFactory = IManagedHoverContent | (() => IManagedHoverContent); export interface IManagedHoverOptions extends Pick { diff --git a/src/vs/base/browser/ui/hover/hoverDelegate.ts b/src/vs/base/browser/ui/hover/hoverDelegate.ts index 31ec93f727ae..cfb87d1ac348 100644 --- a/src/vs/base/browser/ui/hover/hoverDelegate.ts +++ b/src/vs/base/browser/ui/hover/hoverDelegate.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import type { IHoverWidget, IManagedHoverOptions } from './hover.js'; +import type { IHoverWidget, IManagedHoverContentOrFactory, IManagedHoverOptions } from './hover.js'; import { HoverPosition } from './hoverWidget.js'; import { IMarkdownString } from '../../../common/htmlContent.js'; import { IDisposable } from '../../../common/lifecycle.js'; @@ -67,7 +67,7 @@ export interface IHoverDelegateOptions extends IManagedHoverOptions { export interface IHoverDelegate { showHover(options: IHoverDelegateOptions, focus?: boolean): IHoverWidget | undefined; onDidHideHover?: () => void; - delay: number; + delay: number | ((content?: IManagedHoverContentOrFactory) => number); placement?: 'mouse' | 'element'; showNativeHover?: boolean; // TODO@benibenj remove this, only temp fix for contextviews } diff --git a/src/vs/base/browser/ui/hover/hoverDelegate2.ts b/src/vs/base/browser/ui/hover/hoverDelegate2.ts index 05b4c692d940..b49cb84951c5 100644 --- a/src/vs/base/browser/ui/hover/hoverDelegate2.ts +++ b/src/vs/base/browser/ui/hover/hoverDelegate2.ts @@ -7,7 +7,7 @@ import { Disposable } from '../../../common/lifecycle.js'; import type { IHoverDelegate2 } from './hover.js'; let baseHoverDelegate: IHoverDelegate2 = { - showHover: () => undefined, + showInstantHover: () => undefined, showDelayedHover: () => undefined, setupDelayedHover: () => Disposable.None, setupDelayedHoverAtMouse: () => Disposable.None, diff --git a/src/vs/base/browser/ui/iconLabel/iconlabel.css b/src/vs/base/browser/ui/iconLabel/iconlabel.css index ad3a358bc5a2..ed9278d7087d 100644 --- a/src/vs/base/browser/ui/iconLabel/iconlabel.css +++ b/src/vs/base/browser/ui/iconLabel/iconlabel.css @@ -89,11 +89,6 @@ opacity: 0.66; } -/* make sure apply italic font style to decorations as well */ -.monaco-icon-label.italic::after { - font-style: italic; -} - .monaco-icon-label.strikethrough > .monaco-icon-label-container > .monaco-icon-name-container > .label-name, .monaco-icon-label.strikethrough > .monaco-icon-label-container > .monaco-icon-description-container > .label-description { text-decoration: line-through; diff --git a/src/vs/base/browser/ui/inputbox/inputBox.ts b/src/vs/base/browser/ui/inputbox/inputBox.ts index 5ccf89beee48..4081c2f28b22 100644 --- a/src/vs/base/browser/ui/inputbox/inputBox.ts +++ b/src/vs/base/browser/ui/inputbox/inputBox.ts @@ -650,7 +650,7 @@ export class HistoryInputBox extends InputBox implements IHistoryNavigationWidge }, ' ({0} for history)', `\u21C5`); super(container, contextViewProvider, options); - this.history = new HistoryNavigator(options.history, 100); + this.history = this._register(new HistoryNavigator(options.history, 100)); // Function to append the history suffix to the placeholder if necessary const addSuffix = () => { diff --git a/src/vs/base/browser/ui/list/list.css b/src/vs/base/browser/ui/list/list.css index be273a61823d..672f03e42f04 100644 --- a/src/vs/base/browser/ui/list/list.css +++ b/src/vs/base/browser/ui/list/list.css @@ -60,16 +60,6 @@ outline: 0 !important; } -/* Dnd */ -.monaco-drag-image { - display: inline-block; - padding: 1px 7px; - border-radius: 10px; - font-size: 12px; - position: absolute; - z-index: 1000; -} - /* Filter */ .monaco-list-type-filter-message { diff --git a/src/vs/base/browser/ui/list/listView.ts b/src/vs/base/browser/ui/list/listView.ts index 4c8af3e908e6..2db1250ff861 100644 --- a/src/vs/base/browser/ui/list/listView.ts +++ b/src/vs/base/browser/ui/list/listView.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { DataTransfers, IDragAndDropData } from '../../dnd.js'; -import { $, addDisposableListener, animate, Dimension, getActiveElement, getContentHeight, getContentWidth, getDocument, getTopLeftOffset, getWindow, isAncestor, isHTMLElement, isSVGElement, scheduleAtNextAnimationFrame } from '../../dom.js'; +import { addDisposableListener, animate, Dimension, getActiveElement, getContentHeight, getContentWidth, getDocument, getTopLeftOffset, getWindow, isAncestor, isHTMLElement, isSVGElement, scheduleAtNextAnimationFrame } from '../../dom.js'; import { DomEmitter } from '../../event.js'; import { IMouseWheelEvent } from '../../mouseEvent.js'; import { EventType as TouchEventType, Gesture, GestureEvent } from '../../touch.js'; @@ -24,6 +24,7 @@ import { BugIndicatingError } from '../../../common/errors.js'; import { AriaRole } from '../aria/aria.js'; import { ScrollableElementChangeOptions } from '../scrollbar/scrollableElementOptions.js'; import { clamp } from '../../../common/numbers.js'; +import { applyDragImage } from '../dnd/dnd.js'; interface IItem { readonly id: string; @@ -1180,33 +1181,16 @@ export class ListView implements IListView { event.dataTransfer.effectAllowed = 'copyMove'; event.dataTransfer.setData(DataTransfers.TEXT, uri); - if (event.dataTransfer.setDragImage) { - let label: string | undefined; - - if (this.dnd.getDragLabel) { - label = this.dnd.getDragLabel(elements, event); - } - - if (typeof label === 'undefined') { - label = String(elements.length); - } - - const dragImage = $('.monaco-drag-image'); - dragImage.textContent = label; - - const getDragImageContainer = (e: HTMLElement | null) => { - while (e && !e.classList.contains('monaco-workbench')) { - e = e.parentElement; - } - return e || this.domNode.ownerDocument; - }; - - const container = getDragImageContainer(this.domNode); - container.appendChild(dragImage); - event.dataTransfer.setDragImage(dragImage, -10, -10); - setTimeout(() => dragImage.remove(), 0); + let label: string | undefined; + if (this.dnd.getDragLabel) { + label = this.dnd.getDragLabel(elements, event); + } + if (typeof label === 'undefined') { + label = String(elements.length); } + applyDragImage(event, this.domNode, label, [this.domId /* add domId to get list specific styling */]); + this.domNode.classList.add('dragging'); this.currentDragData = new ElementsDragAndDropData(elements); StaticDND.CurrentDragAndDropData = new ExternalElementsDragAndDropData(elements); @@ -1243,7 +1227,11 @@ export class ListView implements IListView { })); selectionStore.add(addDisposableListener(doc, 'selectionchange', () => { const selection = doc.getSelection(); - if (!selection) { + // if the selection changed _after_ mouseup, it's from clearing the list or similar, so teardown + if (!selection || selection.isCollapsed) { + if (movementStore.isDisposed) { + selectionStore.dispose(); + } return; } @@ -1527,8 +1515,9 @@ export class ListView implements IListView { protected getRenderRange(renderTop: number, renderHeight: number): IRange { const range = this.getVisibleRange(renderTop, renderHeight); if (this.currentSelectionBounds) { - range.start = Math.min(range.start, this.currentSelectionBounds.start); - range.end = Math.max(range.end, this.currentSelectionBounds.end + 1); + const max = this.rangeMap.count; + range.start = Math.min(range.start, this.currentSelectionBounds.start, max); + range.end = Math.min(Math.max(range.end, this.currentSelectionBounds.end + 1), max); } return range; diff --git a/src/vs/base/browser/ui/list/listWidget.ts b/src/vs/base/browser/ui/list/listWidget.ts index 78932044f96e..44b59334606e 100644 --- a/src/vs/base/browser/ui/list/listWidget.ts +++ b/src/vs/base/browser/ui/list/listWidget.ts @@ -899,14 +899,14 @@ export class DefaultStyleController implements IStyleController { if (styles.listFocusAndSelectionBackground) { content.push(` - .monaco-drag-image, + .monaco-drag-image${suffix}, .monaco-list${suffix}:focus .monaco-list-row.selected.focused { background-color: ${styles.listFocusAndSelectionBackground}; } `); } if (styles.listFocusAndSelectionForeground) { content.push(` - .monaco-drag-image, + .monaco-drag-image${suffix}, .monaco-list${suffix}:focus .monaco-list-row.selected.focused { color: ${styles.listFocusAndSelectionForeground}; } `); } @@ -952,8 +952,8 @@ export class DefaultStyleController implements IStyleController { if (styles.listFocusOutline) { // default: set content.push(` - .monaco-drag-image, - .monaco-list${suffix}:focus .monaco-list-row.focused { outline: 1px solid ${styles.listFocusOutline}; outline-offset: -1px; } + .monaco-drag-image${suffix}, + .monaco-list${suffix}:focus .monaco-list-row.focused, .monaco-workbench.context-menu-visible .monaco-list${suffix}.last-focused .monaco-list-row.focused { outline: 1px solid ${styles.listFocusOutline}; outline-offset: -1px; } `); } diff --git a/src/vs/platform/severityIcon/browser/media/severityIcon.css b/src/vs/base/browser/ui/severityIcon/media/severityIcon.css similarity index 93% rename from src/vs/platform/severityIcon/browser/media/severityIcon.css rename to src/vs/base/browser/ui/severityIcon/media/severityIcon.css index 4fd1d06c80be..62d99edf3370 100644 --- a/src/vs/platform/severityIcon/browser/media/severityIcon.css +++ b/src/vs/base/browser/ui/severityIcon/media/severityIcon.css @@ -8,7 +8,8 @@ .text-search-provider-messages .providerMessage .codicon.codicon-error, .extensions-viewlet > .extensions .codicon.codicon-error, .extension-editor .codicon.codicon-error, -.preferences-editor .codicon.codicon-error { +.preferences-editor .codicon.codicon-error, +.chat-attached-context-attachment .codicon.codicon-error { color: var(--vscode-problemsErrorIcon-foreground); } diff --git a/src/vs/platform/severityIcon/browser/severityIcon.ts b/src/vs/base/browser/ui/severityIcon/severityIcon.ts similarity index 82% rename from src/vs/platform/severityIcon/browser/severityIcon.ts rename to src/vs/base/browser/ui/severityIcon/severityIcon.ts index 73bfe9c2da17..66f5564139ae 100644 --- a/src/vs/platform/severityIcon/browser/severityIcon.ts +++ b/src/vs/base/browser/ui/severityIcon/severityIcon.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import './media/severityIcon.css'; -import { Codicon } from '../../../base/common/codicons.js'; -import { ThemeIcon } from '../../../base/common/themables.js'; -import Severity from '../../../base/common/severity.js'; +import { Codicon } from '../../../common/codicons.js'; +import { ThemeIcon } from '../../../common/themables.js'; +import Severity from '../../../common/severity.js'; export namespace SeverityIcon { diff --git a/src/vs/base/browser/ui/splitview/paneview.ts b/src/vs/base/browser/ui/splitview/paneview.ts index 9dd4bb3d8974..42d2234a1316 100644 --- a/src/vs/base/browser/ui/splitview/paneview.ts +++ b/src/vs/base/browser/ui/splitview/paneview.ts @@ -18,6 +18,7 @@ import { ScrollEvent } from '../../../common/scrollable.js'; import './paneview.css'; import { localize } from '../../../../nls.js'; import { IView, Sizing, SplitView } from './splitview.js'; +import { applyDragImage } from '../dnd/dnd.js'; export interface IPaneOptions { minimumBodySize?: number; @@ -381,16 +382,16 @@ class PaneDraggable extends Disposable { return; } + const label = this.pane.draggableElement?.textContent || ''; + e.dataTransfer.effectAllowed = 'move'; if (isFirefox) { // Firefox: requires to set a text data transfer to get going - e.dataTransfer?.setData(DataTransfers.TEXT, this.pane.draggableElement?.textContent || ''); + e.dataTransfer?.setData(DataTransfers.TEXT, label); } - const dragImage = append(this.pane.element.ownerDocument.body, $('.monaco-drag-image', {}, this.pane.draggableElement?.textContent || '')); - e.dataTransfer.setDragImage(dragImage, -10, -10); - setTimeout(() => dragImage.remove(), 0); + applyDragImage(e, this.pane.element, label); this.context.draggable = this; } diff --git a/src/vs/base/browser/ui/toolbar/toolbar.ts b/src/vs/base/browser/ui/toolbar/toolbar.ts index 75ab73a53541..1c2be8cc6a8f 100644 --- a/src/vs/base/browser/ui/toolbar/toolbar.ts +++ b/src/vs/base/browser/ui/toolbar/toolbar.ts @@ -18,8 +18,6 @@ import * as nls from '../../../../nls.js'; import { IHoverDelegate } from '../hover/hoverDelegate.js'; import { createInstantHoverDelegate } from '../hover/hoverDelegateFactory.js'; - - export interface IToolBarOptions { orientation?: ActionsOrientation; actionViewItemProvider?: IActionViewItemProvider; diff --git a/src/vs/base/browser/ui/tree/asyncDataTree.ts b/src/vs/base/browser/ui/tree/asyncDataTree.ts index f6c47bb9283e..197df0b42491 100644 --- a/src/vs/base/browser/ui/tree/asyncDataTree.ts +++ b/src/vs/base/browser/ui/tree/asyncDataTree.ts @@ -819,6 +819,10 @@ export class AsyncDataTree implements IDisposable this.tree.resort(this.getDataNode(element), recursive); } + hasElement(element: TInput | T): boolean { + return this.tree.hasElement(this.getDataNode(element)); + } + hasNode(element: TInput | T): boolean { return element === this.root.element || this.nodes.has(element as T); } diff --git a/src/vs/base/common/arrays.ts b/src/vs/base/common/arrays.ts index d4a409034430..98bf168f39da 100644 --- a/src/vs/base/common/arrays.ts +++ b/src/vs/base/common/arrays.ts @@ -620,6 +620,36 @@ function getActualStartIndex(array: T[], start: number): number { return start < 0 ? Math.max(start + array.length, 0) : Math.min(start, array.length); } +/** + * Utility that helps to pick a property from an object. + * + * ## Examples + * + * ```typescript + * interface IObject = { + * a: number, + * b: string, + * }; + * + * const list: IObject[] = [ + * { a: 1, b: 'foo' }, + * { a: 2, b: 'bar' }, + * ]; + * + * assert.deepStrictEqual( + * list.map(pick('a')), + * [1, 2], + * ); + * ``` + */ +export const pick = ( + key: TKeyName, +) => { + return (obj: TObject): TObject[TKeyName] => { + return obj[key]; + }; +}; + /** * When comparing two values, * a negative number indicates that the first value is less than the second, diff --git a/src/vs/base/common/buffer.ts b/src/vs/base/common/buffer.ts index 7f8e1b34a811..4702f6ffdcaa 100644 --- a/src/vs/base/common/buffer.ts +++ b/src/vs/base/common/buffer.ts @@ -175,6 +175,18 @@ export class VSBuffer { indexOf(subarray: VSBuffer | Uint8Array, offset = 0) { return binaryIndexOf(this.buffer, subarray instanceof VSBuffer ? subarray.buffer : subarray, offset); } + + equals(other: VSBuffer): boolean { + if (this === other) { + return true; + } + + if (this.byteLength !== other.byteLength) { + return false; + } + + return this.buffer.every((value, index) => value === other.buffer[index]); + } } /** diff --git a/src/vs/base/common/codiconsLibrary.ts b/src/vs/base/common/codiconsLibrary.ts index 87b0a393661a..13a3a4f0c310 100644 --- a/src/vs/base/common/codiconsLibrary.ts +++ b/src/vs/base/common/codiconsLibrary.ts @@ -589,4 +589,7 @@ export const codiconsLibrary = { keyboardTab: register('keyboard-tab', 0xec3c), copilotBlocked: register('copilot-blocked', 0xec3d), copilotNotConnected: register('copilot-not-connected', 0xec3e), + flag: register('flag', 0xec3f), + lightbulbEmpty: register('lightbulb-empty', 0xec40), + symbolMethodArrow: register('symbol-method-arrow', 0xec41), } as const; diff --git a/src/vs/base/common/color.ts b/src/vs/base/common/color.ts index 9f20f7080ab5..d36c1526d60d 100644 --- a/src/vs/base/common/color.ts +++ b/src/vs/base/common/color.ts @@ -585,15 +585,11 @@ export namespace Color { export namespace CSS { export function formatRGB(color: Color): string { - if (color.rgba.a === 1) { - return `rgb(${color.rgba.r}, ${color.rgba.g}, ${color.rgba.b})`; + const alpha = color.rgba.a; + if (alpha === 1) { + return `rgb(${color.rgba.r} ${color.rgba.g} ${color.rgba.b})`; } - - return Color.Format.CSS.formatRGBA(color); - } - - export function formatRGBA(color: Color): string { - return `rgba(${color.rgba.r}, ${color.rgba.g}, ${color.rgba.b}, ${+(color.rgba.a).toFixed(2)})`; + return `rgb(${color.rgba.r} ${color.rgba.g} ${color.rgba.b} / ${+(alpha).toFixed(2)})`; } export function formatHSL(color: Color): string { @@ -640,7 +636,7 @@ export namespace Color { return Color.Format.CSS.formatHex(color); } - return Color.Format.CSS.formatRGBA(color); + return Color.Format.CSS.formatRGB(color); } /** diff --git a/src/vs/base/common/dataTransfer.ts b/src/vs/base/common/dataTransfer.ts index dd3f31f6e796..3ce5770c57f9 100644 --- a/src/vs/base/common/dataTransfer.ts +++ b/src/vs/base/common/dataTransfer.ts @@ -16,22 +16,25 @@ export interface IDataTransferFile { } export interface IDataTransferItem { + id?: string; asString(): Thenable; asFile(): IDataTransferFile | undefined; value: any; } -export function createStringDataTransferItem(stringOrPromise: string | Promise): IDataTransferItem { +export function createStringDataTransferItem(stringOrPromise: string | Promise, id?: string): IDataTransferItem { return { + id, asString: async () => stringOrPromise, asFile: () => undefined, value: typeof stringOrPromise === 'string' ? stringOrPromise : undefined, }; } -export function createFileDataTransferItem(fileName: string, uri: URI | undefined, data: () => Promise): IDataTransferItem { +export function createFileDataTransferItem(fileName: string, uri: URI | undefined, data: () => Promise, id?: string): IDataTransferItem { const file = { id: generateUuid(), name: fileName, uri, data }; return { + id, asString: async () => '', asFile: () => file, value: undefined, diff --git a/src/vs/base/common/history.ts b/src/vs/base/common/history.ts index a705839b71e3..3c23e8e3a29a 100644 --- a/src/vs/base/common/history.ts +++ b/src/vs/base/common/history.ts @@ -5,6 +5,7 @@ import { SetWithKey } from './collections.js'; import { Event } from './event.js'; +import { IDisposable } from './lifecycle.js'; import { ArrayNavigator, INavigator } from './navigator.js'; export interface IHistory { @@ -20,6 +21,7 @@ export interface IHistory { export class HistoryNavigator implements INavigator { private _limit: number; private _navigator!: ArrayNavigator; + private _disposable: IDisposable | undefined; constructor( private _history: IHistory = new Set(), @@ -28,7 +30,7 @@ export class HistoryNavigator implements INavigator { this._limit = limit; this._onChange(); if (this._history.onDidChange) { - this._history.onDidChange(() => this._onChange()); + this._disposable = this._history.onDidChange(() => this._onChange()); } } @@ -119,6 +121,13 @@ export class HistoryNavigator implements INavigator { this._history.forEach(e => elements.push(e)); return elements; } + + public dispose(): void { + if (this._disposable) { + this._disposable.dispose(); + this._disposable = undefined; + } + } } interface HistoryNode { diff --git a/src/vs/base/common/hotReload.ts b/src/vs/base/common/hotReload.ts index 80901b470f0d..4f0e1341681c 100644 --- a/src/vs/base/common/hotReload.ts +++ b/src/vs/base/common/hotReload.ts @@ -6,12 +6,8 @@ import { IDisposable } from './lifecycle.js'; import { env } from './process.js'; -function hotReloadDisabled() { - return true; // TODO@hediet fix hot reload. -} - export function isHotReloadEnabled(): boolean { - return !hotReloadDisabled() && env && !!env['VSCODE_DEV']; + return env && !!env['VSCODE_DEV_DEBUG']; } export function registerHotReloadHandler(handler: HotReloadHandler): IDisposable { if (!isHotReloadEnabled()) { diff --git a/src/vs/base/common/htmlContent.ts b/src/vs/base/common/htmlContent.ts index 8b6213d18445..4fa81895fa27 100644 --- a/src/vs/base/common/htmlContent.ts +++ b/src/vs/base/common/htmlContent.ts @@ -34,6 +34,18 @@ export class MarkdownString implements IMarkdownString { public supportThemeIcons?: boolean; public supportHtml?: boolean; public baseUri?: URI; + public uris?: { [href: string]: UriComponents } | undefined; + + public static lift(dto: IMarkdownString): MarkdownString { + if (dto instanceof MarkdownString) { + return dto; + } + + const markdownString = new MarkdownString(dto.value, dto); + markdownString.uris = dto.uris; + markdownString.baseUri = dto.baseUri ? URI.revive(dto.baseUri) : undefined; + return markdownString; + } constructor( value: string = '', @@ -58,6 +70,7 @@ export class MarkdownString implements IMarkdownString { appendText(value: string, newlineStyle: MarkdownStringTextNewlineStyle = MarkdownStringTextNewlineStyle.Paragraph): MarkdownString { this.value += escapeMarkdownSyntaxTokens(this.supportThemeIcons ? escapeIcons(value) : value) // CodeQL [SM02383] The Markdown is fully sanitized after being rendered. + .replace(/\\/g, '\\\\') // Escape backslash characters .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. diff --git a/src/vs/base/common/lifecycle.ts b/src/vs/base/common/lifecycle.ts index e04e62f76d38..53e133d37383 100644 --- a/src/vs/base/common/lifecycle.ts +++ b/src/vs/base/common/lifecycle.ts @@ -805,25 +805,27 @@ export class DisposableMap implements ID this._store.delete(key); } - /** - * Delete the value stored for `key` from this map but return it. The caller is - * responsible for disposing of the value. - */ - deleteAndLeak(key: K): V | undefined { - const value = this._store.get(key); - this._store.delete(key); - return value; - } - keys(): IterableIterator { return this._store.keys(); } - values(): IterableIterator { - return this._store.values(); - } - [Symbol.iterator](): IterableIterator<[K, V]> { return this._store[Symbol.iterator](); } } + +/** + * Call `then` on a Promise, unless the returned disposable is disposed. + */ +export function thenIfNotDisposed(promise: Promise, then: (result: T) => void): IDisposable { + let disposed = false; + promise.then(result => { + if (disposed) { + return; + } + then(result); + }); + return toDisposable(() => { + disposed = true; + }); +} diff --git a/src/vs/base/common/marked/marked.js b/src/vs/base/common/marked/marked.js index b7b6ecccd163..d3f5308839b1 100644 --- a/src/vs/base/common/marked/marked.js +++ b/src/vs/base/common/marked/marked.js @@ -1008,7 +1008,7 @@ const html = edit('^ {0,3}(?:' // optional indentation .replace('tag', _tag) .replace('attribute', / +[a-zA-Z:_][\w.:-]*(?: *= *"[^"\n]*"| *= *'[^'\n]*'| *= *[^\s"'=<>`]+)?/) .getRegex(); -const paragraph = edit(_paragraph) +const paragraph = edit(_paragraph, 'i') // <-- add 'i' flag for case-insensitivity .replace('hr', hr) .replace('heading', ' {0,3}#{1,6}(?:\\s|$)') .replace('|lheading', '') // setext headings don't interrupt commonmark paragraphs @@ -1045,7 +1045,7 @@ const blockNormal = { */ const gfmTable = edit('^ *([^\\n ].*)\\n' // Header + ' {0,3}((?:\\| *)?:?-+:? *(?:\\| *:?-+:? *)*(?:\\| *)?)' // Align - + '(?:\\n((?:(?! *\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)') // Cells + + '(?:\\n((?:(?! *\\n|hr|heading|blockquote|code|fences|list|html).*(?:\\n|$))*)\\n*|$)', 'i') // Cells: add case-insensitive flag .replace('hr', hr) .replace('heading', ' {0,3}#{1,6}(?:\\s|$)') .replace('blockquote', ' {0,3}>') @@ -1058,7 +1058,7 @@ const gfmTable = edit('^ *([^\\n ].*)\\n' // Header const blockGfm = { ...blockNormal, table: gfmTable, - paragraph: edit(_paragraph) + paragraph: edit(_paragraph, 'i') // add case-insensitive flag .replace('hr', hr) .replace('heading', ' {0,3}#{1,6}(?:\\s|$)') .replace('|lheading', '') // setext headings don't interrupt commonmark paragraphs @@ -1143,7 +1143,7 @@ const autolink = edit(/^<(scheme:[^\s\x00-\x1f<>]*|email)>/) .replace('scheme', /[a-zA-Z][a-zA-Z0-9+.-]{1,31}/) .replace('email', /[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+(@)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)+(?![-_])/) .getRegex(); -const _inlineComment = edit(_comment).replace('(?:-->|$)', '-->').getRegex(); +const _inlineComment = edit(_comment).replace('(?:-->|$)', '(?:-->|--!>|$)').getRegex(); const tag = edit('^comment' + '|^' // self-closing tag + '|^<[a-zA-Z][\\w-]*(?:attribute)*?\\s*/?>' // open tag diff --git a/src/vs/base/common/marshalling.ts b/src/vs/base/common/marshalling.ts index 82fcd1234d09..f32bbd68ff30 100644 --- a/src/vs/base/common/marshalling.ts +++ b/src/vs/base/common/marshalling.ts @@ -33,6 +33,10 @@ function replacer(key: string, value: any): any { return value; } +// Utility function to escape RegExp special characters +function escapeRegExp(string: string): string { + return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); // $& means the whole matched string +} type Deserialize = T extends UriComponents ? URI : T extends VSBuffer ? VSBuffer @@ -51,7 +55,7 @@ export function revive(obj: any, depth = 0): Revived { switch ((obj).$mid) { case MarshalledId.Uri: return URI.revive(obj); - case MarshalledId.Regexp: return new RegExp(obj.source, obj.flags); + case MarshalledId.Regexp: return new RegExp(escapeRegExp(obj.source), obj.flags); case MarshalledId.Date: return new Date(obj.source); } diff --git a/src/vs/base/common/network.ts b/src/vs/base/common/network.ts index e77c500097a6..16bbfe74115e 100644 --- a/src/vs/base/common/network.ts +++ b/src/vs/base/common/network.ts @@ -357,6 +357,9 @@ class FileAccessImpl { export const FileAccess = new FileAccessImpl(); +export const CacheControlheaders: Record = Object.freeze({ + 'Cache-Control': 'no-cache, no-store', +}); export namespace COI { diff --git a/src/vs/base/common/numbers.ts b/src/vs/base/common/numbers.ts index 594fcf63545d..6d3d90d5cd81 100644 --- a/src/vs/base/common/numbers.ts +++ b/src/vs/base/common/numbers.ts @@ -158,3 +158,8 @@ export const randomInt = (max: number, min: number = 0): number => { return Math.round(min + randomFloat); }; + +export function randomChance(p: number): boolean { + assert(p >= 0 && p <= 1, 'p must be between 0 and 1'); + return Math.random() < p; +} diff --git a/src/vs/base/common/objects.ts b/src/vs/base/common/objects.ts index 94c2fb717d2b..20b08dbe69fb 100644 --- a/src/vs/base/common/objects.ts +++ b/src/vs/base/common/objects.ts @@ -93,6 +93,10 @@ export function mixin(destination: any, source: any, overwrite: boolean = true): if (isObject(source)) { Object.keys(source).forEach(key => { + if (key === '__proto__' || key === 'constructor') { + // Prevent prototype pollution + return; + } if (key in destination) { if (overwrite) { if (isObject(destination[key]) && isObject(source[key])) { diff --git a/src/vs/base/common/observableInternal/autorun.ts b/src/vs/base/common/observableInternal/autorun.ts index 9a8200c0c5a4..03c37aa6c199 100644 --- a/src/vs/base/common/observableInternal/autorun.ts +++ b/src/vs/base/common/observableInternal/autorun.ts @@ -168,13 +168,13 @@ export const enum AutorunState { } export class AutorunObserver implements IObserver, IReader, IDisposable { - public _state = AutorunState.stale; - private updateCount = 0; - private disposed = false; - public _dependencies = new Set>(); - private dependenciesToBeRemoved = new Set>(); - private changeSummary: TChangeSummary | undefined; - public _isRunning = false; + private _state = AutorunState.stale; + private _updateCount = 0; + private _disposed = false; + private _dependencies = new Set>(); + private _dependenciesToBeRemoved = new Set>(); + private _changeSummary: TChangeSummary | undefined; + private _isRunning = false; public get debugName(): string { return this._debugNameData.getDebugName(this) ?? '(anonymous)'; @@ -186,15 +186,15 @@ export class AutorunObserver implements IObserver, IReader private readonly createChangeSummary: (() => TChangeSummary) | undefined, private readonly _handleChange: ((context: IChangeContext, summary: TChangeSummary) => boolean) | undefined, ) { - this.changeSummary = this.createChangeSummary?.(); + this._changeSummary = this.createChangeSummary?.(); getLogger()?.handleAutorunCreated(this); - this._runIfNeeded(); + this._run(); trackDisposable(this); } public dispose(): void { - this.disposed = true; + this._disposed = true; for (const o of this._dependencies) { o.removeObserver(this); // Warning: external call! } @@ -204,23 +204,19 @@ export class AutorunObserver implements IObserver, IReader markAsDisposed(this); } - private _runIfNeeded() { - if (this._state === AutorunState.upToDate) { - return; - } - - const emptySet = this.dependenciesToBeRemoved; - this.dependenciesToBeRemoved = this._dependencies; + private _run() { + const emptySet = this._dependenciesToBeRemoved; + this._dependenciesToBeRemoved = this._dependencies; this._dependencies = emptySet; this._state = AutorunState.upToDate; try { - if (!this.disposed) { + if (!this._disposed) { getLogger()?.handleAutorunStarted(this); - const changeSummary = this.changeSummary!; + const changeSummary = this._changeSummary!; try { - this.changeSummary = this.createChangeSummary?.(); // Warning: external call! + this._changeSummary = this.createChangeSummary?.(); // Warning: external call! this._isRunning = true; this._runFn(this, changeSummary); // Warning: external call! } catch (e) { @@ -230,15 +226,15 @@ export class AutorunObserver implements IObserver, IReader } } } finally { - if (!this.disposed) { + if (!this._disposed) { 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) { + for (const o of this._dependenciesToBeRemoved) { o.removeObserver(this); // Warning: external call! } - this.dependenciesToBeRemoved.clear(); + this._dependenciesToBeRemoved.clear(); } } @@ -251,12 +247,12 @@ export class AutorunObserver implements IObserver, IReader if (this._state === AutorunState.upToDate) { this._state = AutorunState.dependenciesMightHaveChanged; } - this.updateCount++; + this._updateCount++; } public endUpdate(_observable: IObservable): void { try { - if (this.updateCount === 1) { + if (this._updateCount === 1) { do { if (this._state === AutorunState.dependenciesMightHaveChanged) { this._state = AutorunState.upToDate; @@ -269,14 +265,16 @@ export class AutorunObserver implements IObserver, IReader } } - this._runIfNeeded(); // Warning: indirect external call! + if (this._state !== AutorunState.upToDate) { + this._run(); // Warning: indirect external call! + } } while (this._state !== AutorunState.upToDate); } } finally { - this.updateCount--; + this._updateCount--; } - assertFn(() => this.updateCount >= 0); + assertFn(() => this._updateCount >= 0); } public handlePossibleChange(observable: IObservable): void { @@ -294,7 +292,7 @@ export class AutorunObserver implements IObserver, IReader changedObservable: observable, change, didChange: (o): this is any => o === observable as any, - }, this.changeSummary!) : true; + }, this._changeSummary!) : true; if (shouldReact) { this._state = AutorunState.stale; } @@ -305,7 +303,7 @@ export class AutorunObserver implements IObserver, IReader } private _isDependency(observable: IObservableWithChange): boolean { - return this._dependencies.has(observable) && !this.dependenciesToBeRemoved.has(observable); + return this._dependencies.has(observable) && !this._dependenciesToBeRemoved.has(observable); } // IReader implementation @@ -314,16 +312,33 @@ export class AutorunObserver implements IObserver, IReader if (!this._isRunning) { throw new BugIndicatingError('The reader object cannot be used outside its compute function!'); } // In case the run action disposes the autorun - if (this.disposed) { + if (this._disposed) { return observable.get(); // warning: external call! } observable.addObserver(this); // warning: external call! const value = observable.get(); // warning: external call! this._dependencies.add(observable); - this.dependenciesToBeRemoved.delete(observable); + this._dependenciesToBeRemoved.delete(observable); return value; } + + public debugGetState() { + return { + isRunning: this._isRunning, + updateCount: this._updateCount, + dependencies: this._dependencies, + state: this._state, + }; + } + + public debugRerun(): void { + if (!this._isRunning) { + this._run(); + } else { + this._state = AutorunState.stale; + } + } } export namespace autorun { diff --git a/src/vs/base/common/observableInternal/base.ts b/src/vs/base/common/observableInternal/base.ts index 31ce4988a13b..4d7d4ea9e198 100644 --- a/src/vs/base/common/observableInternal/base.ts +++ b/src/vs/base/common/observableInternal/base.ts @@ -290,7 +290,7 @@ export abstract class ConvenientObservable implements IObservableWit } export abstract class BaseObservable extends ConvenientObservable { - public readonly _observers = new Set(); + protected readonly _observers = new Set(); constructor() { super(); @@ -329,6 +329,10 @@ export abstract class BaseObservable extends ConvenientObserv } return this; } + + public debugGetObservers() { + return this._observers; + } } /** @@ -385,7 +389,7 @@ export function subtransaction(tx: ITransaction | undefined, fn: (tx: ITransacti } export class TransactionImpl implements ITransaction { - public _updatingObservers: { observer: IObserver; observable: IObservable }[] | null = []; + private _updatingObservers: { observer: IObserver; observable: IObservable }[] | null = []; constructor(public readonly _fn: Function, private readonly _getDebugName?: () => string) { getLogger()?.handleBeginTransaction(this); @@ -414,6 +418,10 @@ export class TransactionImpl implements ITransaction { this._updatingObservers = null; getLogger()?.handleEndTransaction(this); } + + public debugGetUpdatingObservers() { + return this._updatingObservers; + } } /** @@ -495,6 +503,16 @@ export class ObservableValue protected _setValue(newValue: T): void { this._value = newValue; } + + public debugGetState() { + return { + value: this._value, + }; + } + + public debugSetValue(value: unknown) { + this._value = value as T; + } } /** diff --git a/src/vs/base/common/observableInternal/derived.ts b/src/vs/base/common/observableInternal/derived.ts index b8ff0a7fab02..59d8dc725a72 100644 --- a/src/vs/base/common/observableInternal/derived.ts +++ b/src/vs/base/common/observableInternal/derived.ts @@ -100,8 +100,8 @@ export function derivedHandleChanges( } export function derivedWithStore(computeFn: (reader: IReader, store: DisposableStore) => T): IObservable; -export function derivedWithStore(owner: object, computeFn: (reader: IReader, store: DisposableStore) => T): IObservable; -export function derivedWithStore(computeFnOrOwner: ((reader: IReader, store: DisposableStore) => T) | object, computeFnOrUndefined?: ((reader: IReader, store: DisposableStore) => T)): IObservable { +export function derivedWithStore(owner: DebugOwner, computeFn: (reader: IReader, store: DisposableStore) => T): IObservable; +export function derivedWithStore(computeFnOrOwner: ((reader: IReader, store: DisposableStore) => T) | DebugOwner, computeFnOrUndefined?: ((reader: IReader, store: DisposableStore) => T)): IObservable { let computeFn: (reader: IReader, store: DisposableStore) => T; let owner: DebugOwner; if (computeFnOrUndefined === undefined) { @@ -194,14 +194,14 @@ export const enum DerivedState { } export class Derived extends BaseObservable implements IReader, IObserver { - public _state = DerivedState.initial; - private value: T | undefined = undefined; - public _updateCount = 0; - public _dependencies = new Set>(); - private dependenciesToBeRemoved = new Set>(); - private changeSummary: TChangeSummary | undefined = undefined; + private _state = DerivedState.initial; + private _value: T | undefined = undefined; + private _updateCount = 0; + private _dependencies = new Set>(); + private _dependenciesToBeRemoved = new Set>(); + private _changeSummary: TChangeSummary | undefined = undefined; private _isUpdating = false; - public _isComputing = false; + private _isComputing = false; public override get debugName(): string { return this._debugNameData.getDebugName(this) ?? '(anonymous)'; @@ -216,7 +216,7 @@ export class Derived extends BaseObservable im private readonly _equalityComparator: EqualityComparer, ) { super(); - this.changeSummary = this.createChangeSummary?.(); + this._changeSummary = this.createChangeSummary?.(); } protected override onLastObserverRemoved(): void { @@ -225,7 +225,7 @@ export class Derived extends BaseObservable im * that our cache is invalid. */ this._state = DerivedState.initial; - this.value = undefined; + this._value = undefined; getLogger()?.handleDerivedCleared(this); for (const d of this._dependencies) { d.removeObserver(this); @@ -278,23 +278,22 @@ export class Derived extends BaseObservable im this._state = DerivedState.upToDate; } - this._recomputeIfNeeded(); + if (this._state !== DerivedState.upToDate) { + this._recompute(); + } // In case recomputation changed one of our dependencies, we need to recompute again. } while (this._state !== DerivedState.upToDate); - return this.value!; + return this._value!; } } - private _recomputeIfNeeded() { - if (this._state === DerivedState.upToDate) { - return; - } - const emptySet = this.dependenciesToBeRemoved; - this.dependenciesToBeRemoved = this._dependencies; + private _recompute() { + const emptySet = this._dependenciesToBeRemoved; + this._dependenciesToBeRemoved = this._dependencies; this._dependencies = emptySet; const hadValue = this._state !== DerivedState.initial; - const oldValue = this.value; + const oldValue = this._value; this._state = DerivedState.upToDate; let didChange = false; @@ -302,27 +301,27 @@ export class Derived extends BaseObservable im this._isComputing = true; try { - const changeSummary = this.changeSummary!; - this.changeSummary = this.createChangeSummary?.(); + const changeSummary = this._changeSummary!; + this._changeSummary = this.createChangeSummary?.(); try { this._isReaderValid = true; /** might call {@link handleChange} indirectly, which could invalidate us */ - this.value = this._computeFn(this, changeSummary); + this._value = this._computeFn(this, changeSummary); } finally { this._isReaderValid = false; // 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) { + for (const o of this._dependenciesToBeRemoved) { o.removeObserver(this); } - this.dependenciesToBeRemoved.clear(); + this._dependenciesToBeRemoved.clear(); } - didChange = hadValue && !(this._equalityComparator(oldValue!, this.value)); + didChange = hadValue && !(this._equalityComparator(oldValue!, this._value)); getLogger()?.handleObservableUpdated(this, { oldValue, - newValue: this.value, + newValue: this._value, change: undefined, didChange, hadValue, @@ -397,7 +396,7 @@ export class Derived extends BaseObservable im public handlePossibleChange(observable: IObservable): void { // In all other states, observers already know that we might have changed. - if (this._state === DerivedState.upToDate && this._dependencies.has(observable) && !this.dependenciesToBeRemoved.has(observable)) { + if (this._state === DerivedState.upToDate && this._dependencies.has(observable) && !this._dependenciesToBeRemoved.has(observable)) { this._state = DerivedState.dependenciesMightHaveChanged; for (const r of this._observers) { r.handlePossibleChange(this); @@ -406,7 +405,7 @@ export class Derived extends BaseObservable im } public handleChange(observable: IObservableWithChange, change: TChange): void { - if (this._dependencies.has(observable) && !this.dependenciesToBeRemoved.has(observable)) { + if (this._dependencies.has(observable) && !this._dependenciesToBeRemoved.has(observable)) { getLogger()?.handleDerivedDependencyChanged(this, observable, change); let shouldReact = false; @@ -415,7 +414,7 @@ export class Derived extends BaseObservable im changedObservable: observable, change, didChange: (o): this is any => o === observable as any, - }, this.changeSummary!) : true; + }, this._changeSummary!) : true; } catch (e) { onBugIndicatingError(e); } @@ -444,7 +443,7 @@ export class Derived extends BaseObservable im const value = observable.get(); // Which is why we only add the observable to the dependencies now. this._dependencies.add(observable); - this.dependenciesToBeRemoved.delete(observable); + this._dependenciesToBeRemoved.delete(observable); return value; } @@ -470,6 +469,21 @@ export class Derived extends BaseObservable im } super.removeObserver(observer); } + + public debugGetState() { + return { + state: this._state, + updateCount: this._updateCount, + isComputing: this._isComputing, + dependencies: this._dependencies, + value: this._value, + }; + } + + public debugSetValue(newValue: unknown) { + this._value = newValue as any; + } + } diff --git a/src/vs/base/common/observableInternal/index.ts b/src/vs/base/common/observableInternal/index.ts index 8fe37f4c48ac..c86501e702aa 100644 --- a/src/vs/base/common/observableInternal/index.ts +++ b/src/vs/base/common/observableInternal/index.ts @@ -16,6 +16,8 @@ export { type DebugOwner } from './debugName.js'; import { addLogger, setLogObservableFn } from './logging/logging.js'; import { ConsoleObservableLogger, logObservableToConsole } from './logging/consoleObservableLogger.js'; +import { DevToolsLogger } from './logging/debugger/devToolsLogger.js'; +import { env } from '../process.js'; setLogObservableFn(logObservableToConsole); @@ -27,3 +29,8 @@ const enableLogging = false if (enableLogging) { addLogger(new ConsoleObservableLogger()); } + +if (env && env['VSCODE_DEV_DEBUG']) { + // To debug observables you also need the extension "ms-vscode.debug-value-editor" + addLogger(DevToolsLogger.getInstance()); +} diff --git a/src/vs/base/common/observableInternal/logging/debugger/debuggerApi.d.ts b/src/vs/base/common/observableInternal/logging/debugger/debuggerApi.d.ts index e0b5fae18200..138732f44b21 100644 --- a/src/vs/base/common/observableInternal/logging/debugger/debuggerApi.d.ts +++ b/src/vs/base/common/observableInternal/logging/debugger/debuggerApi.d.ts @@ -23,6 +23,9 @@ export type ObsDebuggerApi = { getSummarizedInstances(): IObsPushState; getDerivedInfo(instanceId: ObsInstanceId): IDerivedObservableDetailedInfo; getAutorunInfo(instanceId: ObsInstanceId): IAutorunDetailedInfo; + getObservableValueInfo(instanceId: ObsInstanceId): IObservableValueInfo; + setValue(instanceId: ObsInstanceId, jsonValue: unknown): void; + getValue(instanceId: ObsInstanceId): unknown; getTransactionState(): ITransactionState | undefined; } @@ -94,6 +97,10 @@ export type ObsStateUpdate = Partial & DeepPartial = { [TKey in keyof T]?: DeepPartial }; +export interface IObservableValueInfo { + observers: IObsInstanceRef[]; +} + export interface IDerivedObservableDetailedInfo { dependencies: IObsInstanceRef[]; observers: IObsInstanceRef[]; diff --git a/src/vs/base/common/observableInternal/logging/debugger/devToolsLogger.ts b/src/vs/base/common/observableInternal/logging/debugger/devToolsLogger.ts index a85329ca8c98..208c623cf7e4 100644 --- a/src/vs/base/common/observableInternal/logging/debugger/devToolsLogger.ts +++ b/src/vs/base/common/observableInternal/logging/debugger/devToolsLogger.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { AutorunObserver, AutorunState } from '../../autorun.js'; -import { IObservable, IObserver, TransactionImpl } from '../../base.js'; +import { BaseObservable, IObservable, IObserver, ObservableValue, TransactionImpl } from '../../base.js'; import { Derived, DerivedState } from '../../derived.js'; import { IChangeInformation, IObservableLogger } from '../logging.js'; import { formatValue } from '../consoleObservableLogger.js'; @@ -12,6 +12,8 @@ import { ObsDebuggerApi, IObsDeclaration, ObsInstanceId, ObsStateUpdate, ITransa import { registerDebugChannel } from './debuggerRpc.js'; import { deepAssign, deepAssignDeleteNulls, getFirstStackFrameOutsideOf, ILocation, Throttler } from './utils.js'; import { isDefined } from '../../../types.js'; +import { FromEventObservable } from '../../utils.js'; +import { BugIndicatingError, onUnexpectedError } from '../../../errors.js'; interface IInstanceInfo { declarationId: number; @@ -75,21 +77,61 @@ export class DevToolsLogger implements IObservableLogger { getSummarizedInstances: () => { return null!; }, + getObservableValueInfo: instanceId => { + const obs = this._aliveInstances.get(instanceId) as BaseObservable; + return { + observers: [...obs.debugGetObservers()].map(d => this._formatObserver(d)).filter(isDefined), + }; + }, getDerivedInfo: instanceId => { const d = this._aliveInstances.get(instanceId) as Derived; return { - dependencies: [...d._dependencies].map(d => this._formatObservable(d)), - observers: [...d._observers].map(d => this._formatObserver(d)).filter(isDefined), + dependencies: [...d.debugGetState().dependencies].map(d => this._formatObservable(d)).filter(isDefined), + observers: [...d.debugGetObservers()].map(d => this._formatObserver(d)).filter(isDefined), }; }, getAutorunInfo: instanceId => { const obs = this._aliveInstances.get(instanceId) as AutorunObserver; return { - dependencies: [...obs._dependencies].map(d => this._formatObservable(d)), + dependencies: [...obs.debugGetState().dependencies].map(d => this._formatObservable(d)).filter(isDefined), }; }, getTransactionState: () => { return this.getTransactionState(); + }, + setValue: (instanceId, jsonValue) => { + const obs = this._aliveInstances.get(instanceId) as BaseObservable; + + if (obs instanceof Derived) { + obs.debugSetValue(jsonValue); + } else if (obs instanceof ObservableValue) { + obs.debugSetValue(jsonValue); + } else if (obs instanceof FromEventObservable) { + obs.debugSetValue(jsonValue); + } else { + throw new BugIndicatingError('Observable is not supported'); + } + + const observers = [...obs.debugGetObservers()]; + for (const d of observers) { + d.beginUpdate(obs); + } + for (const d of observers) { + d.handleChange(obs, undefined); + } + for (const d of observers) { + d.endUpdate(obs); + } + }, + getValue: instanceId => { + const obs = this._aliveInstances.get(instanceId) as BaseObservable; + if (obs instanceof Derived) { + return formatValue(obs.debugGetState().value, 200); + } else if (obs instanceof ObservableValue) { + return formatValue(obs.debugGetState().value, 200); + } + + return undefined; } } }; @@ -101,7 +143,7 @@ export class DevToolsLogger implements IObservableLogger { if (txs.length === 0) { return undefined; } - const observerQueue = txs.flatMap(t => t._updatingObservers ?? []).map(o => o.observer); + const observerQueue = txs.flatMap(t => t.debugGetUpdatingObservers() ?? []).map(o => o.observer); const processedObservers = new Set(); while (observerQueue.length > 0) { const observer = observerQueue.shift()!; @@ -124,36 +166,42 @@ export class DevToolsLogger implements IObservableLogger { return { names: txs.map(t => t.getDebugName() ?? 'tx'), affected }; } - private _getObservableInfo(observable: IObservable): IObservableInfo { + private _getObservableInfo(observable: IObservable): IObservableInfo | undefined { const info = this._instanceInfos.get(observable); if (!info) { - throw new Error('No info found'); + onUnexpectedError(new BugIndicatingError('No info found')); + return undefined; } return info as IObservableInfo; } - private _getAutorunInfo(autorun: AutorunObserver): IAutorunInfo { + private _getAutorunInfo(autorun: AutorunObserver): IAutorunInfo | undefined { const info = this._instanceInfos.get(autorun); if (!info) { - throw new Error('No info found'); + onUnexpectedError(new BugIndicatingError('No info found')); + return undefined; } return info as IAutorunInfo; } private _getInfo(observer: IObserver, queue: (observer: IObserver) => void): ObserverInstanceState | undefined { if (observer instanceof Derived) { - const observersToUpdate = [...observer._observers]; + const observersToUpdate = [...observer.debugGetObservers()]; for (const o of observersToUpdate) { queue(o); } - const info = this._getObservableInfo(observer)!; - const base = { name: observer.debugName, instanceId: info.instanceId, updateCount: observer._updateCount }; - const changedDependencies = [...info.changedObservables].map(o => this._instanceInfos.get(o)!.instanceId); - if (observer._isComputing) { + const info = this._getObservableInfo(observer); + if (!info) { return; } + + const observerState = observer.debugGetState(); + + const base = { name: observer.debugName, instanceId: info.instanceId, updateCount: observerState.updateCount }; + const changedDependencies = [...info.changedObservables].map(o => this._instanceInfos.get(o)?.instanceId).filter(isDefined); + if (observerState.isComputing) { return { ...base, type: 'observable/derived', state: 'updating', changedDependencies, initialComputation: false }; } - switch (observer._state) { + switch (observerState.state) { case DerivedState.initial: return { ...base, type: 'observable/derived', state: 'noValue' }; case DerivedState.upToDate: @@ -164,13 +212,15 @@ export class DevToolsLogger implements IObservableLogger { return { ...base, type: 'observable/derived', state: 'possiblyStale' }; } } else if (observer instanceof AutorunObserver) { - const info = this._getAutorunInfo(observer)!; + const info = this._getAutorunInfo(observer); + if (!info) { return undefined; } + const base = { name: observer.debugName, instanceId: info.instanceId, updateCount: info.updateCount }; const changedDependencies = [...info.changedObservables].map(o => this._instanceInfos.get(o)!.instanceId); - if (observer._isRunning) { + if (observer.debugGetState().isRunning) { return { ...base, type: 'autorun', state: 'updating', changedDependencies }; } - switch (observer._state) { + switch (observer.debugGetState().state) { case AutorunState.upToDate: return { ...base, type: 'autorun', state: 'upToDate' }; case AutorunState.stale: @@ -183,8 +233,10 @@ export class DevToolsLogger implements IObservableLogger { return undefined; } - private _formatObservable(obs: IObservable): { name: string; instanceId: ObsInstanceId } { - return { name: obs.debugName, instanceId: this._getObservableInfo(obs)?.instanceId! }; + private _formatObservable(obs: IObservable): { name: string; instanceId: ObsInstanceId } | undefined { + const info = this._getObservableInfo(obs); + if (!info) { return undefined; } + return { name: obs.debugName, instanceId: info.instanceId }; } private _formatObserver(obs: IObserver): { name: string; instanceId: ObsInstanceId } | undefined { @@ -230,16 +282,18 @@ export class DevToolsLogger implements IObservableLogger { let shallow = true; let loc!: ILocation; + const Err = Error as any as { stackTraceLimit: number }; // For the monaco editor checks, which don't have the nodejs types. + while (true) { - const l = Error.stackTraceLimit; - Error.stackTraceLimit = shallow ? 6 : 20; + const l = Err.stackTraceLimit; + Err.stackTraceLimit = shallow ? 6 : 20; const stack = new Error().stack!; - Error.stackTraceLimit = l; + Err.stackTraceLimit = l; - let result = getFirstStackFrameOutsideOf(stack, /[/\\]observableInternal[/\\]|[/\\]util(s)?\./); + let result = getFirstStackFrameOutsideOf(stack, /[/\\]observableInternal[/\\]|\.observe|[/\\]util(s)?\./); if (!shallow && !result) { - result = getFirstStackFrameOutsideOf(stack, /[/\\]observableInternal[/\\]/)!; + result = getFirstStackFrameOutsideOf(stack, /[/\\]observableInternal[/\\]|\.observe/)!; } if (result) { loc = result; @@ -248,6 +302,7 @@ export class DevToolsLogger implements IObservableLogger { if (!shallow) { console.error('Could not find location for declaration', new Error().stack); loc = { fileName: 'unknown', line: 0, column: 0, id: 'unknown' }; + break; } shallow = false; } @@ -284,30 +339,30 @@ export class DevToolsLogger implements IObservableLogger { handleOnListenerCountChanged(observable: IObservable, newCount: number): void { const info = this._getObservableInfo(observable); - if (info) { - if (info.listenerCount === 0 && newCount > 0) { - const type: IObsDeclaration['type'] = - observable instanceof Derived ? 'observable/derived' : 'observable/value'; - this._aliveInstances.set(info.instanceId, observable); - this._handleChange({ - instances: { - [info.instanceId]: { - instanceId: info.instanceId, - declarationId: info.declarationId, - formattedValue: info.lastValue, - type, - name: observable.debugName, - } + if (!info) { return; } + + if (info.listenerCount === 0 && newCount > 0) { + const type: IObsDeclaration['type'] = + observable instanceof Derived ? 'observable/derived' : 'observable/value'; + this._aliveInstances.set(info.instanceId, observable); + this._handleChange({ + instances: { + [info.instanceId]: { + instanceId: info.instanceId, + declarationId: info.declarationId, + formattedValue: info.lastValue, + type, + name: observable.debugName, } - }); - } else if (info.listenerCount > 0 && newCount === 0) { - this._handleChange({ - instances: { [info.instanceId]: null } - }); - this._aliveInstances.delete(info.instanceId); - } - info.listenerCount = newCount; + } + }); + } else if (info.listenerCount > 0 && newCount === 0) { + this._handleChange({ + instances: { [info.instanceId]: null } + }); + this._aliveInstances.delete(info.instanceId); } + info.listenerCount = newCount; } handleObservableUpdated(observable: IObservable, changeInfo: IChangeInformation): void { @@ -355,32 +410,32 @@ export class DevToolsLogger implements IObservableLogger { } handleAutorunDisposed(autorun: AutorunObserver): void { const info = this._getAutorunInfo(autorun); - if (info) { - this._handleChange({ - instances: { [info.instanceId]: null } - }); - this._instanceInfos.delete(autorun); - this._aliveInstances.delete(info.instanceId); - } + if (!info) { return; } + + this._handleChange({ + instances: { [info.instanceId]: null } + }); + this._instanceInfos.delete(autorun); + this._aliveInstances.delete(info.instanceId); } handleAutorunDependencyChanged(autorun: AutorunObserver, observable: IObservable, change: unknown): void { const info = this._getAutorunInfo(autorun); - if (info) { - info.changedObservables.add(observable); - } + if (!info) { return; } + + info.changedObservables.add(observable); } handleAutorunStarted(autorun: AutorunObserver): void { } handleAutorunFinished(autorun: AutorunObserver): void { const info = this._getAutorunInfo(autorun); - if (info) { - info.changedObservables.clear(); - info.updateCount++; - this._handleChange({ - instances: { [info.instanceId]: { runCount: info.updateCount } } - }); - } + if (!info) { return; } + + info.changedObservables.clear(); + info.updateCount++; + this._handleChange({ + instances: { [info.instanceId]: { runCount: info.updateCount } } + }); } handleDerivedDependencyChanged(derived: Derived, observable: IObservable, change: unknown): void { @@ -391,33 +446,33 @@ export class DevToolsLogger implements IObservableLogger { } _handleDerivedRecomputed(observable: Derived, changeInfo: IChangeInformation): void { const info = this._getObservableInfo(observable); - if (info) { - const formattedValue = formatValue(changeInfo.newValue, 30); - info.updateCount++; - info.changedObservables.clear(); - - info.lastValue = formattedValue; - if (info.listenerCount > 0) { - this._handleChange({ - instances: { [info.instanceId]: { formattedValue: formattedValue, recomputationCount: info.updateCount } } - }); - } + if (!info) { return; } + + const formattedValue = formatValue(changeInfo.newValue, 30); + info.updateCount++; + info.changedObservables.clear(); + + info.lastValue = formattedValue; + if (info.listenerCount > 0) { + this._handleChange({ + instances: { [info.instanceId]: { formattedValue: formattedValue, recomputationCount: info.updateCount } } + }); } } handleDerivedCleared(observable: Derived): void { const info = this._getObservableInfo(observable); - if (info) { - info.lastValue = undefined; - info.changedObservables.clear(); - if (info.listenerCount > 0) { - this._handleChange({ - instances: { - [info.instanceId]: { - formattedValue: undefined, - } + if (!info) { return; } + + info.lastValue = undefined; + info.changedObservables.clear(); + if (info.listenerCount > 0) { + this._handleChange({ + instances: { + [info.instanceId]: { + formattedValue: undefined, } - }); - } + } + }); } } handleBeginTransaction(transaction: TransactionImpl): void { diff --git a/src/vs/base/common/observableInternal/logging/debugger/utils.ts b/src/vs/base/common/observableInternal/logging/debugger/utils.ts index 2648b82cffdd..10105b8f209f 100644 --- a/src/vs/base/common/observableInternal/logging/debugger/utils.ts +++ b/src/vs/base/common/observableInternal/logging/debugger/utils.ts @@ -96,6 +96,9 @@ export class Throttler implements IDisposable { export function deepAssign(target: T, source: T): void { for (const key in source) { + if (key === '__proto__' || key === 'constructor' || key === 'prototype') { + continue; + } if (!!target[key] && typeof target[key] === 'object' && !!source[key] && typeof source[key] === 'object') { deepAssign(target[key], source[key]); } else { @@ -106,6 +109,9 @@ export function deepAssign(target: T, source: T): void { export function deepAssignDeleteNulls(target: T, source: T): void { for (const key in source) { + + continue; + } if (source[key] === null) { delete target[key]; } else if (!!target[key] && typeof target[key] === 'object' && !!source[key] && typeof source[key] === 'object') { diff --git a/src/vs/base/common/observableInternal/utils.ts b/src/vs/base/common/observableInternal/utils.ts index 3b5bf7d35bfe..530474b334ea 100644 --- a/src/vs/base/common/observableInternal/utils.ts +++ b/src/vs/base/common/observableInternal/utils.ts @@ -102,9 +102,9 @@ export function observableFromEventOpts( export class FromEventObservable extends BaseObservable { public static globalTransaction: ITransaction | undefined; - private value: T | undefined; - private hasValue = false; - private subscription: IDisposable | undefined; + private _value: T | undefined; + private _hasValue = false; + private _subscription: IDisposable | undefined; constructor( private readonly _debugNameData: DebugNameData, @@ -126,25 +126,25 @@ export class FromEventObservable extends BaseObservable { } protected override onFirstObserverAdded(): void { - this.subscription = this.event(this.handleEvent); + this._subscription = this.event(this.handleEvent); } private readonly handleEvent = (args: TArgs | undefined) => { const newValue = this._getValue(args); - const oldValue = this.value; + const oldValue = this._value; - const didChange = !this.hasValue || !(this._equalityComparator(oldValue!, newValue)); + const didChange = !this._hasValue || !(this._equalityComparator(oldValue!, newValue)); let didRunTransaction = false; if (didChange) { - this.value = newValue; + this._value = newValue; - if (this.hasValue) { + if (this._hasValue) { didRunTransaction = true; subtransaction( this._getTransaction(), (tx) => { - getLogger()?.handleObservableUpdated(this, { oldValue, newValue, change: undefined, didChange, hadValue: this.hasValue }); + getLogger()?.handleObservableUpdated(this, { oldValue, newValue, change: undefined, didChange, hadValue: this._hasValue }); for (const o of this._observers) { tx.updateObserver(o, this); @@ -157,33 +157,37 @@ export class FromEventObservable extends BaseObservable { } ); } - this.hasValue = true; + this._hasValue = true; } if (!didRunTransaction) { - getLogger()?.handleObservableUpdated(this, { oldValue, newValue, change: undefined, didChange, hadValue: this.hasValue }); + getLogger()?.handleObservableUpdated(this, { oldValue, newValue, change: undefined, didChange, hadValue: this._hasValue }); } }; protected override onLastObserverRemoved(): void { - this.subscription!.dispose(); - this.subscription = undefined; - this.hasValue = false; - this.value = undefined; + this._subscription!.dispose(); + this._subscription = undefined; + this._hasValue = false; + this._value = undefined; } public get(): T { - if (this.subscription) { - if (!this.hasValue) { + if (this._subscription) { + if (!this._hasValue) { this.handleEvent(undefined); } - return this.value!; + return this._value!; } else { // no cache, as there are no subscribers to keep it updated const value = this._getValue(undefined); return value; } } + + public debugSetValue(value: unknown) { + this._value = value as any; + } } export namespace observableFromEvent { diff --git a/src/vs/base/common/path.ts b/src/vs/base/common/path.ts index 29621411b780..14a0dcda30ad 100644 --- a/src/vs/base/common/path.ts +++ b/src/vs/base/common/path.ts @@ -4,9 +4,11 @@ *--------------------------------------------------------------------------------------------*/ // NOTE: VSCode's copy of nodejs path library to be usable in common (non-node) namespace -// Copied from: https://github.com/nodejs/node/commits/v20.9.0/lib/path.js +// Copied from: https://github.com/nodejs/node/commits/v20.18.2/lib/path.js // Excluding: the change that adds primordials // (https://github.com/nodejs/node/commit/187a862d221dec42fa9a5c4214e7034d9092792f and others) +// Excluding: the change that adds glob matching +// (https://github.com/nodejs/node/commit/57b8b8e18e5e2007114c63b71bf0baedc01936a6) /** * Copyright Joyent, Inc. and other Node contributors. @@ -425,6 +427,23 @@ export const win32: IPath = { if (tail.length > 0 && isPathSeparator(path.charCodeAt(len - 1))) { tail += '\\'; } + if (!isAbsolute && device === undefined && path.includes(':')) { + // If the original path was not absolute and if we have not been able to + // resolve it relative to a particular device, we need to ensure that the + // `tail` has not become something that Windows might interpret as an + // absolute path. See CVE-2024-36139. + if (tail.length >= 2 && + isWindowsDeviceRoot(tail.charCodeAt(0)) && + tail.charCodeAt(1) === CHAR_COLON) { + return `.\\${tail}`; + } + let index = path.indexOf(':'); + do { + if (index === len - 1 || isPathSeparator(path.charCodeAt(index + 1))) { + return `.\\${tail}`; + } + } while ((index = path.indexOf(':', index + 1)) !== -1); + } if (device === undefined) { return isAbsolute ? `\\${tail}` : tail; } @@ -544,6 +563,42 @@ export const win32: IPath = { return ''; } + if (fromOrig.length !== from.length || toOrig.length !== to.length) { + const fromSplit = fromOrig.split('\\'); + const toSplit = toOrig.split('\\'); + if (fromSplit[fromSplit.length - 1] === '') { + fromSplit.pop(); + } + if (toSplit[toSplit.length - 1] === '') { + toSplit.pop(); + } + + const fromLen = fromSplit.length; + const toLen = toSplit.length; + const length = fromLen < toLen ? fromLen : toLen; + + let i; + for (i = 0; i < length; i++) { + if (fromSplit[i].toLowerCase() !== toSplit[i].toLowerCase()) { + break; + } + } + + if (i === 0) { + return toOrig; + } else if (i === length) { + if (toLen > length) { + return toSplit.slice(i).join('\\'); + } + if (fromLen > length) { + return '..\\'.repeat(fromLen - 1 - i) + '..'; + } + return ''; + } + + return '..\\'.repeat(fromLen - i) + toSplit.slice(i).join('\\'); + } + // Trim any leading backslashes let fromStart = 0; while (fromStart < from.length && @@ -672,7 +727,7 @@ export const win32: IPath = { return `\\\\?\\${resolvedPath}`; } - return path; + return resolvedPath; }, dirname(path: string): string { @@ -1098,9 +1153,8 @@ export const posix: IPath = { let resolvedPath = ''; let resolvedAbsolute = false; - for (let i = pathSegments.length - 1; i >= -1 && !resolvedAbsolute; i--) { - const path = i >= 0 ? pathSegments[i] : posixCwd(); - + for (let i = pathSegments.length - 1; i >= 0 && !resolvedAbsolute; i--) { + const path = pathSegments[i]; validateString(path, `paths[${i}]`); // Skip empty entries @@ -1112,6 +1166,13 @@ export const posix: IPath = { resolvedAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH; } + if (!resolvedAbsolute) { + const cwd = posixCwd(); + resolvedPath = `${cwd}/${resolvedPath}`; + resolvedAbsolute = + cwd.charCodeAt(0) === CHAR_FORWARD_SLASH; + } + // At this point the path should be resolved to a full absolute path, but // handle relative paths to be safe (might happen when process.cwd() fails) @@ -1161,22 +1222,21 @@ export const posix: IPath = { if (paths.length === 0) { return '.'; } - let joined; + + const path = []; for (let i = 0; i < paths.length; ++i) { const arg = paths[i]; validateString(arg, 'path'); if (arg.length > 0) { - if (joined === undefined) { - joined = arg; - } else { - joined += `/${arg}`; - } + path.push(arg); } } - if (joined === undefined) { + + if (path.length === 0) { return '.'; } - return posix.normalize(joined); + + return posix.normalize(path.join('/')); }, relative(from: string, to: string): string { @@ -1288,7 +1348,7 @@ export const posix: IPath = { basename(path: string, suffix?: string): string { if (suffix !== undefined) { - validateString(suffix, 'ext'); + validateString(suffix, 'suffix'); } validateString(path, 'path'); @@ -1376,8 +1436,8 @@ export const posix: IPath = { // after any path separator we find let preDotState = 0; for (let i = path.length - 1; i >= 0; --i) { - const code = path.charCodeAt(i); - if (code === CHAR_FORWARD_SLASH) { + const char = path[i]; + if (char === '/') { // If we reached a path separator that was not part of a set of path // separators at the end of the string, stop now if (!matchedSlash) { @@ -1392,7 +1452,7 @@ export const posix: IPath = { matchedSlash = false; end = i + 1; } - if (code === CHAR_DOT) { + if (char === '.') { // If this is our first dot, mark it as the start of our extension if (startDot === -1) { startDot = i; diff --git a/src/vs/base/common/product.ts b/src/vs/base/common/product.ts index 52c4c943d97d..0527af13b60a 100644 --- a/src/vs/base/common/product.ts +++ b/src/vs/base/common/product.ts @@ -95,11 +95,9 @@ export interface IProductConfiguration { readonly extensionsGallery?: { readonly serviceUrl: string; - readonly itemUrl: string; - readonly publisherUrl: string; - readonly resourceUrlTemplate: string; - readonly extensionUrlTemplate: string; readonly controlUrl: string; + readonly extensionUrlTemplate: string; + readonly resourceUrlTemplate: string; readonly nlsBaseUrl: string; }; @@ -188,6 +186,7 @@ export interface IProductConfiguration { readonly 'editSessions.store'?: Omit; readonly darwinUniversalAssetId?: string; + readonly darwinBundleIdentifier?: string; readonly profileTemplatesUrl?: string; readonly commonlyUsedSettings?: string[]; @@ -319,10 +318,21 @@ export interface IDefaultChatAgent { readonly providerName: string; readonly enterpriseProviderId: string; readonly enterpriseProviderName: string; - readonly providerSetting: string; readonly providerUriSetting: string; readonly providerScopes: string[][]; readonly entitlementUrl: string; readonly entitlementSignupLimitedUrl: string; + + readonly chatQuotaExceededContext: string; + readonly completionsQuotaExceededContext: string; + + readonly walkthroughCommand: string; + readonly completionsMenuCommand: string; + readonly completionsRefreshTokenCommand: string; + readonly chatRefreshTokenCommand: string; + + readonly completionsAdvancedSetting: string; + readonly completionsEnablementSetting: string; + readonly nextEditSuggestionsSetting: string; } diff --git a/src/vs/base/common/resources.ts b/src/vs/base/common/resources.ts index 3b8370c160b1..e874198b2d3b 100644 --- a/src/vs/base/common/resources.ts +++ b/src/vs/base/common/resources.ts @@ -3,13 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CharCode } from './charCode.js'; -import * as extpath from './extpath.js'; -import { Schemas } from './network.js'; -import * as paths from './path.js'; -import { isLinux, isWindows } from './platform.js'; -import { compare as strCompare, equalsIgnoreCase } from './strings.js'; -import { URI, uriToFsPath } from './uri.js'; +import * as extpath from 'vs/base/common/extpath'; +import * as paths from 'vs/base/common/path'; +import { URI, uriToFsPath } from 'vs/base/common/uri'; +import { equalsIgnoreCase, compare as strCompare } from 'vs/base/common/strings'; +import { Schemas } from 'vs/base/common/network'; +import { isLinux } from 'vs/base/common/platform'; +import { CharCode } from 'vs/base/common/charCode'; +import { ParsedExpression, IExpression, parse } from 'vs/base/common/glob'; +import { TernarySearchTree } from 'vs/base/common/map'; export function originalFSPath(uri: URI): string { return uriToFsPath(uri, true); @@ -236,12 +238,10 @@ export class ExtUri implements IExtUri { if (from.scheme !== to.scheme || !isEqualAuthority(from.authority, to.authority)) { return undefined; } - if (from.scheme === Schemas.file) { - const relativePath = paths.relative(originalFSPath(from), originalFSPath(to)); - return isWindows ? extpath.toSlashes(relativePath) : relativePath; + let fromPath = from.path || '/', toPath = to.path || '/'; + if (getWindowsDriveLetter(fromPath) !== getWindowsDriveLetter(toPath)) { + return undefined; // no relative path possible if the drive letter doesn't match } - let fromPath = from.path || '/'; - const toPath = to.path || '/'; if (this._ignorePathCasing(from)) { // make casing of fromPath match toPath let i = 0; @@ -396,6 +396,17 @@ export function distinctParents(items: T[], resourceAccessor: (item: T) => UR return distinctParents; } +/** + * Given a URI path (not a fs path!), tests if the path looks like a Window path with drive letter and returns the lowercase variant of that drive letter. + * @param path returns the drive letter (lower case) or undefined if the path does not look like a windows path + */ +function getWindowsDriveLetter(path: string): string | undefined { + if (/^\/[a-zA-Z]:(\/|$)/.test(path)) { + return path.charAt(1).toLowerCase(); + } + return undefined; +} + /** * Data URI related helpers. */ diff --git a/src/vs/base/common/strings.ts b/src/vs/base/common/strings.ts index 9ed7eaa47326..1a8467170b55 100644 --- a/src/vs/base/common/strings.ts +++ b/src/vs/base/common/strings.ts @@ -802,14 +802,20 @@ export function rcut(text: string, n: number, suffix = ''): string { return result.trim().replace(/b$/, '') + suffix; } -// Escape codes, compiled from https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Functions-using-CSI-_-ordered-by-the-final-character_s_ -// Plus additional markers for custom `\x1b]...\x07` instructions. -const CSI_SEQUENCE = /(?:(?:\x1b\[|\x9B)[=?>!]?[\d;:]*["$#'* ]?[a-zA-Z@^`{}|~])|(:?\x1b\].*?\x07)/g; +// Defacto standard: https://invisible-island.net/xterm/ctlseqs/ctlseqs.html +const CSI_SEQUENCE = /(?:\x1b\[|\x9b)[=?>!]?[\d;:]*["$#'* ]?[a-zA-Z@^`{}|~]/; +const OSC_SEQUENCE = /(?:\x1b\]|\x9d).*?(?:\x1b\\|\x07|\x9c)/; +const ESC_SEQUENCE = /\x1b(?:[ #%\(\)\*\+\-\.\/]?[a-zA-Z0-9\|}~@])/; +const CONTROL_SEQUENCES = new RegExp('(?:' + [ + CSI_SEQUENCE.source, + OSC_SEQUENCE.source, + ESC_SEQUENCE.source, +].join('|') + ')', 'g'); /** Iterates over parts of a string with CSI sequences */ export function* forAnsiStringParts(str: string) { let last = 0; - for (const match of str.matchAll(CSI_SEQUENCE)) { + for (const match of str.matchAll(CONTROL_SEQUENCES)) { if (last !== match.index) { yield { isCode: false, str: str.substring(last, match.index) }; } @@ -833,7 +839,7 @@ export function* forAnsiStringParts(str: string) { */ export function removeAnsiEscapeCodes(str: string): string { if (str) { - str = str.replace(CSI_SEQUENCE, ''); + str = str.replace(CONTROL_SEQUENCES, ''); } return str; diff --git a/src/vs/base/common/uuid.ts b/src/vs/base/common/uuid.ts index d1d08064f5af..8aa1e8801db2 100644 --- a/src/vs/base/common/uuid.ts +++ b/src/vs/base/common/uuid.ts @@ -10,4 +10,56 @@ export function isUUID(value: string): boolean { return _UUIDPattern.test(value); } -export const generateUuid: () => string = crypto.randomUUID.bind(crypto); +export const generateUuid = (function (): () => string { + + // use `randomUUID` if possible + if (typeof crypto.randomUUID === 'function') { + // see https://developer.mozilla.org/en-US/docs/Web/API/Window/crypto + // > Although crypto is available on all windows, the returned Crypto object only has one + // > usable feature in insecure contexts: the getRandomValues() method. + // > In general, you should use this API only in secure contexts. + + return crypto.randomUUID.bind(crypto); + } + + // prep-work + const _data = new Uint8Array(16); + const _hex: string[] = []; + for (let i = 0; i < 256; i++) { + _hex.push(i.toString(16).padStart(2, '0')); + } + + return function generateUuid(): string { + // get data + crypto.getRandomValues(_data); + + // set version bits + _data[6] = (_data[6] & 0x0f) | 0x40; + _data[8] = (_data[8] & 0x3f) | 0x80; + + // print as string + let i = 0; + let result = ''; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += '-'; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += '-'; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += '-'; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += '-'; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + result += _hex[_data[i++]]; + return result; + }; +})(); diff --git a/src/vs/base/node/powershell.ts b/src/vs/base/node/powershell.ts index 05d68e3cefd0..63d230f56bca 100644 --- a/src/vs/base/node/powershell.ts +++ b/src/vs/base/node/powershell.ts @@ -226,6 +226,13 @@ function findPSCoreDotnetGlobalTool(): IPossiblePowerShellExe { return new PossiblePowerShellExe(dotnetGlobalToolExePath, '.NET Core PowerShell Global Tool'); } +function findPSCoreScoopInstallation(): IPossiblePowerShellExe { + const scoopAppsDir = path.join(os.homedir(), 'scoop', 'apps'); + const scoopPwsh = path.join(scoopAppsDir, 'pwsh', 'current', 'pwsh.exe'); + + return new PossiblePowerShellExe(scoopPwsh, 'PowerShell (Scoop)'); +} + function findWinPS(): IPossiblePowerShellExe | null { const winPSPath = path.join( process.env.windir!, @@ -285,6 +292,11 @@ async function* enumerateDefaultPowerShellInstallations(): AsyncIterable(this.port, 'message', (e: MessageEvent) => { - if (e.data) { - return VSBuffer.wrap(e.data); - } - return VSBuffer.alloc(0); - }); + readonly onMessage; constructor(private port: MessagePort) { - + this.onMessage = Event.fromDOMEventEmitter(this.port, 'message', (e: MessageEvent) => { + if (e.data) { + return VSBuffer.wrap(e.data); + } + return VSBuffer.alloc(0); + }); // we must call start() to ensure messages are flowing port.start(); } diff --git a/src/vs/base/parts/ipc/node/ipc.mp.ts b/src/vs/base/parts/ipc/node/ipc.mp.ts index 87e1016f045f..df9cd52c58ae 100644 --- a/src/vs/base/parts/ipc/node/ipc.mp.ts +++ b/src/vs/base/parts/ipc/node/ipc.mp.ts @@ -15,15 +15,15 @@ import { assertType } from '../../../common/types.js'; */ class Protocol implements IMessagePassingProtocol { - readonly onMessage = Event.fromNodeEventEmitter(this.port, 'message', (e: MessageEvent) => { - if (e.data) { - return VSBuffer.wrap(e.data); - } - return VSBuffer.alloc(0); - }); + readonly onMessage; constructor(private port: MessagePortMain) { - + this.onMessage = Event.fromNodeEventEmitter(this.port, 'message', (e: MessageEvent) => { + if (e.data) { + return VSBuffer.wrap(e.data); + } + return VSBuffer.alloc(0); + }); // we must call start() to ensure messages are flowing port.start(); } diff --git a/src/vs/base/parts/storage/node/storage.ts b/src/vs/base/parts/storage/node/storage.ts index 2def700f2b05..bf674966c0a0 100644 --- a/src/vs/base/parts/storage/node/storage.ts +++ b/src/vs/base/parts/storage/node/storage.ts @@ -38,13 +38,20 @@ export class SQLiteStorageDatabase implements IStorageDatabase { private static readonly BUSY_OPEN_TIMEOUT = 2000; // timeout in ms to retry when opening DB fails with SQLITE_BUSY private static readonly MAX_HOST_PARAMETERS = 256; // maximum number of parameters within a statement - private readonly name = basename(this.path); + private readonly name: string; - private readonly logger = new SQLiteStorageDatabaseLogger(this.options.logging); + private readonly logger: SQLiteStorageDatabaseLogger; - private readonly whenConnected = this.connect(this.path); + private readonly whenConnected: Promise; - constructor(private readonly path: string, private readonly options: ISQLiteStorageDatabaseOptions = Object.create(null)) { } + constructor( + private readonly path: string, + options: ISQLiteStorageDatabaseOptions = Object.create(null) + ) { + this.name = basename(this.path); + this.logger = new SQLiteStorageDatabaseLogger(options.logging); + this.whenConnected = this.connect(this.path); + } async getItems(): Promise> { const connection = await this.whenConnected; diff --git a/src/vs/base/test/browser/markdownRenderer.test.ts b/src/vs/base/test/browser/markdownRenderer.test.ts index 3263774598ed..dcda5f102e59 100644 --- a/src/vs/base/test/browser/markdownRenderer.test.ts +++ b/src/vs/base/test/browser/markdownRenderer.test.ts @@ -852,7 +852,7 @@ suite('MarkdownRenderer', () => { }); test('incomplete link target with incomplete arg 2', () => { - const incomplete = '[text](command:_github.copilot.openRelativePath "arg'; + const incomplete = '[text](command:vscode.openRelativePath "arg'; const tokens = marked.marked.lexer(incomplete); const newTokens = fillInIncompleteTokens(tokens); diff --git a/src/vs/base/test/browser/ui/tree/asyncDataTree.test.ts b/src/vs/base/test/browser/ui/tree/asyncDataTree.test.ts index 4f7ba0d46c81..6b3d35df9ffd 100644 --- a/src/vs/base/test/browser/ui/tree/asyncDataTree.test.ts +++ b/src/vs/base/test/browser/ui/tree/asyncDataTree.test.ts @@ -12,6 +12,7 @@ import { IAsyncDataSource, ITreeNode } from '../../../../browser/ui/tree/tree.js import { timeout } from '../../../../common/async.js'; import { Iterable } from '../../../../common/iterator.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../common/utils.js'; +import { runWithFakedTimers } from '../../../common/timeTravelScheduler.js'; interface Element { id: string; @@ -465,45 +466,47 @@ suite('AsyncDataTree', function () { }); test('issue #80098 - first expand should call getChildren', async () => { - const container = document.createElement('div'); + return runWithFakedTimers({ useFakeTimers: true }, async () => { + const container = document.createElement('div'); - const calls: Function[] = []; - const dataSource = new class implements IAsyncDataSource { - hasChildren(element: Element): boolean { - return !!element.children && element.children.length > 0; - } - getChildren(element: Element): Promise { - return new Promise(c => calls.push(() => c(element.children || []))); - } - }; + const calls: Function[] = []; + const dataSource = new class implements IAsyncDataSource { + hasChildren(element: Element): boolean { + return !!element.children && element.children.length > 0; + } + getChildren(element: Element): Promise { + return new Promise(c => calls.push(() => c(element.children || []))); + } + }; - const model = new Model({ - id: 'root', - children: [{ - id: 'a', children: [{ - id: 'aa' + const model = new Model({ + id: 'root', + children: [{ + id: 'a', children: [{ + id: 'aa' + }] }] - }] - }); + }); - const tree = store.add(new AsyncDataTree('test', container, new VirtualDelegate(), [new Renderer()], dataSource, { identityProvider: new IdentityProvider() })); - tree.layout(200); + const tree = store.add(new AsyncDataTree('test', container, new VirtualDelegate(), [new Renderer()], dataSource, { identityProvider: new IdentityProvider() })); + tree.layout(200); - const pSetInput = tree.setInput(model.root); - calls.pop()!(); // resolve getChildren(root) - await pSetInput; + const pSetInput = tree.setInput(model.root); + calls.pop()!(); // resolve getChildren(root) + await pSetInput; - const pExpandA = tree.expand(model.get('a')); - assert.strictEqual(calls.length, 1, 'expand(a) should\'ve called getChildren(a)'); + const pExpandA = tree.expand(model.get('a')); + assert.strictEqual(calls.length, 1, 'expand(a) should\'ve called getChildren(a)'); - let race = await Promise.race([pExpandA.then(() => 'expand'), timeout(1).then(() => 'timeout')]); - assert.strictEqual(race, 'timeout', 'expand(a) should not be yet done'); + let race = await Promise.race([pExpandA.then(() => 'expand'), timeout(1).then(() => 'timeout')]); + assert.strictEqual(race, 'timeout', 'expand(a) should not be yet done'); - calls.pop()!(); - assert.strictEqual(calls.length, 0, 'no pending getChildren calls'); + calls.pop()!(); + assert.strictEqual(calls.length, 0, 'no pending getChildren calls'); - race = await Promise.race([pExpandA.then(() => 'expand'), timeout(1).then(() => 'timeout')]); - assert.strictEqual(race, 'expand', 'expand(a) should now be done'); + race = await Promise.race([pExpandA.then(() => 'expand'), timeout(1).then(() => 'timeout')]); + assert.strictEqual(race, 'expand', 'expand(a) should now be done'); + }); }); test('issue #78388 - tree should react to hasChildren toggles', async () => { diff --git a/src/vs/base/test/common/arrays.test.ts b/src/vs/base/test/common/arrays.test.ts index f1144fbb6137..b9a18cffdf26 100644 --- a/src/vs/base/test/common/arrays.test.ts +++ b/src/vs/base/test/common/arrays.test.ts @@ -6,6 +6,7 @@ import assert from 'assert'; import * as arrays from '../../common/arrays.js'; import * as arraysFind from '../../common/arraysFind.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from './utils.js'; +import { pick } from '../../common/arrays.js'; suite('Arrays', () => { @@ -399,6 +400,64 @@ suite('Arrays', () => { ); }); + suite('pick', () => { + suite('object', () => { + test('numbers', () => { + const array = [{ v: 3, foo: 'a' }, { v: 5, foo: 'b' }, { v: 2, foo: 'c' }, { v: 2, foo: 'd' }, { v: 17, bar: '1' }, { v: -100, baz: '10' }]; + + assert.deepStrictEqual( + array.map(pick('v')), + [3, 5, 2, 2, 17, -100], + ); + }); + + test('strings', () => { + const array = [{ v: 3, foo: 'a' }, { v: 5, foo: 'b' }, { v: 2, foo: 'c' }, { v: 2, foo: 'd' }, { v: 17, bar: '1' }, { v: -100, baz: '10' }, { foo: '12' }]; + + assert.deepStrictEqual( + array.map(pick('foo')), + ['a', 'b', 'c', 'd', undefined, undefined, '12'], + ); + }); + + test('booleans', () => { + const array = [{ v: 3, foo: 'a' }, { v: 5, foo: 'b' }, { v: 2, foo: 'c' }, { v: 2, foo: 'd' }, { v: 17, bar: true }, { v: -100, bar: false }, { bar: false }]; + + assert.deepStrictEqual( + array.map(pick('bar')), + [undefined, undefined, undefined, undefined, true, false, false], + ); + }); + + test('objects', () => { + const array = [{ v: { test: 12 } }, { v: { test: 24 } }, {}, { v: { test: 17892 } }]; + + assert.deepStrictEqual( + array.map(pick('v')), + [{ test: 12 }, { test: 24 }, undefined, { test: 17892 }], + ); + }); + + test('mixed', () => { + const array = [{ v: { test: 104 } }, { v: 2 }, {}, { v: '24' }, { v: null }]; + + assert.deepStrictEqual( + array.map(pick('v')), + [{ test: 104 }, 2, undefined, '24', null], + ); + }); + }); + + test('string', () => { + const array = ['haallo', 'there', ':wave:', '!']; + + assert.deepStrictEqual( + array.map(pick('length')), + [6, 5, 6, 1], + ); + }); + }); + suite('ArrayQueue', () => { suite('takeWhile/takeFromEndWhile', () => { test('TakeWhile 1', () => { diff --git a/src/vs/base/test/common/buffer.test.ts b/src/vs/base/test/common/buffer.test.ts index 3ff4056f2efd..94bc1815fcea 100644 --- a/src/vs/base/test/common/buffer.test.ts +++ b/src/vs/base/test/common/buffer.test.ts @@ -429,7 +429,106 @@ suite('Buffer', () => { assert.strictEqual(haystack.indexOf(VSBuffer.fromString('ccc')), 15); assert.strictEqual(haystack.indexOf(VSBuffer.fromString('cccb')), -1); + }); + + test('wrap', () => { + const actual = new Uint8Array([1, 2, 3]); + const wrapped = VSBuffer.wrap(actual); + assert.strictEqual(wrapped.byteLength, 3); + assert.deepStrictEqual(Array.from(wrapped.buffer), [1, 2, 3]); + }); + + test('fromString', () => { + const value = 'Hello World'; + const buff = VSBuffer.fromString(value); + assert.strictEqual(buff.toString(), value); + }); + + test('fromByteArray', () => { + const array = [1, 2, 3, 4, 5]; + const buff = VSBuffer.fromByteArray(array); + assert.strictEqual(buff.byteLength, array.length); + assert.deepStrictEqual(Array.from(buff.buffer), array); + }); + + test('concat', () => { + const chunks = [ + VSBuffer.fromString('abc'), + VSBuffer.fromString('def'), + VSBuffer.fromString('ghi') + ]; + + // Test without total length + const result1 = VSBuffer.concat(chunks); + assert.strictEqual(result1.toString(), 'abcdefghi'); + + // Test with total length + const result2 = VSBuffer.concat(chunks, 9); + assert.strictEqual(result2.toString(), 'abcdefghi'); + }); + + test('clone', () => { + const original = VSBuffer.fromString('test'); + const clone = original.clone(); + + assert.notStrictEqual(original.buffer, clone.buffer); + assert.deepStrictEqual(Array.from(original.buffer), Array.from(clone.buffer)); + }); + + test('slice', () => { + const buff = VSBuffer.fromString('Hello World'); + + const slice1 = buff.slice(0, 5); + assert.strictEqual(slice1.toString(), 'Hello'); + + const slice2 = buff.slice(6); + assert.strictEqual(slice2.toString(), 'World'); + }); + + test('set', () => { + const buff = VSBuffer.alloc(5); + + // Test setting from VSBuffer + buff.set(VSBuffer.fromString('ab'), 0); + assert.strictEqual(buff.toString().substring(0, 2), 'ab'); + + // Test setting from Uint8Array + buff.set(new Uint8Array([99, 100]), 2); // 'cd' + assert.strictEqual(buff.toString().substring(2, 4), 'cd'); + + // Test invalid input + assert.throws(() => { + buff.set({} as any); + }); + }); + + test('equals', () => { + const buff1 = VSBuffer.fromString('test'); + const buff2 = VSBuffer.fromString('test'); + const buff3 = VSBuffer.fromString('different'); + const buff4 = VSBuffer.fromString('tes1'); + + assert.strictEqual(buff1.equals(buff1), true); + assert.strictEqual(buff1.equals(buff2), true); + assert.strictEqual(buff1.equals(buff3), false); + assert.strictEqual(buff1.equals(buff4), false); + }); + + test('read/write methods', () => { + const buff = VSBuffer.alloc(8); + + // Test UInt32BE + buff.writeUInt32BE(0x12345678, 0); + assert.strictEqual(buff.readUInt32BE(0), 0x12345678); + + // Test UInt32LE + buff.writeUInt32LE(0x12345678, 4); + assert.strictEqual(buff.readUInt32LE(4), 0x12345678); + // Test UInt8 + const buff2 = VSBuffer.alloc(1); + buff2.writeUInt8(123, 0); + assert.strictEqual(buff2.readUInt8(0), 123); }); suite('base64', () => { diff --git a/src/vs/base/test/common/date.test.ts b/src/vs/base/test/common/date.test.ts index 2260d6104b5f..287d192cea50 100644 --- a/src/vs/base/test/common/date.test.ts +++ b/src/vs/base/test/common/date.test.ts @@ -37,11 +37,13 @@ suite('Date', () => { test('yesterday', () => { const yesterday = new Date(); yesterday.setDate(yesterday.getDate() - 1); + yesterday.setHours(12); strictEqual(fromNowByDay(yesterday), 'Yesterday'); }); test('daysAgo', () => { const daysAgo = new Date(); daysAgo.setDate(daysAgo.getDate() - 5); + daysAgo.setHours(daysAgo.getHours() - 2); // 2 hours further to avoid DST issues strictEqual(fromNowByDay(daysAgo, true), '5 days ago'); }); }); diff --git a/src/vs/base/test/common/lifecycle.test.ts b/src/vs/base/test/common/lifecycle.test.ts index fd2fa4ef562c..6576cab31bde 100644 --- a/src/vs/base/test/common/lifecycle.test.ts +++ b/src/vs/base/test/common/lifecycle.test.ts @@ -5,7 +5,7 @@ import assert from 'assert'; import { Emitter } from '../../common/event.js'; -import { DisposableStore, dispose, IDisposable, markAsSingleton, ReferenceCollection, SafeDisposable, toDisposable } from '../../common/lifecycle.js'; +import { DisposableStore, dispose, IDisposable, markAsSingleton, ReferenceCollection, SafeDisposable, thenIfNotDisposed, toDisposable } from '../../common/lifecycle.js'; import { ensureNoDisposablesAreLeakedInTestSuite, throwIfDisposablesAreLeaked } from './utils.js'; class Disposable implements IDisposable { @@ -328,4 +328,30 @@ suite('No Leakage Utilities', () => { toDisposable(() => { }).dispose(); }); }); + + suite('thenIfNotDisposed', () => { + const store = ensureNoDisposablesAreLeakedInTestSuite(); + + test('normal case', async () => { + let called = false; + store.add(thenIfNotDisposed(Promise.resolve(123), (result: number) => { + assert.strictEqual(result, 123); + called = true; + })); + + await new Promise(resolve => setTimeout(resolve, 0)); + assert.strictEqual(called, true); + }); + + test('disposed before promise resolves', async () => { + let called = false; + const disposable = thenIfNotDisposed(Promise.resolve(123), () => { + called = true; + }); + + disposable.dispose(); + await new Promise(resolve => setTimeout(resolve, 0)); + assert.strictEqual(called, false); + }); + }); }); diff --git a/src/vs/base/test/common/markdownString.test.ts b/src/vs/base/test/common/markdownString.test.ts index 509325d07c0f..28fd291fb140 100644 --- a/src/vs/base/test/common/markdownString.test.ts +++ b/src/vs/base/test/common/markdownString.test.ts @@ -4,8 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import assert from 'assert'; -import { MarkdownString } from '../../common/htmlContent.js'; +import { IMarkdownString, MarkdownString } from '../../common/htmlContent.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from './utils.js'; +import { URI } from '../../common/uri.js'; suite('MarkdownString', () => { @@ -65,6 +66,27 @@ suite('MarkdownString', () => { ); }); + test('lift', () => { + const dto: IMarkdownString = { + value: 'hello', + baseUri: URI.file('/foo/bar'), + supportThemeIcons: true, + isTrusted: true, + supportHtml: true, + uris: { + [URI.file('/foo/bar2').toString()]: URI.file('/foo/bar2'), + [URI.file('/foo/bar3').toString()]: URI.file('/foo/bar3') + } + }; + const mds = MarkdownString.lift(dto); + assert.strictEqual(mds.value, dto.value); + assert.strictEqual(mds.baseUri?.toString(), dto.baseUri?.toString()); + assert.strictEqual(mds.supportThemeIcons, dto.supportThemeIcons); + assert.strictEqual(mds.isTrusted, dto.isTrusted); + assert.strictEqual(mds.supportHtml, dto.supportHtml); + assert.deepStrictEqual(mds.uris, dto.uris); + }); + suite('appendCodeBlock', () => { function assertCodeBlock(lang: string, code: string, result: string) { const mds = new MarkdownString(); diff --git a/src/vs/base/test/common/resources.test.ts b/src/vs/base/test/common/resources.test.ts index deed570761bf..d37a12acc0ce 100644 --- a/src/vs/base/test/common/resources.test.ts +++ b/src/vs/base/test/common/resources.test.ts @@ -276,6 +276,8 @@ suite('Resources', () => { assertRelativePath(URI.parse('foo://a/foo'), URI.parse('foo://A/FOO/BAR/GOO'), 'BAR/GOO', false, true); assertRelativePath(URI.parse('foo://a/foo/xoo'), URI.parse('foo://A/FOO/BAR/GOO'), '../BAR/GOO', false, true); assertRelativePath(URI.parse('foo:///c:/a/foo'), URI.parse('foo:///C:/a/foo/xoo/'), 'xoo', false, true); + assertRelativePath(URI.parse('foo:///c:/a/foo'), URI.parse('foo:///D:/a/foo/xoo/'), undefined, false, true); + assertRelativePath(URI.parse('file:///c:/a/foo'), URI.parse('file:///D:/a/foo/xoo/'), undefined, false, true); if (isWindows) { assertRelativePath(URI.file('c:\\foo\\bar'), URI.file('c:\\foo\\bar'), ''); diff --git a/src/vs/base/test/common/strings.test.ts b/src/vs/base/test/common/strings.test.ts index 7c567df4b6f1..dca2a40410e6 100644 --- a/src/vs/base/test/common/strings.test.ts +++ b/src/vs/base/test/common/strings.test.ts @@ -422,132 +422,9 @@ suite('Strings', () => { }), 'a0ca1ca2ca3c'); }); - test('removeAnsiEscapeCodes', () => { - const CSI = '\x1b\['; - const sequences = [ - // Base cases from https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Functions-using-CSI-_-ordered-by-the-final-character_s_ - `${CSI}42@`, - `${CSI}42 @`, - `${CSI}42A`, - `${CSI}42 A`, - `${CSI}42B`, - `${CSI}42C`, - `${CSI}42D`, - `${CSI}42E`, - `${CSI}42F`, - `${CSI}42G`, - `${CSI}42;42H`, - `${CSI}42I`, - `${CSI}42J`, - `${CSI}?42J`, - `${CSI}42K`, - `${CSI}?42K`, - `${CSI}42L`, - `${CSI}42M`, - `${CSI}42P`, - `${CSI}#P`, - `${CSI}3#P`, - `${CSI}#Q`, - `${CSI}3#Q`, - `${CSI}#R`, - `${CSI}42S`, - `${CSI}?1;2;3S`, - `${CSI}42T`, - `${CSI}42;42;42;42;42T`, - `${CSI}>3T`, - `${CSI}42X`, - `${CSI}42Z`, - `${CSI}42^`, - `${CSI}42\``, - `${CSI}42a`, - `${CSI}42b`, - `${CSI}42c`, - `${CSI}=42c`, - `${CSI}>42c`, - `${CSI}42d`, - `${CSI}42e`, - `${CSI}42;42f`, - `${CSI}42g`, - `${CSI}3h`, - `${CSI}?3h`, - `${CSI}42i`, - `${CSI}?42i`, - `${CSI}3l`, - `${CSI}?3l`, - `${CSI}3m`, - `${CSI}>0;0m`, - `${CSI}>0m`, - `${CSI}?0m`, - `${CSI}42n`, - `${CSI}>42n`, - `${CSI}?42n`, - `${CSI}>42p`, - `${CSI}!p`, - `${CSI}0;0"p`, - `${CSI}42$p`, - `${CSI}?42$p`, - `${CSI}#p`, - `${CSI}3#p`, - `${CSI}>42q`, - `${CSI}42q`, - `${CSI}42 q`, - `${CSI}42"q`, - `${CSI}#q`, - `${CSI}42;42r`, - `${CSI}?3r`, - `${CSI}0;0;0;0;3$r`, - `${CSI}s`, - `${CSI}0;0s`, - `${CSI}>42s`, - `${CSI}?3s`, - `${CSI}42;42;42t`, - `${CSI}>3t`, - `${CSI}42 t`, - `${CSI}0;0;0;0;3$t`, - `${CSI}u`, - `${CSI}42 u`, - `${CSI}0;0;0;0;0;0;0;0$v`, - `${CSI}42$w`, - `${CSI}0;0;0;0'w`, - `${CSI}42x`, - `${CSI}42*x`, - `${CSI}0;0;0;0;0$x`, - `${CSI}42#y`, - `${CSI}0;0;0;0;0;0*y`, - `${CSI}42;0'z`, - `${CSI}0;1;2;4$z`, - `${CSI}3'{`, - `${CSI}#{`, - `${CSI}3#{`, - `${CSI}0;0;0;0\${`, - `${CSI}0;0;0;0#|`, - `${CSI}42$|`, - `${CSI}42'|`, - `${CSI}42*|`, - `${CSI}#}`, - `${CSI}42'}`, - `${CSI}42$}`, - `${CSI}42'~`, - `${CSI}42$~`, - - // Common SGR cases: - `${CSI}1;31m`, // multiple attrs - `${CSI}105m`, // bright background - `${CSI}48:5:128m`, // 256 indexed color - `${CSI}48;5;128m`, // 256 indexed color alt - `${CSI}38:2:0:255:255:255m`, // truecolor - `${CSI}38;2;255;255;255m`, // truecolor alt - - // Custom sequences: - '\x1b]633;SetMark;\x07', - '\x1b]633;P;Cwd=/foo\x07', - ]; - - for (const sequence of sequences) { + suite('removeAnsiEscapeCodes', () => { + function testSequence(sequence: string) { assert.strictEqual(strings.removeAnsiEscapeCodes(`hello${sequence}world`), 'helloworld', `expect to remove ${JSON.stringify(sequence)}`); - } - - for (const sequence of sequences) { assert.deepStrictEqual( [...strings.forAnsiStringParts(`hello${sequence}world`)], [{ isCode: false, str: 'hello' }, { isCode: true, str: sequence }, { isCode: false, str: 'world' }], @@ -555,10 +432,203 @@ suite('Strings', () => { ); } - // #209937 - assert.strictEqual( - strings.removeAnsiEscapeCodes(`localhost:\x1b[31m1234`), - 'localhost:1234',); + test('CSI sequences', () => { + const CSI = '\x1b['; + const sequences = [ + // Base cases from https://invisible-island.net/xterm/ctlseqs/ctlseqs.html#h3-Functions-using-CSI-_-ordered-by-the-final-character_s_ + `${CSI}42@`, + `${CSI}42 @`, + `${CSI}42A`, + `${CSI}42 A`, + `${CSI}42B`, + `${CSI}42C`, + `${CSI}42D`, + `${CSI}42E`, + `${CSI}42F`, + `${CSI}42G`, + `${CSI}42;42H`, + `${CSI}42I`, + `${CSI}42J`, + `${CSI}?42J`, + `${CSI}42K`, + `${CSI}?42K`, + `${CSI}42L`, + `${CSI}42M`, + `${CSI}42P`, + `${CSI}#P`, + `${CSI}3#P`, + `${CSI}#Q`, + `${CSI}3#Q`, + `${CSI}#R`, + `${CSI}42S`, + `${CSI}?1;2;3S`, + `${CSI}42T`, + `${CSI}42;42;42;42;42T`, + `${CSI}>3T`, + `${CSI}42X`, + `${CSI}42Z`, + `${CSI}42^`, + `${CSI}42\``, + `${CSI}42a`, + `${CSI}42b`, + `${CSI}42c`, + `${CSI}=42c`, + `${CSI}>42c`, + `${CSI}42d`, + `${CSI}42e`, + `${CSI}42;42f`, + `${CSI}42g`, + `${CSI}3h`, + `${CSI}?3h`, + `${CSI}42i`, + `${CSI}?42i`, + `${CSI}3l`, + `${CSI}?3l`, + `${CSI}3m`, + `${CSI}>0;0m`, + `${CSI}>0m`, + `${CSI}?0m`, + `${CSI}42n`, + `${CSI}>42n`, + `${CSI}?42n`, + `${CSI}>42p`, + `${CSI}!p`, + `${CSI}0;0"p`, + `${CSI}42$p`, + `${CSI}?42$p`, + `${CSI}#p`, + `${CSI}3#p`, + `${CSI}>42q`, + `${CSI}42q`, + `${CSI}42 q`, + `${CSI}42"q`, + `${CSI}#q`, + `${CSI}42;42r`, + `${CSI}?3r`, + `${CSI}0;0;0;0;3$r`, + `${CSI}s`, + `${CSI}0;0s`, + `${CSI}>42s`, + `${CSI}?3s`, + `${CSI}42;42;42t`, + `${CSI}>3t`, + `${CSI}42 t`, + `${CSI}0;0;0;0;3$t`, + `${CSI}u`, + `${CSI}42 u`, + `${CSI}0;0;0;0;0;0;0;0$v`, + `${CSI}42$w`, + `${CSI}0;0;0;0'w`, + `${CSI}42x`, + `${CSI}42*x`, + `${CSI}0;0;0;0;0$x`, + `${CSI}42#y`, + `${CSI}0;0;0;0;0;0*y`, + `${CSI}42;0'z`, + `${CSI}0;1;2;4$z`, + `${CSI}3'{`, + `${CSI}#{`, + `${CSI}3#{`, + `${CSI}0;0;0;0\${`, + `${CSI}0;0;0;0#|`, + `${CSI}42$|`, + `${CSI}42'|`, + `${CSI}42*|`, + `${CSI}#}`, + `${CSI}42'}`, + `${CSI}42$}`, + `${CSI}42'~`, + `${CSI}42$~`, + + // Common SGR cases: + `${CSI}1;31m`, // multiple attrs + `${CSI}105m`, // bright background + `${CSI}48:5:128m`, // 256 indexed color + `${CSI}48;5;128m`, // 256 indexed color alt + `${CSI}38:2:0:255:255:255m`, // truecolor + `${CSI}38;2;255;255;255m`, // truecolor alt + ]; + + for (const sequence of sequences) { + testSequence(sequence); + } + }); + + suite('OSC sequences', () => { + function testOscSequence(prefix: string, suffix: string) { + const sequenceContent = [ + `633;SetMark;`, + `633;P;Cwd=/foo`, + `7;file://local/Users/me/foo/bar` + ]; + + const sequences = []; + for (const content of sequenceContent) { + sequences.push(`${prefix}${content}${suffix}`); + } + for (const sequence of sequences) { + testSequence(sequence); + } + } + test('ESC ] Ps ; Pt ESC \\', () => { + testOscSequence('\x1b]', '\x1b\\'); + }); + test('ESC ] Ps ; Pt BEL', () => { + testOscSequence('\x1b]', '\x07'); + }); + test('ESC ] Ps ; Pt ST', () => { + testOscSequence('\x1b]', '\x9c'); + }); + test('OSC Ps ; Pt ESC \\', () => { + testOscSequence('\x9d', '\x1b\\'); + }); + test('OSC Ps ; Pt BEL', () => { + testOscSequence('\x9d', '\x07'); + }); + test('OSC Ps ; Pt ST', () => { + testOscSequence('\x9d', '\x9c'); + }); + }); + + test('ESC sequences', () => { + const sequenceContent = [ + ` F`, + ` G`, + ` L`, + ` M`, + ` N`, + `#3`, + `#4`, + `#5`, + `#6`, + `#8`, + `%@`, + `%G`, + `(C`, + `)C`, + `*C`, + `+C`, + `-C`, + `.C`, + `/C` + ]; + const sequences = []; + for (const content of sequenceContent) { + sequences.push(`\x1b${content}`); + } + for (const sequence of sequences) { + testSequence(sequence); + } + }); + + suite('regression tests', () => { + test('#209937', () => { + assert.strictEqual( + strings.removeAnsiEscapeCodes(`localhost:\x1b[31m1234`), + 'localhost:1234' + ); + }); + }); }); test('removeAnsiEscapeCodesFromPrompt', () => { diff --git a/src/vs/base/worker/workerMain.ts b/src/vs/base/worker/workerMain.ts index a094636cc84e..bd1880e77a7e 100644 --- a/src/vs/base/worker/workerMain.ts +++ b/src/vs/base/worker/workerMain.ts @@ -3,6 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +// TODO @hediet @alexdima check where this code is used or remove this file +// (code oss runs fine without this file, but is probably needed by the monaco-editor). + (function () { function loadCode(moduleId: string): Promise { diff --git a/src/vs/code/browser/workbench/workbench.ts b/src/vs/code/browser/workbench/workbench.ts index faa711a30c5b..66e6a9ea6a93 100644 --- a/src/vs/code/browser/workbench/workbench.ts +++ b/src/vs/code/browser/workbench/workbench.ts @@ -27,13 +27,66 @@ interface ISecretStorageCrypto { unseal(data: string): Promise; } -class TransparentCrypto implements ISecretStorageCrypto { +// Secure crypto provider using Web Crypto API (AES-GCM) +class WebCryptoProvider implements ISecretStorageCrypto { + private readonly keyPromise: Promise; + + constructor(secret: string) { + // Derive key from secret using PBKDF2 and SHA-256 + this.keyPromise = (async () => { + const enc = new TextEncoder(); + const keyMaterial = await window.crypto.subtle.importKey( + "raw", + enc.encode(secret), + "PBKDF2", + false, + ["deriveKey"] + ); + const salt = enc.encode('vscode-secret-storage-salt'); + const key = await window.crypto.subtle.deriveKey( + { + name: "PBKDF2", + salt, + iterations: 100000, + hash: "SHA-256" + }, + keyMaterial, + { name: "AES-GCM", length: 256 }, + false, + ["encrypt", "decrypt"] + ); + return key; + })(); + } + async seal(data: string): Promise { - return data; + const key = await this.keyPromise; + const enc = new TextEncoder(); + const iv = window.crypto.getRandomValues(new Uint8Array(12)); + const encrypted = await window.crypto.subtle.encrypt( + { name: "AES-GCM", iv }, + key, + enc.encode(data) + ); + // Store iv + encrypted data as base64 + const buffer = new Uint8Array(iv.length + encrypted.byteLength); + buffer.set(iv, 0); + buffer.set(new Uint8Array(encrypted), iv.length); + return encodeBase64(VSBuffer.wrap(buffer)); } async unseal(data: string): Promise { - return data; + const key = await this.keyPromise; + const buf = VSBuffer.wrap(decodeBase64(data)).buffer; + const iv = new Uint8Array(buf.slice(0, 12)); + const encryptedBytes = buf.slice(12); + const decrypted = await window.crypto.subtle.decrypt( + { name: "AES-GCM", iv }, + key, + encryptedBytes + ); + const dec = new TextDecoder(); + return dec.decode(decrypted); } } @@ -44,6 +97,7 @@ const enum AESConstants { } class NetworkError extends Error { + constructor(inner: Error) { super(inner.message); this.name = inner.name; @@ -52,10 +106,13 @@ class NetworkError extends Error { } class ServerKeyedAESCrypto implements ISecretStorageCrypto { - private _serverKey: Uint8Array | undefined; - /** Gets whether the algorithm is supported; requires a secure context */ - public static supported() { + private serverKey: Uint8Array | undefined; + + /** + * Gets whether the algorithm is supported; requires a secure context + */ + static supported() { return !!crypto.subtle; } @@ -141,8 +198,8 @@ class ServerKeyedAESCrypto implements ISecretStorageCrypto { } private async getServerKeyPart(): Promise { - if (this._serverKey) { - return this._serverKey; + if (this.serverKey) { + return this.serverKey; } let attempt = 0; @@ -154,12 +211,15 @@ class ServerKeyedAESCrypto implements ISecretStorageCrypto { if (!res.ok) { throw new Error(res.statusText); } + const serverKey = new Uint8Array(await res.arrayBuffer()); if (serverKey.byteLength !== AESConstants.KEY_LENGTH / 8) { throw Error(`The key retrieved by the server is not ${AESConstants.KEY_LENGTH} bit long.`); } - this._serverKey = serverKey; - return this._serverKey; + + this.serverKey = serverKey; + + return this.serverKey; } catch (e) { lastError = e instanceof Error ? e : new Error(String(e)); attempt++; @@ -172,34 +232,39 @@ class ServerKeyedAESCrypto implements ISecretStorageCrypto { if (lastError) { throw new NetworkError(lastError); } + throw new Error('Unknown error'); } } export class LocalStorageSecretStorageProvider implements ISecretStorageProvider { - private readonly _storageKey = 'secrets.provider'; - private _secretsPromise: Promise> = this.load(); + private readonly storageKey = 'secrets.provider'; + + private secretsPromise: Promise>; type: 'in-memory' | 'persisted' | 'unknown' = 'persisted'; constructor( - private readonly crypto: ISecretStorageCrypto, - ) { } + private readonly crypto: ISecretStorageCrypto = new WebCryptoProvider('vscode_default_secret'), + ) { + this.secretsPromise = this.load(); + } private async load(): Promise> { const record = this.loadAuthSessionFromElement(); - // Get the secrets from localStorage - const encrypted = localStorage.getItem(this._storageKey); + + const encrypted = localStorage.getItem(this.storageKey); if (encrypted) { try { const decrypted = JSON.parse(await this.crypto.unseal(encrypted)); + return { ...record, ...decrypted }; } catch (err) { // TODO: send telemetry console.error('Failed to decrypt secrets from localStorage', err); if (!(err instanceof NetworkError)) { - localStorage.removeItem(this._storageKey); + localStorage.removeItem(this.storageKey); } } } @@ -243,33 +308,35 @@ export class LocalStorageSecretStorageProvider implements ISecretStorageProvider } async get(key: string): Promise { - const secrets = await this._secretsPromise; + const secrets = await this.secretsPromise; + return secrets[key]; } + async set(key: string, value: string): Promise { - const secrets = await this._secretsPromise; + const secrets = await this.secretsPromise; secrets[key] = value; - this._secretsPromise = Promise.resolve(secrets); + this.secretsPromise = Promise.resolve(secrets); this.save(); } + async delete(key: string): Promise { - const secrets = await this._secretsPromise; + const secrets = await this.secretsPromise; delete secrets[key]; - this._secretsPromise = Promise.resolve(secrets); + this.secretsPromise = Promise.resolve(secrets); this.save(); } private async save(): Promise { try { - const encrypted = await this.crypto.seal(JSON.stringify(await this._secretsPromise)); - localStorage.setItem(this._storageKey, encrypted); + const encrypted = await this.crypto.seal(JSON.stringify(await this.secretsPromise)); + localStorage.setItem(this.storageKey, encrypted); } catch (err) { console.error(err); } } } - class LocalStorageURLCallbackProvider extends Disposable implements IURLCallbackProvider { private static REQUEST_ID = 0; @@ -485,6 +552,7 @@ class WorkspaceProvider implements IWorkspaceProvider { return !!result; } } + return false; } diff --git a/src/vs/code/electron-main/app.ts b/src/vs/code/electron-main/app.ts index 9c7c68572b6e..7f9dbad5c949 100644 --- a/src/vs/code/electron-main/app.ts +++ b/src/vs/code/electron-main/app.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { app, BrowserWindow, protocol, session, Session, systemPreferences, WebFrameMain } from 'electron'; +import { app, BrowserWindow, protocol, session, Session, systemPreferences, WebFrameMain, Notification } from 'electron'; import { addUNCHostToAllowlist, disableUNCAccessRestrictions } from '../../base/node/unc.js'; import { validatedIpcMain } from '../../base/parts/ipc/electron-main/ipcMain.js'; import { hostname, release } from 'os'; @@ -71,7 +71,7 @@ import { ITelemetryService, TelemetryLevel } from '../../platform/telemetry/comm import { TelemetryAppenderClient } from '../../platform/telemetry/common/telemetryIpc.js'; import { ITelemetryServiceConfig, TelemetryService } from '../../platform/telemetry/common/telemetryService.js'; import { getPiiPathsFromEnvironment, getTelemetryLevel, isInternalTelemetry, NullTelemetryService, supportsTelemetry } from '../../platform/telemetry/common/telemetryUtils.js'; -import { IUpdateService } from '../../platform/update/common/update.js'; +import { IUpdateService, StateType } from '../../platform/update/common/update.js'; import { UpdateChannel } from '../../platform/update/common/updateIpc.js'; import { DarwinUpdateService } from '../../platform/update/electron-main/updateService.darwin.js'; import { LinuxUpdateService } from '../../platform/update/electron-main/updateService.linux.js'; @@ -83,7 +83,7 @@ import { NativeURLService } from '../../platform/url/common/urlService.js'; import { ElectronURLListener } from '../../platform/url/electron-main/electronUrlListener.js'; import { IWebviewManagerService } from '../../platform/webview/common/webviewManagerService.js'; import { WebviewMainService } from '../../platform/webview/electron-main/webviewMainService.js'; -import { isFolderToOpen, isWorkspaceToOpen, IWindowOpenable, TitlebarStyle, overrideDefaultTitlebarStyle } from '../../platform/window/common/window.js'; +import { isFolderToOpen, isWorkspaceToOpen, IWindowOpenable } from '../../platform/window/common/window.js'; import { IWindowsMainService, OpenContext } from '../../platform/windows/electron-main/windows.js'; import { ICodeWindow } from '../../platform/window/electron-main/window.js'; import { WindowsMainService } from '../../platform/windows/electron-main/windowsMainService.js'; @@ -118,6 +118,10 @@ import { IAuxiliaryWindowsMainService } from '../../platform/auxiliaryWindow/ele import { AuxiliaryWindowsMainService } from '../../platform/auxiliaryWindow/electron-main/auxiliaryWindowsMainService.js'; import { normalizeNFC } from '../../base/common/normalization.js'; import { ICSSDevelopmentService, CSSDevelopmentService } from '../../platform/cssDev/node/cssDevService.js'; +import { INativeMcpDiscoveryHelperService, NativeMcpDiscoveryHelperChannelName } from '../../platform/mcp/common/nativeMcpDiscoveryHelper.js'; +import { NativeMcpDiscoveryHelperService } from '../../platform/mcp/node/nativeMcpDiscoveryHelperService.js'; +import { IWebContentExtractorService } from '../../platform/webContentExtractor/common/webContentExtractor.js'; +import { NativeWebContentExtractorService } from '../../platform/webContentExtractor/electron-main/webContentExtractorService.js'; /** * The main VS Code application. There will only ever be one instance, @@ -599,12 +603,92 @@ export class CodeApplication extends Disposable { // Services const appInstantiationService = await this.initServices(machineId, sqmId, devDeviceId, sharedProcessReady); - // Linux (stable only): custom title default style override - if (isLinux && this.productService.quality === 'stable') { - const titleBarDefaultStyleOverride = this.stateService.getItem('window.titleBarStyleOverride'); - if (titleBarDefaultStyleOverride === TitlebarStyle.CUSTOM) { - overrideDefaultTitlebarStyle(titleBarDefaultStyleOverride); - } + // Update mode: block + if (this.configurationService.getValue('update.mode') === 'block') { + await appInstantiationService.invokeFunction(async accessor => { + const updateService = accessor.get(IUpdateService); + + const logUpdate = () => { + switch (updateService.state.type) { + case StateType.Uninitialized: + this.logService.info('Update State: Uninitialized'); + break; + case StateType.Idle: + this.logService.info('Update State: Idle'); + break; + case StateType.Disabled: + this.logService.info('Update State: Disabled'); + break; + case StateType.CheckingForUpdates: + this.logService.info('Update State: CheckingForUpdates'); + break; + case StateType.AvailableForDownload: + this.logService.info('Update State: AvailableForDownload'); + break; + case StateType.Downloading: + this.logService.info('Update State: Downloading'); + break; + case StateType.Downloaded: + this.logService.info('Update State: Downloaded'); + break; + case StateType.Updating: + this.logService.info('Update State: Updating'); + break; + case StateType.Ready: + this.logService.info('Update State: Ready'); + break; + } + }; + + logUpdate(); + await updateService.initialize(); + logUpdate(); + + setInterval(() => logUpdate(), 100); + + let notification = new Notification({ + title: 'Checking for updates...', + timeoutType: 'never' + }); + notification.show(); + + app.setBadgeCount(); + + await updateService.checkForUpdates(true); + await this.awaitUpdateState(updateService, StateType.Ready, StateType.Idle); + notification.close(); + + if (updateService.state.type === StateType.Ready) { + // this.logService.info('Before downloadUpdate()'); + // notification = new Notification({ + // title: 'Downloading update...', + // timeoutType: 'never' + // }); + // notification.show(); + // await updateService.downloadUpdate(); + // this.logService.info('After downloadUpdate()'); + + // notification.close(); + // notification = new Notification({ + // title: 'Applying update...', + // timeoutType: 'never' + // }); + // notification.show(); + // this.logService.info('Before applyUpdate()'); + // await updateService.applyUpdate(); + // this.logService.info('After applyUpdate()'); + + // notification.close(); + notification = new Notification({ + title: 'Restarting...', + timeoutType: 'never' + }); + notification.show(); + await updateService.quitAndInstall(true); + + return; + } + }); } // Auth Handler @@ -648,6 +732,17 @@ export class CodeApplication extends Disposable { eventuallyPhaseScheduler.schedule(); } + private awaitUpdateState(updateService: IUpdateService, ...types: StateType[]): Promise { + return new Promise(resolve => { + const disposable = this._register(updateService.onStateChange(e => { + if (types.includes(e.type)) { + disposable.dispose(); + resolve(); + } + })); + }); + } + private async setupProtocolUrlHandlers(accessor: ServicesAccessor, mainProcessElectronServer: ElectronIPCServer): Promise { const windowsMainService = this.windowsMainService = accessor.get(IWindowsMainService); const urlService = accessor.get(IURLService); @@ -1054,6 +1149,9 @@ export class CodeApplication extends Disposable { // Native Host services.set(INativeHostMainService, new SyncDescriptor(NativeHostMainService, undefined, false /* proxied to other processes */)); + // Web Contents Extractor + services.set(IWebContentExtractorService, new SyncDescriptor(NativeWebContentExtractorService, undefined, false /* proxied to other processes */)); + // Webview Manager services.set(IWebviewManagerService, new SyncDescriptor(WebviewMainService)); @@ -1127,6 +1225,10 @@ export class CodeApplication extends Disposable { // Proxy Auth services.set(IProxyAuthService, new SyncDescriptor(ProxyAuthService)); + // MCP + services.set(INativeMcpDiscoveryHelperService, new SyncDescriptor(NativeMcpDiscoveryHelperService)); + + // Dev Only: CSS service (for ESM) services.set(ICSSDevelopmentService, new SyncDescriptor(CSSDevelopmentService, undefined, true)); @@ -1197,6 +1299,10 @@ export class CodeApplication extends Disposable { mainProcessElectronServer.registerChannel('nativeHost', nativeHostChannel); sharedProcessClient.then(client => client.registerChannel('nativeHost', nativeHostChannel)); + // Web Content Extractor + const webContentExtractorChannel = ProxyChannel.fromService(accessor.get(IWebContentExtractorService), disposables); + mainProcessElectronServer.registerChannel('webContentExtractor', webContentExtractorChannel); + // Workspaces const workspacesChannel = ProxyChannel.fromService(accessor.get(IWorkspacesService), disposables); mainProcessElectronServer.registerChannel('workspaces', workspacesChannel); @@ -1230,6 +1336,10 @@ export class CodeApplication extends Disposable { const externalTerminalChannel = ProxyChannel.fromService(accessor.get(IExternalTerminalMainService), disposables); mainProcessElectronServer.registerChannel('externalTerminal', externalTerminalChannel); + // MCP + const mcpDiscoveryChannel = ProxyChannel.fromService(accessor.get(INativeMcpDiscoveryHelperService), disposables); + mainProcessElectronServer.registerChannel(NativeMcpDiscoveryHelperChannelName, mcpDiscoveryChannel); + // Logger const loggerChannel = new LoggerChannel(accessor.get(ILoggerMainService),); mainProcessElectronServer.registerChannel('logger', loggerChannel); diff --git a/src/vs/code/electron-main/main.ts b/src/vs/code/electron-main/main.ts index 3f819e1662b5..84c4e12898f5 100644 --- a/src/vs/code/electron-main/main.ts +++ b/src/vs/code/electron-main/main.ts @@ -198,9 +198,16 @@ class CodeMain { fileService.registerProvider(Schemas.vscodeUserData, new FileUserDataProvider(Schemas.file, diskFileSystemProvider, Schemas.vscodeUserData, userDataProfilesMainService, uriIdentityService, logService)); // Policy - const policyService = isWindows && productService.win32RegValueName ? disposables.add(new NativePolicyService(logService, productService.win32RegValueName)) - : environmentMainService.policyFile ? disposables.add(new FilePolicyService(environmentMainService.policyFile, fileService, logService)) - : new NullPolicyService(); + let policyService: IPolicyService | undefined; + if (isWindows && productService.win32RegValueName) { + policyService = disposables.add(new NativePolicyService(logService, productService.win32RegValueName)); + } else if (isMacintosh && productService.darwinBundleIdentifier) { + policyService = disposables.add(new NativePolicyService(logService, productService.darwinBundleIdentifier)); + } else if (environmentMainService.policyFile) { + policyService = disposables.add(new FilePolicyService(environmentMainService.policyFile, fileService, logService)); + } else { + policyService = new NullPolicyService(); + } services.set(IPolicyService, policyService); // Configuration diff --git a/src/vs/code/electron-sandbox/workbench/workbench.ts b/src/vs/code/electron-sandbox/workbench/workbench.ts index 47c3d28a6fcd..44a6051573c0 100644 --- a/src/vs/code/electron-sandbox/workbench/workbench.ts +++ b/src/vs/code/electron-sandbox/workbench/workbench.ts @@ -90,15 +90,19 @@ splash.className = baseTheme ?? 'vs-dark'; if (layoutInfo.windowBorder && colorInfo.windowBorder) { - splash.style.position = 'relative'; - splash.style.height = 'calc(100vh - 2px)'; - splash.style.width = 'calc(100vw - 2px)'; - splash.style.border = `1px solid var(--window-border-color)`; - splash.style.setProperty('--window-border-color', colorInfo.windowBorder); + const borderElement = document.createElement('div'); + borderElement.style.position = 'absolute'; + borderElement.style.width = 'calc(100vw - 2px)'; + borderElement.style.height = 'calc(100vh - 2px)'; + borderElement.style.zIndex = '1'; // allow border above other elements + borderElement.style.border = `1px solid var(--window-border-color)`; + borderElement.style.setProperty('--window-border-color', colorInfo.windowBorder); if (layoutInfo.windowBorderRadius) { - splash.style.borderRadius = layoutInfo.windowBorderRadius; + borderElement.style.borderRadius = layoutInfo.windowBorderRadius; } + + splash.appendChild(borderElement); } // ensure there is enough space diff --git a/src/vs/code/electron-utility/sharedProcess/contrib/codeCacheCleaner.ts b/src/vs/code/electron-utility/sharedProcess/contrib/codeCacheCleaner.ts index 7560b8fac32c..5e7ebeaee5d2 100644 --- a/src/vs/code/electron-utility/sharedProcess/contrib/codeCacheCleaner.ts +++ b/src/vs/code/electron-utility/sharedProcess/contrib/codeCacheCleaner.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as fs from 'fs'; +import { promises } from 'fs'; import { RunOnceScheduler } from '../../../../base/common/async.js'; import { onUnexpectedError } from '../../../../base/common/errors.js'; import { Disposable } from '../../../../base/common/lifecycle.js'; @@ -14,17 +14,19 @@ import { IProductService } from '../../../../platform/product/common/productServ export class CodeCacheCleaner extends Disposable { - private readonly _DataMaxAge = this.productService.quality !== 'stable' - ? 1000 * 60 * 60 * 24 * 7 // roughly 1 week (insiders) - : 1000 * 60 * 60 * 24 * 30 * 3; // roughly 3 months (stable) + private readonly dataMaxAge: number; constructor( currentCodeCachePath: string | undefined, - @IProductService private readonly productService: IProductService, + @IProductService productService: IProductService, @ILogService private readonly logService: ILogService ) { super(); + this.dataMaxAge = productService.quality !== 'stable' + ? 1000 * 60 * 60 * 24 * 7 // roughly 1 week (insiders) + : 1000 * 60 * 60 * 24 * 30 * 3; // roughly 3 months (stable) + // Cached data is stored as user data and we run a cleanup task every time // the editor starts. The strategy is to delete all files that are older than // 3 months (1 week respectively) @@ -55,8 +57,8 @@ export class CodeCacheCleaner extends Disposable { // Delete cache folder if old enough const codeCacheEntryPath = join(codeCacheRootPath, codeCache); - const codeCacheEntryStat = await fs.promises.stat(codeCacheEntryPath); - if (codeCacheEntryStat.isDirectory() && (now - codeCacheEntryStat.mtime.getTime()) > this._DataMaxAge) { + const codeCacheEntryStat = await promises.stat(codeCacheEntryPath); + if (codeCacheEntryStat.isDirectory() && (now - codeCacheEntryStat.mtime.getTime()) > this.dataMaxAge) { this.logService.trace(`[code cache cleanup]: Removing code cache folder ${codeCache}.`); return Promises.rm(codeCacheEntryPath); diff --git a/src/vs/code/electron-utility/sharedProcess/contrib/languagePackCachedDataCleaner.ts b/src/vs/code/electron-utility/sharedProcess/contrib/languagePackCachedDataCleaner.ts index 0c70b33f15c2..f3f351ebaa49 100644 --- a/src/vs/code/electron-utility/sharedProcess/contrib/languagePackCachedDataCleaner.ts +++ b/src/vs/code/electron-utility/sharedProcess/contrib/languagePackCachedDataCleaner.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import * as fs from 'fs'; +import { promises } from 'fs'; import { RunOnceScheduler } from '../../../../base/common/async.js'; import { IStringDictionary } from '../../../../base/common/collections.js'; import { onUnexpectedError } from '../../../../base/common/errors.js'; @@ -33,17 +33,19 @@ interface ILanguagePackFile { export class LanguagePackCachedDataCleaner extends Disposable { - private readonly _DataMaxAge = this.productService.quality !== 'stable' - ? 1000 * 60 * 60 * 24 * 7 // roughly 1 week (insiders) - : 1000 * 60 * 60 * 24 * 30 * 3; // roughly 3 months (stable) + private readonly dataMaxAge: number; constructor( @INativeEnvironmentService private readonly environmentService: INativeEnvironmentService, @ILogService private readonly logService: ILogService, - @IProductService private readonly productService: IProductService + @IProductService productService: IProductService ) { super(); + this.dataMaxAge = productService.quality !== 'stable' + ? 1000 * 60 * 60 * 24 * 7 // roughly 1 week (insiders) + : 1000 * 60 * 60 * 24 * 30 * 3; // roughly 3 months (stable) + // We have no Language pack support for dev version (run from source) // So only cleanup when we have a build version. if (this.environmentService.isBuilt) { @@ -59,7 +61,7 @@ export class LanguagePackCachedDataCleaner extends Disposable { try { const installed: IStringDictionary = Object.create(null); - const metaData: ILanguagePackFile = JSON.parse(await fs.promises.readFile(join(this.environmentService.userDataPath, 'languagepacks.json'), 'utf8')); + const metaData: ILanguagePackFile = JSON.parse(await promises.readFile(join(this.environmentService.userDataPath, 'languagepacks.json'), 'utf8')); for (const locale of Object.keys(metaData)) { const entry = metaData[locale]; installed[`${entry.hash}.${locale}`] = true; @@ -94,8 +96,8 @@ export class LanguagePackCachedDataCleaner extends Disposable { } const candidate = join(folder, entry); - const stat = await fs.promises.stat(candidate); - if (stat.isDirectory() && (now - stat.mtime.getTime()) > this._DataMaxAge) { + const stat = await promises.stat(candidate); + if (stat.isDirectory() && (now - stat.mtime.getTime()) > this.dataMaxAge) { this.logService.trace(`[language pack cache cleanup]: Removing language pack cache folder: ${join(packEntry, entry)}`); await Promises.rm(candidate); diff --git a/src/vs/code/electron-utility/sharedProcess/sharedProcessMain.ts b/src/vs/code/electron-utility/sharedProcess/sharedProcessMain.ts index 16e9793483b5..e0240babbcf5 100644 --- a/src/vs/code/electron-utility/sharedProcess/sharedProcessMain.ts +++ b/src/vs/code/electron-utility/sharedProcess/sharedProcessMain.ts @@ -120,6 +120,8 @@ import { getCodeDisplayProtocol, getDisplayProtocol } from '../../../base/node/o import { RequestService } from '../../../platform/request/electron-utility/requestService.js'; import { DefaultExtensionsInitializer } from './contrib/defaultExtensionsInitializer.js'; import { AllowedExtensionsService } from '../../../platform/extensionManagement/common/allowedExtensionsService.js'; +import { IExtensionGalleryManifestService } from '../../../platform/extensionManagement/common/extensionGalleryManifest.js'; +import { ExtensionGalleryManifestIPCService } from '../../../platform/extensionManagement/common/extensionGalleryManifestServiceIpc.js'; class SharedProcessMain extends Disposable implements IClientConnectionFilter { @@ -331,6 +333,7 @@ class SharedProcessMain extends Disposable implements IClientConnectionFilter { services.set(INativeServerExtensionManagementService, new SyncDescriptor(ExtensionManagementService, undefined, true)); // Extension Gallery + services.set(IExtensionGalleryManifestService, new ExtensionGalleryManifestIPCService(this.server, productService)); services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryService, undefined, true)); // Extension Tips @@ -376,8 +379,6 @@ class SharedProcessMain extends Disposable implements IClientConnectionFilter { private initChannels(accessor: ServicesAccessor): void { - // const disposables = this._register(new DisposableStore()); - // Extensions Management const channel = new ExtensionManagementChannel(accessor.get(IExtensionManagementService), () => null); this.server.registerChannel('extensions', channel); diff --git a/src/vs/code/node/cli.ts b/src/vs/code/node/cli.ts index e91bea07d6a8..312df0c1f929 100644 --- a/src/vs/code/node/cli.ts +++ b/src/vs/code/node/cli.ts @@ -36,6 +36,7 @@ function shouldSpawnCliProcess(argv: NativeParsedArgs): boolean { || !!argv['uninstall-extension'] || !!argv['update-extensions'] || !!argv['locate-extension'] + || !!argv['add-mcp'] || !!argv['telemetry']; } diff --git a/src/vs/code/node/cliProcessMain.ts b/src/vs/code/node/cliProcessMain.ts index aff3b2da5794..7087d2ac4318 100644 --- a/src/vs/code/node/cliProcessMain.ts +++ b/src/vs/code/node/cliProcessMain.ts @@ -65,6 +65,9 @@ import { localize } from '../../nls.js'; import { FileUserDataProvider } from '../../platform/userData/common/fileUserDataProvider.js'; import { addUNCHostToAllowlist, getUNCHost } from '../../base/node/unc.js'; import { AllowedExtensionsService } from '../../platform/extensionManagement/common/allowedExtensionsService.js'; +import { McpManagementCli } from '../../platform/mcp/common/mcpManagementCli.js'; +import { IExtensionGalleryManifestService } from '../../platform/extensionManagement/common/extensionGalleryManifest.js'; +import { ExtensionGalleryManifestService } from '../../platform/extensionManagement/common/extensionGalleryManifestService.js'; class CliMain extends Disposable { @@ -208,6 +211,7 @@ class CliMain extends Disposable { services.set(IExtensionSignatureVerificationService, new SyncDescriptor(ExtensionSignatureVerificationService, undefined, true)); services.set(IAllowedExtensionsService, new SyncDescriptor(AllowedExtensionsService, undefined, true)); services.set(INativeServerExtensionManagementService, new SyncDescriptor(ExtensionManagementService, undefined, true)); + services.set(IExtensionGalleryManifestService, new SyncDescriptor(ExtensionGalleryManifestService)); services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryServiceWithNoStorageService, undefined, true)); // Localizations @@ -304,6 +308,11 @@ class CliMain extends Disposable { return instantiationService.createInstance(ExtensionManagementCLI, new ConsoleLogger(LogLevel.Info, false)).locateExtension(this.argv['locate-extension']); } + // Install MCP server + else if (this.argv['add-mcp']) { + return instantiationService.createInstance(McpManagementCli, new ConsoleLogger(LogLevel.Info, false)).addMcpDefinitions(this.argv['add-mcp']); + } + // Telemetry else if (this.argv['telemetry']) { console.log(await buildTelemetryMessage(environmentService.appRoot, environmentService.extensionsPath)); diff --git a/src/vs/editor/browser/config/migrateOptions.ts b/src/vs/editor/browser/config/migrateOptions.ts index 4adf4e3d848e..a056495bfef8 100644 --- a/src/vs/editor/browser/config/migrateOptions.ts +++ b/src/vs/editor/browser/config/migrateOptions.ts @@ -232,3 +232,10 @@ registerEditorSettingMigration('lightbulb.enabled', (value, read, write) => { } }); +// NES Code Shifting +registerEditorSettingMigration('inlineSuggest.edits.codeShifting', (value, read, write) => { + if (typeof value === 'boolean') { + write('inlineSuggest.edits.codeShifting', undefined); + write('inlineSuggest.edits.allowCodeShifting', value ? 'always' : 'never'); + } +}); diff --git a/src/vs/editor/browser/controller/editContext/native/nativeEditContext.css b/src/vs/editor/browser/controller/editContext/native/nativeEditContext.css index 792c15feec2b..5ca36c59620d 100644 --- a/src/vs/editor/browser/controller/editContext/native/nativeEditContext.css +++ b/src/vs/editor/browser/controller/editContext/native/nativeEditContext.css @@ -7,10 +7,10 @@ margin: 0; padding: 0; position: absolute; - overflow: hidden; + overflow-y: scroll; + scrollbar-width: none; z-index: -10; white-space: pre-wrap; - text-wrap: nowrap; } .monaco-editor .native-edit-context-textarea { diff --git a/src/vs/editor/browser/controller/editContext/native/nativeEditContext.ts b/src/vs/editor/browser/controller/editContext/native/nativeEditContext.ts index 75acb7c2c8d7..4e6eac746fb6 100644 --- a/src/vs/editor/browser/controller/editContext/native/nativeEditContext.ts +++ b/src/vs/editor/browser/controller/editContext/native/nativeEditContext.ts @@ -11,10 +11,10 @@ import { StandardKeyboardEvent } from '../../../../../base/browser/keyboardEvent import { KeyCode } from '../../../../../base/common/keyCodes.js'; import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; import { EditorOption } from '../../../../common/config/editorOptions.js'; -import { EndOfLinePreference, EndOfLineSequence, IModelDeltaDecoration } from '../../../../common/model.js'; -import { ViewConfigurationChangedEvent, ViewCursorStateChangedEvent, ViewDecorationsChangedEvent, ViewFlushedEvent, ViewLinesChangedEvent, ViewLinesDeletedEvent, ViewLinesInsertedEvent, ViewScrollChangedEvent, ViewZonesChangedEvent } from '../../../../common/viewEvents.js'; +import { EndOfLinePreference, IModelDeltaDecoration } from '../../../../common/model.js'; +import { ViewConfigurationChangedEvent, ViewCursorStateChangedEvent, ViewScrollChangedEvent } from '../../../../common/viewEvents.js'; import { ViewContext } from '../../../../common/viewModel/viewContext.js'; -import { RestrictedRenderingContext, RenderingContext } from '../../../view/renderingContext.js'; +import { RestrictedRenderingContext, RenderingContext, HorizontalPosition } from '../../../view/renderingContext.js'; import { ViewController } from '../../../view/viewController.js'; import { ClipboardEventUtils, ClipboardStoredMetadata, getDataToCopy, InMemoryClipboardMetadataManager } from '../clipboardUtils.js'; import { AbstractEditContext } from '../editContext.js'; @@ -25,11 +25,7 @@ import { Selection } from '../../../../common/core/selection.js'; import { Position } from '../../../../common/core/position.js'; import { IVisibleRangeProvider } from '../textArea/textAreaEditContext.js'; import { PositionOffsetTransformer } from '../../../../common/core/positionToOffset.js'; -import { IDisposable, MutableDisposable } from '../../../../../base/common/lifecycle.js'; -import { EditContext } from './editContextFactory.js'; -import { IAccessibilityService } from '../../../../../platform/accessibility/common/accessibility.js'; -import { NativeEditContextRegistry } from './nativeEditContextRegistry.js'; -import { IEditorAriaOptions } from '../../../editorBrowser.js'; +import { DebugEditContext } from './debugEditContext.js'; // Corresponds to classes in nativeEditContext.css enum CompositionClassName { @@ -77,6 +73,10 @@ export class NativeEditContext extends AbstractEditContext { this.textArea = new FastDomNode(document.createElement('textarea')); this.textArea.setClassName('native-edit-context-textarea'); this.textArea.setAttribute('tabindex', '-1'); + this.domNode.setAttribute('autocorrect', 'off'); + this.domNode.setAttribute('autocapitalize', 'off'); + this.domNode.setAttribute('autocomplete', 'off'); + this.domNode.setAttribute('spellcheck', 'false'); this._updateDomAttributes(); @@ -84,20 +84,8 @@ export class NativeEditContext extends AbstractEditContext { overflowGuardContainer.appendChild(this.textArea); this._parent = overflowGuardContainer.domNode; - this._selectionChangeListener = this._register(new MutableDisposable()); - this._focusTracker = this._register(new FocusTracker(this.domNode.domNode, (newFocusValue: boolean) => { - if (newFocusValue) { - this._selectionChangeListener.value = this._setSelectionChangeListener(viewController); - this._screenReaderSupport.setIgnoreSelectionChangeTime('onFocus'); - } else { - this._selectionChangeListener.value = undefined; - } - this._context.viewModel.setHasFocus(newFocusValue); - })); - - const window = getWindow(this.domNode.domNode); - this._editContext = EditContext.create(window); - this.setEditContextOnDomNode(); + this._editContext = new DebugEditContext(); + this.domNode.domNode.editContext = this._editContext; this._screenReaderSupport = instantiationService.createInstance(ScreenReaderSupport, this.domNode, context); @@ -208,6 +196,13 @@ export class NativeEditContext extends AbstractEditContext { public override onCursorStateChanged(e: ViewCursorStateChangedEvent): boolean { this._primarySelection = e.modelSelections[0] ?? new Selection(1, 1, 1, 1); this._screenReaderSupport.onCursorStateChanged(e); + this._updateEditContext(); + return true; + } + + public override onScrollChanged(e: ViewScrollChangedEvent): boolean { + const visibleRangeForPosition = (position: Position) => this._visibleRangeProvider.visibleRangeForPosition(position); + this._updateSelectionAndControlBounds({ visibleRangeForPosition }); return true; } @@ -257,7 +252,7 @@ export class NativeEditContext extends AbstractEditContext { } public isFocused(): boolean { - return this._focusTracker.isFocused || (getActiveWindow().document.activeElement === this.textArea.domNode); + return this._focusTracker.isFocused; } public focus(): void { @@ -415,7 +410,9 @@ export class NativeEditContext extends AbstractEditContext { this._decorations = this._context.viewModel.model.deltaDecorations(this._decorations, decorations); } - private _updateSelectionAndControlBounds(ctx: RenderingContext) { + private _updateSelectionAndControlBounds(ctx: { + visibleRangeForPosition(position: Position): HorizontalPosition | null; + }) { if (!this._parent) { return; } @@ -443,8 +440,21 @@ export class NativeEditContext extends AbstractEditContext { } const selectionBounds = new DOMRect(left, top, width, height); - this._editContext.updateSelectionBounds(selectionBounds); - this._editContext.updateControlBounds(selectionBounds); + let adjustedTop: number = selectionBounds.top; + if (selectionBounds.top < parentBounds.top) { + adjustedTop = parentBounds.top; + } else if (selectionBounds.top > parentBounds.top + parentBounds.height) { + adjustedTop = parentBounds.top + parentBounds.height - selectionBounds.height; + } + let adjustedLeft: number = selectionBounds.left; + if (selectionBounds.left < parentBounds.left) { + adjustedLeft = parentBounds.left; + } else if (selectionBounds.left > parentBounds.left + parentBounds.width) { + adjustedLeft = parentBounds.left + parentBounds.width - selectionBounds.width; + } + const adjustedSelectionBounds = new DOMRect(adjustedLeft, adjustedTop, selectionBounds.width, selectionBounds.height); + this._editContext.updateSelectionBounds(adjustedSelectionBounds); + this._editContext.updateControlBounds(adjustedSelectionBounds); } private _updateCharacterBounds(e: CharacterBoundsUpdateEvent): void { diff --git a/src/vs/editor/browser/controller/editContext/native/nativeEditContextUtils.ts b/src/vs/editor/browser/controller/editContext/native/nativeEditContextUtils.ts index fd170ef812d0..4189ae9c7584 100644 --- a/src/vs/editor/browser/controller/editContext/native/nativeEditContextUtils.ts +++ b/src/vs/editor/browser/controller/editContext/native/nativeEditContextUtils.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { addDisposableListener, getActiveWindow } from '../../../../../base/browser/dom.js'; +import { addDisposableListener, getActiveElement, getShadowRoot } from '../../../../../base/browser/dom.js'; import { IDisposable, Disposable } from '../../../../../base/common/lifecycle.js'; export interface ITypeData { @@ -21,8 +21,12 @@ export class FocusTracker extends Disposable { private readonly _onFocusChange: (newFocusValue: boolean) => void, ) { super(); - this._register(addDisposableListener(this._domNode, 'focus', () => this._handleFocusedChanged(true))); - this._register(addDisposableListener(this._domNode, 'blur', () => this._handleFocusedChanged(false))); + this._register(addDisposableListener(this._domNode, 'focus', () => { + this.refreshFocusState(); + })); + this._register(addDisposableListener(this._domNode, 'blur', () => { + this._handleFocusedChanged(false); + })); } private _handleFocusedChanged(focused: boolean): void { @@ -30,24 +34,23 @@ export class FocusTracker extends Disposable { return; } this._isFocused = focused; - if (this._isFocused) { - this._domNode.focus(); - } else { - this._domNode.blur(); - } this._onFocusChange(this._isFocused); } public focus(): void { - // fixes: https://github.com/microsoft/vscode/issues/228147 - // Immediately call this method in order to directly set the field isFocused to true so the textInputFocus context key is evaluated correctly - this._handleFocusedChanged(true); this._domNode.focus(); + this.refreshFocusState(); } public refreshFocusState(): void { - const focused = this._domNode === getActiveWindow().document.activeElement; - this._handleFocusedChanged(focused); + let activeElement: Element | null = null; + const shadowRoot = getShadowRoot(this._domNode); + if (shadowRoot) { + activeElement = shadowRoot.activeElement; + } else { + activeElement = getActiveElement(); + } + this._handleFocusedChanged(activeElement === this._domNode); } get isFocused(): boolean { diff --git a/src/vs/editor/browser/controller/editContext/native/screenReaderSupport.ts b/src/vs/editor/browser/controller/editContext/native/screenReaderSupport.ts index 769bb020d728..93a038232201 100644 --- a/src/vs/editor/browser/controller/editContext/native/screenReaderSupport.ts +++ b/src/vs/editor/browser/controller/editContext/native/screenReaderSupport.ts @@ -19,7 +19,7 @@ import { ViewContext } from '../../../../common/viewModel/viewContext.js'; import { applyFontInfo } from '../../../config/domFontInfo.js'; import { IEditorAriaOptions } from '../../../editorBrowser.js'; import { RestrictedRenderingContext, RenderingContext, HorizontalPosition } from '../../../view/renderingContext.js'; -import { ariaLabelForScreenReaderContent, ISimpleModel, newlinecount, PagedScreenReaderStrategy, ScreenReaderContentState } from '../screenReaderUtils.js'; +import { ariaLabelForScreenReaderContent, ISimpleModel, PagedScreenReaderStrategy, ScreenReaderContentState } from '../screenReaderUtils.js'; export class ScreenReaderSupport { @@ -27,6 +27,7 @@ export class ScreenReaderSupport { private _contentLeft: number = 1; private _contentWidth: number = 1; private _contentHeight: number = 1; + private _divWidth: number = 1; private _lineHeight: number = 1; private _fontInfo!: FontInfo; private _accessibilityPageSize: number = 1; @@ -69,12 +70,14 @@ export class ScreenReaderSupport { private _updateConfigurationSettings(): void { const options = this._context.configuration.options; const layoutInfo = options.get(EditorOption.layoutInfo); + const wrappingColumn = layoutInfo.wrappingColumn; this._contentLeft = layoutInfo.contentLeft; this._contentWidth = layoutInfo.contentWidth; this._contentHeight = layoutInfo.height; this._fontInfo = options.get(EditorOption.fontInfo); this._lineHeight = options.get(EditorOption.lineHeight); this._accessibilityPageSize = options.get(EditorOption.accessibilityPageSize); + this._divWidth = Math.round(wrappingColumn * this._fontInfo.typicalHalfwidthCharacterWidth); } private _updateDomAttributes(): void { @@ -88,6 +91,9 @@ export class ScreenReaderSupport { const tabSize = this._context.viewModel.model.getOptions().tabSize; const spaceWidth = options.get(EditorOption.fontInfo).spaceWidth; this._domNode.domNode.style.tabSize = `${tabSize * spaceWidth}px`; + const wordWrapOverride2 = options.get(EditorOption.wordWrapOverride2); + const wordWrapValue = wordWrapOverride2 !== 'inherit' ? wordWrapOverride2 : options.get(EditorOption.wordWrap); + this._domNode.domNode.style.textWrap = wordWrapValue === 'off' ? 'nowrap' : 'wrap'; } public onCursorStateChanged(e: ViewCursorStateChangedEvent): void { @@ -119,22 +125,25 @@ export class ScreenReaderSupport { } const editorScrollTop = this._context.viewLayout.getCurrentScrollTop(); - const top = this._context.viewLayout.getVerticalOffsetForLineNumber(this._primarySelection.positionLineNumber) - editorScrollTop; + const positionLineNumber = this._primarySelection.positionLineNumber; + const top = this._context.viewLayout.getVerticalOffsetForLineNumber(positionLineNumber) - editorScrollTop; if (top < 0 || top > this._contentHeight) { // cursor is outside the viewport this._renderAtTopLeft(); return; } - this._doRender(top, this._contentLeft, this._contentWidth, this._lineHeight); - this._setScrollTop(); + const offsetForStartPositionWithinEditor = this._context.viewLayout.getVerticalOffsetForLineNumber(this._screenReaderContentState.startPositionWithinEditor.lineNumber); + const offsetForPositionLineNumber = this._context.viewLayout.getVerticalOffsetForLineNumber(positionLineNumber); + const scrollTop = offsetForPositionLineNumber - offsetForStartPositionWithinEditor; + this._doRender(scrollTop, top, this._contentLeft, this._divWidth, this._lineHeight); } private _renderAtTopLeft(): void { - this._doRender(0, 0, this._contentWidth, 1); + this._doRender(0, 0, 0, this._contentWidth, 1); } - private _doRender(top: number, left: number, width: number, height: number): void { + private _doRender(scrollTop: number, top: number, left: number, width: number, height: number): void { // For correct alignment of the screen reader content, we need to apply the correct font applyFontInfo(this._domNode, this._fontInfo); @@ -142,16 +151,7 @@ export class ScreenReaderSupport { this._domNode.setLeft(left); this._domNode.setWidth(width); this._domNode.setHeight(height); - } - - private _setScrollTop(): void { - if (!this._screenReaderContentState) { - return; - } - // Setting position within the screen reader content by modifying scroll position - const textContentBeforeSelection = this._screenReaderContentState.value.substring(0, this._screenReaderContentState.selectionStart); - const numberOfLinesOfContentBeforeSelection = newlinecount(textContentBeforeSelection); - this._domNode.domNode.scrollTop = numberOfLinesOfContentBeforeSelection * this._lineHeight; + this._domNode.domNode.scrollTop = scrollTop; } public setAriaOptions(options: IEditorAriaOptions): void { diff --git a/src/vs/editor/browser/observableCodeEditor.ts b/src/vs/editor/browser/observableCodeEditor.ts index 4940703fd426..eb5172693cef 100644 --- a/src/vs/editor/browser/observableCodeEditor.ts +++ b/src/vs/editor/browser/observableCodeEditor.ts @@ -14,7 +14,7 @@ import { Selection } from '../common/core/selection.js'; import { ICursorSelectionChangedEvent } from '../common/cursorEvents.js'; import { IModelDeltaDecoration, ITextModel } from '../common/model.js'; import { IModelContentChangedEvent } from '../common/textModelEvents.js'; -import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition, IMouseTarget, IOverlayWidget, IOverlayWidgetPosition, IPasteEvent } from './editorBrowser.js'; +import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition, IEditorMouseEvent, IOverlayWidget, IOverlayWidgetPosition, IPasteEvent } from './editorBrowser.js'; import { Point } from './point.js'; /** @@ -233,6 +233,7 @@ export class ObservableCodeEditor extends Disposable { public readonly layoutInfoDecorationsLeft = this.layoutInfo.map(l => l.decorationsLeft); public readonly layoutInfoWidth = this.layoutInfo.map(l => l.width); public readonly layoutInfoMinimap = this.layoutInfo.map(l => l.minimap); + public readonly layoutInfoVerticalScrollbarWidth = this.layoutInfo.map(l => l.verticalScrollbarWidth); public readonly contentWidth = observableFromEvent(this.editor.onDidContentSizeChange, () => this.editor.getContentWidth()); @@ -355,10 +356,10 @@ export class ObservableCodeEditor extends Disposable { public readonly openedPeekWidgets = observableValue(this, 0); - isTargetHovered(predicate: (target: IMouseTarget) => boolean, store: DisposableStore): IObservable { + isTargetHovered(predicate: (target: IEditorMouseEvent) => boolean, store: DisposableStore): IObservable { const isHovered = observableValue('isInjectedTextHovered', false); store.add(this.editor.onMouseMove(e => { - const val = predicate(e.target); + const val = predicate(e); isHovered.set(val, undefined); })); diff --git a/src/vs/editor/browser/rect.ts b/src/vs/editor/browser/rect.ts index e8958b5e8465..66c1bd87ebd6 100644 --- a/src/vs/editor/browser/rect.ts +++ b/src/vs/editor/browser/rect.ts @@ -108,6 +108,13 @@ export class Rect { && this.bottom >= other.bottom; } + containsPoint(point: Point): boolean { + return this.left <= point.x + && this.top <= point.y + && this.right >= point.x + && this.bottom >= point.y; + } + moveToBeContainedIn(parent: Rect): Rect { const width = this.width; const height = this.height; diff --git a/src/vs/editor/browser/services/abstractCodeEditorService.ts b/src/vs/editor/browser/services/abstractCodeEditorService.ts index 0743bcae74ad..295d5bf1ca10 100644 --- a/src/vs/editor/browser/services/abstractCodeEditorService.ts +++ b/src/vs/editor/browser/services/abstractCodeEditorService.ts @@ -7,7 +7,7 @@ import * as dom from '../../../base/browser/dom.js'; import * as domStylesheets from '../../../base/browser/domStylesheets.js'; import * as cssJs from '../../../base/browser/cssValue.js'; import { Emitter, Event } from '../../../base/common/event.js'; -import { IDisposable, DisposableStore, Disposable, toDisposable } from '../../../base/common/lifecycle.js'; +import { IDisposable, DisposableStore, Disposable, toDisposable, DisposableMap } from '../../../base/common/lifecycle.js'; import { LinkedList } from '../../../base/common/linkedList.js'; import * as strings from '../../../base/common/strings.js'; import { URI } from '../../../base/common/uri.js'; @@ -210,7 +210,7 @@ export abstract class AbstractCodeEditorService extends Disposable implements IC return provider.resolveDecorationCSSRules(); } - private readonly _transientWatchers: { [uri: string]: ModelTransientSettingWatcher } = {}; + private readonly _transientWatchers = this._register(new DisposableMap()); private readonly _modelProperties = new Map>(); public setModelProperty(resource: URI, key: string, value: any): void { @@ -238,12 +238,10 @@ export abstract class AbstractCodeEditorService extends Disposable implements IC public setTransientModelProperty(model: ITextModel, key: string, value: any): void { const uri = model.uri.toString(); - let w: ModelTransientSettingWatcher; - if (this._transientWatchers.hasOwnProperty(uri)) { - w = this._transientWatchers[uri]; - } else { + let w = this._transientWatchers.get(uri); + if (!w) { w = new ModelTransientSettingWatcher(uri, model, this); - this._transientWatchers[uri] = w; + this._transientWatchers.set(uri, w); } const previousValue = w.get(key); @@ -256,25 +254,27 @@ export abstract class AbstractCodeEditorService extends Disposable implements IC public getTransientModelProperty(model: ITextModel, key: string): any { const uri = model.uri.toString(); - if (!this._transientWatchers.hasOwnProperty(uri)) { + const watcher = this._transientWatchers.get(uri); + if (!watcher) { return undefined; } - return this._transientWatchers[uri].get(key); + return watcher.get(key); } public getTransientModelProperties(model: ITextModel): [string, any][] | undefined { const uri = model.uri.toString(); - if (!this._transientWatchers.hasOwnProperty(uri)) { + const watcher = this._transientWatchers.get(uri); + if (!watcher) { return undefined; } - return this._transientWatchers[uri].keys().map(key => [key, this._transientWatchers[uri].get(key)]); + return watcher.keys().map(key => [key, watcher.get(key)]); } _removeWatcher(w: ModelTransientSettingWatcher): void { - delete this._transientWatchers[w.uri]; + this._transientWatchers.deleteAndDispose(w.uri); } abstract getActiveCodeEditor(): ICodeEditor | null; @@ -295,14 +295,16 @@ export abstract class AbstractCodeEditorService extends Disposable implements IC } } -export class ModelTransientSettingWatcher { +export class ModelTransientSettingWatcher extends Disposable { public readonly uri: string; private readonly _values: { [key: string]: any }; constructor(uri: string, model: ITextModel, owner: AbstractCodeEditorService) { + super(); + this.uri = uri; this._values = {}; - model.onWillDispose(() => owner._removeWatcher(this)); + this._register(model.onWillDispose(() => owner._removeWatcher(this))); } public set(key: string, value: any): void { diff --git a/src/vs/editor/browser/services/hoverService/hover.css b/src/vs/editor/browser/services/hoverService/hover.css index 21f7221bf68e..8d483a541633 100644 --- a/src/vs/editor/browser/services/hoverService/hover.css +++ b/src/vs/editor/browser/services/hoverService/hover.css @@ -33,12 +33,8 @@ .monaco-workbench .workbench-hover-container.locked .workbench-hover { outline: 1px solid var(--vscode-editorHoverWidget-border); } -.monaco-workbench .workbench-hover-container.locked .workbench-hover:focus, -.monaco-workbench .workbench-hover-lock:focus { - outline: 1px solid var(--vscode-focusBorder); -} -.monaco-workbench .workbench-hover-container.locked .workbench-hover-lock:hover { - background: var(--vscode-toolbar-hoverBackground); +.monaco-workbench .workbench-hover-container:focus-within.locked .workbench-hover { + outline-color: var(--vscode-focusBorder); } .monaco-workbench .workbench-hover-pointer { @@ -57,12 +53,16 @@ border-right: 1px solid var(--vscode-editorHoverWidget-border); border-bottom: 1px solid var(--vscode-editorHoverWidget-border); } -.monaco-workbench .locked .workbench-hover-pointer:after { +.monaco-workbench .workbench-hover-container:not(:focus-within).locked .workbench-hover-pointer:after { width: 4px; height: 4px; border-right-width: 2px; border-bottom-width: 2px; } +.monaco-workbench .workbench-hover-container:focus-within .workbench-hover-pointer:after { + border-right: 1px solid var(--vscode-focusBorder); + border-bottom: 1px solid var(--vscode-focusBorder); +} .monaco-workbench .workbench-hover-pointer.left { left: -3px; } .monaco-workbench .workbench-hover-pointer.right { right: 3px; } diff --git a/src/vs/editor/browser/services/hoverService/hoverService.ts b/src/vs/editor/browser/services/hoverService/hoverService.ts index 83b8c5d2276a..523378c4c79f 100644 --- a/src/vs/editor/browser/services/hoverService/hoverService.ts +++ b/src/vs/editor/browser/services/hoverService/hoverService.ts @@ -29,6 +29,7 @@ import { isNumber } from '../../../../base/common/types.js'; import { KeyChord, KeyCode, KeyMod } from '../../../../base/common/keyCodes.js'; import { KeybindingsRegistry, KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js'; import { EditorContextKeys } from '../../../common/editorContextKeys.js'; +import { IMarkdownString } from '../../../../base/common/htmlContent.js'; export class HoverService extends Disposable implements IHoverService { declare readonly _serviceBrand: undefined; @@ -68,7 +69,7 @@ export class HoverService extends Disposable implements IHoverService { })); } - showHover(options: IHoverOptions, focus?: boolean, skipLastFocusedUpdate?: boolean, dontShow?: boolean): IHoverWidget | undefined { + showInstantHover(options: IHoverOptions, focus?: boolean, skipLastFocusedUpdate?: boolean, dontShow?: boolean): IHoverWidget | undefined { const hover = this._createHover(options, skipLastFocusedUpdate); if (!hover) { return undefined; @@ -81,6 +82,11 @@ export class HoverService extends Disposable implements IHoverService { options: IHoverOptions, lifecycleOptions: Pick, ): IHoverWidget | undefined { + // Set `id` to default if it's undefined + if (options.id === undefined) { + options.id = getHoverIdFromContent(options.content); + } + if (!this._currentDelayedHover || this._currentDelayedHoverWasShown) { // Current hover is locked, reject if (this._currentHover?.isLocked) { @@ -94,7 +100,7 @@ export class HoverService extends Disposable implements IHoverService { // Check group identity, if it's the same skip the delay and show the hover immediately if (this._currentHover && !this._currentHover.isDisposed && this._currentDelayedHoverGroupId !== undefined && this._currentDelayedHoverGroupId === lifecycleOptions?.groupId) { - return this.showHover({ + return this.showInstantHover({ ...options, appearance: { ...options.appearance, @@ -102,6 +108,9 @@ export class HoverService extends Disposable implements IHoverService { } }); } + } else if (this._currentDelayedHover && getHoverOptionsIdentity(this._currentHoverOptions) === getHoverOptionsIdentity(options)) { + // If the hover is the same but timeout is not finished yet, return the current hover + return this._currentDelayedHover; } const hover = this._createHover(options, undefined); @@ -118,7 +127,6 @@ export class HoverService extends Disposable implements IHoverService { timeout(this._configurationService.getValue('workbench.hover.delay')).then(() => { if (hover && !hover.isDisposed) { - this._currentDelayedHoverWasShown = true; this._currentDelayedHoverWasShown = true; this._showHover(hover, options); } @@ -169,12 +177,12 @@ export class HoverService extends Disposable implements IHoverService { store.add(addDisposableListener(target, EventType.KEY_DOWN, e => { const evt = new StandardKeyboardEvent(e); if (evt.equals(KeyCode.Space) || evt.equals(KeyCode.Enter)) { - this.showHover(resolveHoverOptions(), true); + this.showInstantHover(resolveHoverOptions(), true); } })); } - this._delayedHovers.set(target, { show: (focus: boolean) => { this.showHover(resolveHoverOptions(), focus); } }); + this._delayedHovers.set(target, { show: (focus: boolean) => { this.showInstantHover(resolveHoverOptions(), focus); } }); store.add(toDisposable(() => this._delayedHovers.delete(target))); return store; @@ -186,6 +194,12 @@ export class HoverService extends Disposable implements IHoverService { if (this._currentHover?.isLocked) { return undefined; } + + // Set `id` to default if it's undefined + if (options.id === undefined) { + options.id = getHoverIdFromContent(options.content); + } + if (getHoverOptionsIdentity(this._currentHoverOptions) === getHoverOptionsIdentity(options)) { return undefined; } @@ -204,15 +218,6 @@ export class HoverService extends Disposable implements IHoverService { } } - // Set `id` to default if it's undefined - if (options.id === undefined) { - options.id = isHTMLElement(options.content) - ? undefined - : typeof options.content === 'string' - ? options.content.toString() - : options.content.value; - } - const hoverDisposables = new DisposableStore(); const hover = this._instantiationService.createInstance(HoverWidget, options); if (options.persistence?.sticky) { @@ -291,8 +296,8 @@ export class HoverService extends Disposable implements IHoverService { ); } - hideHover(): void { - if (this._currentHover?.isLocked || !this._currentHoverOptions) { + hideHover(force?: boolean): void { + if ((!force && this._currentHover?.isLocked) || !this._currentHoverOptions) { return; } this.doHideHover(); @@ -315,7 +320,7 @@ export class HoverService extends Disposable implements IHoverService { if (!this._lastHoverOptions) { return; } - this.showHover(this._lastHoverOptions, true, true); + this.showInstantHover(this._lastHoverOptions, true, true); } private _showAndFocusHoverForActiveElement(): void { @@ -440,7 +445,7 @@ export class HoverService extends Disposable implements IHoverService { return; // Do not show hover when the mouse is over another hover target } - mouseOverStore.add(triggerShowHover(hoverDelegate.delay, false, target)); + mouseOverStore.add(triggerShowHover(typeof hoverDelegate.delay === 'function' ? hoverDelegate.delay(content) : hoverDelegate.delay, false, target)); }, true)); const onFocus = () => { @@ -454,7 +459,7 @@ export class HoverService extends Disposable implements IHoverService { const toDispose: DisposableStore = new DisposableStore(); const onBlur = () => hideHover(true, true); toDispose.add(addDisposableListener(targetElement, EventType.BLUR, onBlur, true)); - toDispose.add(triggerShowHover(hoverDelegate.delay, false, target)); + toDispose.add(triggerShowHover(typeof hoverDelegate.delay === 'function' ? hoverDelegate.delay(content) : hoverDelegate.delay, false, target)); hoverPreparation = toDispose; }; @@ -505,6 +510,16 @@ function getHoverOptionsIdentity(options: IHoverOptions | undefined): IHoverOpti return options?.id ?? options; } +function getHoverIdFromContent(content: string | HTMLElement | IMarkdownString): string | undefined { + if (isHTMLElement(content)) { + return undefined; + } + if (typeof content === 'string') { + return content.toString(); + } + return content.value; +} + class HoverContextViewDelegate implements IDelegate { // Render over all other context views diff --git a/src/vs/editor/browser/services/hoverService/updatableHoverWidget.ts b/src/vs/editor/browser/services/hoverService/updatableHoverWidget.ts index c5b656b99d36..19a333e0035a 100644 --- a/src/vs/editor/browser/services/hoverService/updatableHoverWidget.ts +++ b/src/vs/editor/browser/services/hoverService/updatableHoverWidget.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { isHTMLElement } from '../../../../base/browser/dom.js'; -import type { IHoverWidget, IManagedHoverContent, IManagedHoverOptions } from '../../../../base/browser/ui/hover/hover.js'; +import { isManagedHoverTooltipMarkdownString, type IHoverWidget, type IManagedHoverContent, type IManagedHoverOptions } from '../../../../base/browser/ui/hover/hover.js'; import type { IHoverDelegate, IHoverDelegateOptions, IHoverDelegateTarget } from '../../../../base/browser/ui/hover/hoverDelegate.js'; import { HoverPosition } from '../../../../base/browser/ui/hover/hoverWidget.js'; import { CancellationTokenSource } from '../../../../base/common/cancellation.js'; @@ -20,8 +20,7 @@ export class ManagedHoverWidget implements IDisposable { private _hoverWidget: IHoverWidget | undefined; private _cancellationTokenSource: CancellationTokenSource | undefined; - constructor(private hoverDelegate: IHoverDelegate, private target: IHoverDelegateTarget | HTMLElement, private fadeInAnimation: boolean) { - } + constructor(private hoverDelegate: IHoverDelegate, private target: IHoverDelegateTarget | HTMLElement, private fadeInAnimation: boolean) { } async update(content: IManagedHoverContent, focus?: boolean, options?: IManagedHoverOptions): Promise { if (this._cancellationTokenSource) { @@ -33,25 +32,37 @@ export class ManagedHoverWidget implements IDisposable { return; } - let resolvedContent; - if (content === undefined || isString(content) || isHTMLElement(content)) { + let resolvedContent: string | HTMLElement | IMarkdownString | undefined; + if (isString(content) || isHTMLElement(content) || content === undefined) { resolvedContent = content; - } else if (!isFunction(content.markdown)) { - resolvedContent = content.markdown ?? content.markdownNotSupportedFallback; } else { // compute the content, potentially long-running - // show 'Loading' if no hover is up yet - if (!this._hoverWidget) { - this.show(localize('iconLabel.loading', "Loading..."), focus, options); + this._cancellationTokenSource = new CancellationTokenSource(); + const token = this._cancellationTokenSource.token; + + let managedContent; + if (isManagedHoverTooltipMarkdownString(content)) { + if (isFunction(content.markdown)) { + managedContent = content.markdown(token).then(resolvedContent => resolvedContent ?? content.markdownNotSupportedFallback); + } else { + managedContent = content.markdown ?? content.markdownNotSupportedFallback; + } + } else { + managedContent = content.element(token); } // compute the content - this._cancellationTokenSource = new CancellationTokenSource(); - const token = this._cancellationTokenSource.token; - resolvedContent = await content.markdown(token); - if (resolvedContent === undefined) { - resolvedContent = content.markdownNotSupportedFallback; + if (managedContent instanceof Promise) { + + // show 'Loading' if no hover is up yet + if (!this._hoverWidget) { + this.show(localize('iconLabel.loading', "Loading..."), focus, options); + } + + resolvedContent = await managedContent; + } else { + resolvedContent = managedContent; } if (this.isDisposed || token.isCancellationRequested) { diff --git a/src/vs/editor/browser/view.ts b/src/vs/editor/browser/view.ts index e2a992cd339a..3588ee91b12a 100644 --- a/src/vs/editor/browser/view.ts +++ b/src/vs/editor/browser/view.ts @@ -9,7 +9,7 @@ import { IMouseWheelEvent } from '../../base/browser/mouseEvent.js'; import { inputLatency } from '../../base/browser/performance.js'; import { CodeWindow } from '../../base/browser/window.js'; import { BugIndicatingError, onUnexpectedError } from '../../base/common/errors.js'; -import { IDisposable } from '../../base/common/lifecycle.js'; +import { Disposable, IDisposable } from '../../base/common/lifecycle.js'; import { IPointerHandlerHelper } from './controller/mouseHandler.js'; import { PointerHandlerLastRenderData } from './controller/mouseTarget.js'; import { PointerHandler } from './controller/pointerHandler.js'; @@ -63,6 +63,7 @@ import { NativeEditContext } from './controller/editContext/native/nativeEditCon import { RulersGpu } from './viewParts/rulersGpu/rulersGpu.js'; import { GpuMarkOverlay } from './viewParts/gpuMark/gpuMark.js'; import { AccessibilitySupport } from '../../platform/accessibility/common/accessibility.js'; +import { Event, Emitter } from '../../base/common/event.js'; export interface IContentWidgetData { @@ -82,6 +83,8 @@ export interface IGlyphMarginWidgetData { export class View extends ViewEventHandler { + private _widgetFocusTracker: CodeEditorWidgetFocusTracker; + private readonly _scrollbar: EditorScrollbar; private readonly _context: ViewContext; private readonly _viewGpuContext?: ViewGpuContext; @@ -116,6 +119,7 @@ export class View extends ViewEventHandler { private _ownerID: string; constructor( + editorContainer: HTMLElement, ownerID: string, commandDelegate: ICommandDelegate, configuration: IEditorConfiguration, @@ -127,6 +131,14 @@ export class View extends ViewEventHandler { ) { super(); this._ownerID = ownerID; + + this._widgetFocusTracker = this._register( + new CodeEditorWidgetFocusTracker(editorContainer, overflowWidgetsDomNode) + ); + this._register(this._widgetFocusTracker.onChange(() => { + this._context.viewModel.setHasWidgetFocus(this._widgetFocusTracker.hasFocus()); + })); + this._selections = [new Selection(1, 1, 1, 1)]; this._renderAnimationFrame = null; @@ -276,11 +288,14 @@ export class View extends ViewEventHandler { this._pointerHandler = this._register(new PointerHandler(this._context, this._viewController, this._createPointerHandlerHelper())); } - private _instantiateEditContext(): AbstractEditContext { - const usingExperimentalEditContext = this._context.configuration.options.get(EditorOption.effectiveExperimentalEditContextEnabled); - if (usingExperimentalEditContext) { + private _instantiateEditContext(experimentalEditContextEnabled: boolean, accessibilitySupport: AccessibilitySupport): AbstractEditContext { + const domNode = dom.getWindow(this._overflowGuardContainer.domNode); + const isEditContextSupported = EditContext.supported(domNode); + if (experimentalEditContextEnabled && isEditContextSupported) { + console.log('creating native edit context'); return this._instantiationService.createInstance(NativeEditContext, this._ownerID, this._context, this._overflowGuardContainer, this._viewController, this._createTextAreaHandlerHelper()); } else { + console.log('creating text area edit context'); return this._instantiationService.createInstance(TextAreaEditContext, this._context, this._overflowGuardContainer, this._viewController, this._createTextAreaHandlerHelper()); } } @@ -684,8 +699,13 @@ export class View extends ViewEventHandler { return this._editContext.isFocused(); } + public isWidgetFocused(): boolean { + return this._widgetFocusTracker.hasFocus(); + } + public refreshFocusState() { this._editContext.refreshFocusState(); + this._widgetFocusTracker.refreshState(); } public setAriaOptions(options: IEditorAriaOptions): void { @@ -851,3 +871,63 @@ class EditorRenderingCoordinator { } } +class CodeEditorWidgetFocusTracker extends Disposable { + + private _hasDomElementFocus: boolean; + private readonly _domFocusTracker: dom.IFocusTracker; + private readonly _overflowWidgetsDomNode: dom.IFocusTracker | undefined; + + private readonly _onChange: Emitter = this._register(new Emitter()); + public readonly onChange: Event = this._onChange.event; + + private _overflowWidgetsDomNodeHasFocus: boolean; + + private _hadFocus: boolean | undefined = undefined; + + constructor(domElement: HTMLElement, overflowWidgetsDomNode: HTMLElement | undefined) { + super(); + + this._hasDomElementFocus = false; + this._domFocusTracker = this._register(dom.trackFocus(domElement)); + + this._overflowWidgetsDomNodeHasFocus = false; + + this._register(this._domFocusTracker.onDidFocus(() => { + this._hasDomElementFocus = true; + this._update(); + })); + this._register(this._domFocusTracker.onDidBlur(() => { + this._hasDomElementFocus = false; + this._update(); + })); + + if (overflowWidgetsDomNode) { + this._overflowWidgetsDomNode = this._register(dom.trackFocus(overflowWidgetsDomNode)); + this._register(this._overflowWidgetsDomNode.onDidFocus(() => { + this._overflowWidgetsDomNodeHasFocus = true; + this._update(); + })); + this._register(this._overflowWidgetsDomNode.onDidBlur(() => { + this._overflowWidgetsDomNodeHasFocus = false; + this._update(); + })); + } + } + + private _update() { + const focused = this._hasDomElementFocus || this._overflowWidgetsDomNodeHasFocus; + if (this._hadFocus !== focused) { + this._hadFocus = focused; + this._onChange.fire(undefined); + } + } + + public hasFocus(): boolean { + return this._hadFocus ?? false; + } + + public refreshState(): void { + this._domFocusTracker.refreshState(); + this._overflowWidgetsDomNode?.refreshState?.(); + } +} diff --git a/src/vs/editor/browser/widget/codeEditor/codeEditorWidget.ts b/src/vs/editor/browser/widget/codeEditor/codeEditorWidget.ts index 7e325a7f888b..042152e7ddea 100644 --- a/src/vs/editor/browser/widget/codeEditor/codeEditorWidget.ts +++ b/src/vs/editor/browser/widget/codeEditor/codeEditorWidget.ts @@ -231,8 +231,6 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE private readonly _commandService: ICommandService; private readonly _themeService: IThemeService; - private readonly _focusTracker: CodeEditorWidgetFocusTracker; - private _contentWidgets: { [key: string]: IContentWidgetData }; private _overlayWidgets: { [key: string]: IOverlayWidgetData }; private _glyphMarginWidgets: { [key: string]: IGlyphMarginWidgetData }; @@ -306,11 +304,6 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE this._modelData = null; - this._focusTracker = new CodeEditorWidgetFocusTracker(domElement, this._overflowWidgetsDomNode); - this._register(this._focusTracker.onChange(() => { - this._editorWidgetFocus.setValue(this._focusTracker.hasFocus()); - })); - this._contentWidgets = {}; this._overlayWidgets = {}; this._glyphMarginWidgets = {}; @@ -406,7 +399,6 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE public override dispose(): void { this._codeEditorService.removeCodeEditor(this); - this._focusTracker.dispose(); this._actions.clear(); this._contentWidgets = {}; this._overlayWidgets = {}; @@ -1033,7 +1025,6 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE public onHide(): void { this._modelData?.view.refreshFocusState(); - this._focusTracker.refreshState(); } public getContribution(id: string): T | null { @@ -1451,7 +1442,10 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE } public hasWidgetFocus(): boolean { - return this._focusTracker && this._focusTracker.hasFocus(); + if (!this._modelData || !this._modelData.hasRealView) { + return false; + } + return this._modelData.view.isWidgetFocused(); } public addContentWidget(widget: editorBrowser.IContentWidget): void { @@ -1690,6 +1684,9 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE case OutgoingViewModelEventKind.FocusChanged: this._editorTextFocus.setValue(e.hasFocus); break; + case OutgoingViewModelEventKind.WidgetFocusChanged: + this._editorWidgetFocus.setValue(e.hasFocus); + break; case OutgoingViewModelEventKind.ScrollChanged: this._onDidScrollChange.fire(e); break; @@ -1873,6 +1870,7 @@ export class CodeEditorWidget extends Disposable implements editorBrowser.ICodeE viewUserInputEvents.onMouseWheel = (e) => this._onMouseWheel.fire(e); const view = new View( + this._domElement, this.getId(), commandDelegate, this._configuration, @@ -2029,11 +2027,11 @@ const enum BooleanEventValue { } export class BooleanEventEmitter extends Disposable { - private readonly _onDidChangeToTrue: Emitter = this._register(new Emitter(this._emitterOptions)); - public readonly onDidChangeToTrue: Event = this._onDidChangeToTrue.event; + private readonly _onDidChangeToTrue: Emitter; + public readonly onDidChangeToTrue: Event; - private readonly _onDidChangeToFalse: Emitter = this._register(new Emitter(this._emitterOptions)); - public readonly onDidChangeToFalse: Event = this._onDidChangeToFalse.event; + private readonly _onDidChangeToFalse: Emitter; + public readonly onDidChangeToFalse: Event; private _value: BooleanEventValue; @@ -2041,6 +2039,10 @@ export class BooleanEventEmitter extends Disposable { private readonly _emitterOptions: EmitterOptions ) { super(); + this._onDidChangeToTrue = this._register(new Emitter(this._emitterOptions)); + this.onDidChangeToTrue = this._onDidChangeToTrue.event; + this._onDidChangeToFalse = this._register(new Emitter(this._emitterOptions)); + this.onDidChangeToFalse = this._onDidChangeToFalse.event; this._value = BooleanEventValue.NotSet; } @@ -2301,66 +2303,6 @@ export class EditorModeContext extends Disposable { } } -class CodeEditorWidgetFocusTracker extends Disposable { - - private _hasDomElementFocus: boolean; - private readonly _domFocusTracker: dom.IFocusTracker; - private readonly _overflowWidgetsDomNode: dom.IFocusTracker | undefined; - - private readonly _onChange: Emitter = this._register(new Emitter()); - public readonly onChange: Event = this._onChange.event; - - private _overflowWidgetsDomNodeHasFocus: boolean; - - private _hadFocus: boolean | undefined = undefined; - - constructor(domElement: HTMLElement, overflowWidgetsDomNode: HTMLElement | undefined) { - super(); - - this._hasDomElementFocus = false; - this._domFocusTracker = this._register(dom.trackFocus(domElement)); - - this._overflowWidgetsDomNodeHasFocus = false; - - this._register(this._domFocusTracker.onDidFocus(() => { - this._hasDomElementFocus = true; - this._update(); - })); - this._register(this._domFocusTracker.onDidBlur(() => { - this._hasDomElementFocus = false; - this._update(); - })); - - if (overflowWidgetsDomNode) { - this._overflowWidgetsDomNode = this._register(dom.trackFocus(overflowWidgetsDomNode)); - this._register(this._overflowWidgetsDomNode.onDidFocus(() => { - this._overflowWidgetsDomNodeHasFocus = true; - this._update(); - })); - this._register(this._overflowWidgetsDomNode.onDidBlur(() => { - this._overflowWidgetsDomNodeHasFocus = false; - this._update(); - })); - } - } - - private _update() { - const focused = this._hasDomElementFocus || this._overflowWidgetsDomNodeHasFocus; - if (this._hadFocus !== focused) { - this._hadFocus = focused; - this._onChange.fire(undefined); - } - } - - public hasFocus(): boolean { - return this._hadFocus ?? false; - } - - public refreshState(): void { - this._domFocusTracker.refreshState(); - this._overflowWidgetsDomNode?.refreshState?.(); - } -} class EditorDecorationsCollection implements editorCommon.IEditorDecorationsCollection { diff --git a/src/vs/editor/browser/widget/diffEditor/features/gutterFeature.ts b/src/vs/editor/browser/widget/diffEditor/features/gutterFeature.ts index a9aaeab304d5..0d8b665b9e46 100644 --- a/src/vs/editor/browser/widget/diffEditor/features/gutterFeature.ts +++ b/src/vs/editor/browser/widget/diffEditor/features/gutterFeature.ts @@ -218,7 +218,7 @@ class DiffToolBar extends Disposable implements IGutterItemView { const hoverDelegate = this._register(instantiationService.createInstance( WorkbenchHoverDelegate, 'element', - true, + { instantHover: true }, { position: { hoverPosition: HoverPosition.RIGHT } } )); diff --git a/src/vs/editor/browser/widget/diffEditor/style.css b/src/vs/editor/browser/widget/diffEditor/style.css index 8c027c33e1ce..8a461de93ef4 100644 --- a/src/vs/editor/browser/widget/diffEditor/style.css +++ b/src/vs/editor/browser/widget/diffEditor/style.css @@ -382,7 +382,7 @@ .actions-container { width: fit-content; border-radius: 4px; - background: var(--vscode-editorGutter-commentRangeForeground); + background: var(--vscode-editorGutter-itemBackground); .action-item { &:hover { @@ -390,6 +390,7 @@ } .action-label { + color: var(--vscode-editorGutter-itemGlyphForeground); padding: 1px 2px; } } diff --git a/src/vs/editor/common/codecs/markdownCodec/markdownDecoder.ts b/src/vs/editor/common/codecs/markdownCodec/markdownDecoder.ts index 3703fa1df29c..9da1394cf82e 100644 --- a/src/vs/editor/common/codecs/markdownCodec/markdownDecoder.ts +++ b/src/vs/editor/common/codecs/markdownCodec/markdownDecoder.ts @@ -3,232 +3,35 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { MarkdownLink } from './tokens/markdownLink.js'; -import { NewLine } from '../linesCodec/tokens/newLine.js'; -import { assert } from '../../../../base/common/assert.js'; -import { FormFeed } from '../simpleCodec/tokens/formFeed.js'; +import { MarkdownToken } from './tokens/markdownToken.js'; import { VSBuffer } from '../../../../base/common/buffer.js'; -import { VerticalTab } from '../simpleCodec/tokens/verticalTab.js'; +import { LeftBracket } from '../simpleCodec/tokens/brackets.js'; import { ReadableStream } from '../../../../base/common/stream.js'; -import { CarriageReturn } from '../linesCodec/tokens/carriageReturn.js'; +import { LeftAngleBracket } from '../simpleCodec/tokens/angleBrackets.js'; import { BaseDecoder } from '../../../../base/common/codecs/baseDecoder.js'; -import { LeftBracket, RightBracket } from '../simpleCodec/tokens/brackets.js'; import { SimpleDecoder, TSimpleToken } from '../simpleCodec/simpleDecoder.js'; -import { ParserBase, TAcceptTokenResult } from '../simpleCodec/parserBase.js'; -import { LeftParenthesis, RightParenthesis } from '../simpleCodec/tokens/parentheses.js'; +import { MarkdownCommentStart, PartialMarkdownCommentStart } from './parsers/markdownComment.js'; +import { MarkdownLinkCaption, PartialMarkdownLink, PartialMarkdownLinkCaption } from './parsers/markdownLink.js'; +import { ExclamationMark } from '../simpleCodec/tokens/exclamationMark.js'; +import { PartialMarkdownImage } from './parsers/markdownImage.js'; /** * Tokens handled by this decoder. */ -export type TMarkdownToken = MarkdownLink | TSimpleToken; +export type TMarkdownToken = MarkdownToken | TSimpleToken; /** - * List of characters that stop a markdown link sequence. - */ -const MARKDOWN_LINK_STOP_CHARACTERS: readonly string[] = [CarriageReturn, NewLine, VerticalTab, FormFeed] - .map((token) => { return token.symbol; }); - -/** - * The parser responsible for parsing a `markdown link caption` part of a markdown - * link (e.g., the `[caption text]` part of the `[caption text](./some/path)` link). - * - * The parsing process starts with single `[` token and collects all tokens until - * the first `]` token is encountered. In this successful case, the parser transitions - * into the {@linkcode MarkdownLinkCaption} parser type which continues the general - * parsing process of the markdown link. - * - * Otherwise, if one of the stop characters defined in the {@linkcode MARKDOWN_LINK_STOP_CHARACTERS} - * is encountered before the `]` token, the parsing process is aborted which is communicated to - * the caller by returning a `failure` result. In this case, the caller is assumed to be responsible - * for re-emitting the {@link tokens} accumulated so far as standalone entities since they are no - * longer represent a coherent token entity of a larger size. - */ -class PartialMarkdownLinkCaption extends ParserBase { - constructor(token: LeftBracket) { - super([token]); - } - - public accept(token: TSimpleToken): TAcceptTokenResult { - // any of stop characters is are breaking a markdown link caption sequence - if (MARKDOWN_LINK_STOP_CHARACTERS.includes(token.text)) { - return { - result: 'failure', - wasTokenConsumed: false, - }; - } - - // the `]` character ends the caption of a markdown link - if (token instanceof RightBracket) { - return { - result: 'success', - nextParser: new MarkdownLinkCaption([...this.tokens, token]), - wasTokenConsumed: true, - }; - } - - // otherwise, include the token in the sequence - // and keep the current parser object instance - this.currentTokens.push(token); - return { - result: 'success', - nextParser: this, - wasTokenConsumed: true, - }; - } -} - -/** - * The parser responsible for transitioning from a {@linkcode PartialMarkdownLinkCaption} - * parser to the {@link PartialMarkdownLink} one, therefore serves a parser glue between - * the `[caption]` and the `(./some/path)` parts of the `[caption](./some/path)` link. - * - * The only successful case of this parser is the `(` token that initiated the process - * of parsing the `reference` part of a markdown link and in this case the parser - * transitions into the `PartialMarkdownLink` parser type. - * - * Any other character is considered a failure result. In this case, the caller is assumed - * to be responsible for re-emitting the {@link tokens} accumulated so far as standalone - * entities since they are no longer represent a coherent token entity of a larger size. - */ -class MarkdownLinkCaption extends ParserBase { - public accept(token: TSimpleToken): TAcceptTokenResult { - // the `(` character starts the link part of a markdown link - // that is the only character that can follow the caption - if (token instanceof LeftParenthesis) { - return { - result: 'success', - wasTokenConsumed: true, - nextParser: new PartialMarkdownLink([...this.tokens], token), - }; - } - - return { - result: 'failure', - wasTokenConsumed: false, - }; - } -} - -/** - * The parser responsible for parsing a `link reference` part of a markdown link - * (e.g., the `(./some/path)` part of the `[caption text](./some/path)` link). - * - * The parsing process starts with tokens that represent the `[caption]` part of a markdown - * link, followed by the `(` token. The parser collects all subsequent tokens until final closing - * parenthesis (`)`) is encountered (*\*see [1] below*). In this successful case, the parser object - * transitions into the {@linkcode MarkdownLink} token type which signifies the end of the entire - * parsing process of the link text. - * - * Otherwise, if one of the stop characters defined in the {@linkcode MARKDOWN_LINK_STOP_CHARACTERS} - * is encountered before the final `)` token, the parsing process is aborted which is communicated to - * the caller by returning a `failure` result. In this case, the caller is assumed to be responsible - * for re-emitting the {@link tokens} accumulated so far as standalone entities since they are no - * longer represent a coherent token entity of a larger size. - * - * `[1]` The `reference` part of the markdown link can contain any number of nested parenthesis, e.g., - * `[caption](/some/p(th/file.md)` is a valid markdown link and a valid folder name, hence number - * of open parenthesis must match the number of closing ones and the path sequence is considered - * to be complete as soon as this requirement is met. Therefore the `final` word is used in - * the description comments above to highlight this important detail. - */ -class PartialMarkdownLink extends ParserBase { - /** - * Number of open parenthesis in the sequence. - * See comment in the {@linkcode accept} method for more details. - */ - private openParensCount: number = 1; - - constructor( - protected readonly captionTokens: TSimpleToken[], - token: LeftParenthesis, - ) { - super([token]); - } - - public override get tokens(): readonly TSimpleToken[] { - return [...this.captionTokens, ...this.currentTokens]; - } - - public accept(token: TSimpleToken): TAcceptTokenResult { - // markdown links allow for nested parenthesis inside the link reference part, but - // the number of open parenthesis must match the number of closing parenthesis, e.g.: - // - `[caption](/some/p()th/file.md)` is a valid markdown link - // - `[caption](/some/p(th/file.md)` is an invalid markdown link - // hence we use the `openParensCount` variable to keep track of the number of open - // parenthesis encountered so far; then upon encountering a closing parenthesis we - // decrement the `openParensCount` and if it reaches 0 - we consider the link reference - // to be complete - - if (token instanceof LeftParenthesis) { - this.openParensCount += 1; - } - - if (token instanceof RightParenthesis) { - this.openParensCount -= 1; - - // sanity check! this must alway hold true because we return a complete markdown - // link as soon as we encounter matching number of closing parenthesis, hence - // we must never have `openParensCount` that is less than 0 - assert( - this.openParensCount >= 0, - `Unexpected right parenthesis token encountered: '${token}'.`, - ); - - // the markdown link is complete as soon as we get the same number of closing parenthesis - if (this.openParensCount === 0) { - const { startLineNumber, startColumn } = this.captionTokens[0].range; - - // create link caption string - const caption = this.captionTokens - .map((token) => { return token.text; }) - .join(''); - - // create link reference string - this.currentTokens.push(token); - const reference = this.currentTokens - .map((token) => { return token.text; }).join(''); - - // return complete markdown link object - return { - result: 'success', - wasTokenConsumed: true, - nextParser: new MarkdownLink( - startLineNumber, - startColumn, - caption, - reference, - ), - }; - } - } - - // any of stop characters is are breaking a markdown link reference sequence - if (MARKDOWN_LINK_STOP_CHARACTERS.includes(token.text)) { - return { - result: 'failure', - wasTokenConsumed: false, - }; - } - - // the rest of the tokens can be included in the sequence - this.currentTokens.push(token); - return { - result: 'success', - nextParser: this, - wasTokenConsumed: true, - }; - } -} - -/** - * Decoder capable of parsing markdown entities (e.g., links) from a sequence of simplier tokens. + * Decoder capable of parsing markdown entities (e.g., links) from a sequence of simple tokens. */ export class MarkdownDecoder extends BaseDecoder { /** - * Current parser object that is responsible for parsing a sequence of tokens - * into some markdown entity. + * Current parser object that is responsible for parsing a sequence of tokens into + * some markdown entity. Set to `undefined` when no parsing is in progress at the moment. */ - private current?: PartialMarkdownLinkCaption | MarkdownLinkCaption | PartialMarkdownLink; + private current?: + PartialMarkdownLinkCaption | MarkdownLinkCaption | PartialMarkdownLink | + PartialMarkdownCommentStart | MarkdownCommentStart | + PartialMarkdownImage; constructor( stream: ReadableStream, @@ -237,7 +40,7 @@ export class MarkdownDecoder extends BaseDecoder { } protected override onStreamData(token: TSimpleToken): void { - // markdown links start with `[` character, so here we can + // `markdown links` start with `[` character, so here we can // initiate the process of parsing a markdown link if (token instanceof LeftBracket && !this.current) { this.current = new PartialMarkdownLinkCaption(token); @@ -245,9 +48,24 @@ export class MarkdownDecoder extends BaseDecoder { return; } - // if current parser was not initiated before, - we are not inside a - // sequence of tokens we care about, therefore re-emit the token - // immediately and continue to the next one + // `markdown comments` start with `<` character, so here we can + // initiate the process of parsing a markdown comment + if (token instanceof LeftAngleBracket && !this.current) { + this.current = new PartialMarkdownCommentStart(token); + + return; + } + + // `markdown image links` start with `!` character, so here we can + // initiate the process of parsing a markdown image + if (token instanceof ExclamationMark && !this.current) { + this.current = new PartialMarkdownImage(token); + + return; + } + + // if current parser was not initiated before, - we are not inside a sequence + // of tokens we care about, therefore re-emit the token immediately and continue if (!this.current) { this._onData.fire(token); return; @@ -257,14 +75,16 @@ export class MarkdownDecoder extends BaseDecoder { // so it can progress with parsing the tokens sequence const parseResult = this.current.accept(token); if (parseResult.result === 'success') { - // if got a parsed out `MarkdownLink` back, emit it - // then reset the current parser object - if (parseResult.nextParser instanceof MarkdownLink) { - this._onData.fire(parseResult.nextParser); + const { nextParser } = parseResult; + + // if got a fully parsed out token back, emit it and reset + // the current parser object so a new parsing process can start + if (nextParser instanceof MarkdownToken) { + this._onData.fire(nextParser); delete this.current; } else { // otherwise, update the current parser object - this.current = parseResult.nextParser; + this.current = nextParser; } } else { // if failed to parse a sequence of a tokens as a single markdown @@ -286,8 +106,18 @@ export class MarkdownDecoder extends BaseDecoder { protected override onStreamEnd(): void { // if the stream has ended and there is a current incomplete parser - // object present, then re-emit its tokens as standalone entities + // object present, handle the remaining parser object if (this.current) { + // if a `markdown comment` does not have an end marker `-->` + // it is still a comment that extends to the end of the file + // so re-emit the current parser as a comment token + if (this.current instanceof MarkdownCommentStart) { + this._onData.fire(this.current.asMarkdownComment()); + delete this.current; + return this.onStreamEnd(); + } + + // in all other cases, re-emit existing parser tokens const { tokens } = this.current; delete this.current; diff --git a/src/vs/editor/common/codecs/markdownCodec/parsers/markdownComment.ts b/src/vs/editor/common/codecs/markdownCodec/parsers/markdownComment.ts new file mode 100644 index 000000000000..df5f3f028ab8 --- /dev/null +++ b/src/vs/editor/common/codecs/markdownCodec/parsers/markdownComment.ts @@ -0,0 +1,177 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Range } from '../../../core/range.js'; +import { Dash } from '../../simpleCodec/tokens/dash.js'; +import { pick } from '../../../../../base/common/arrays.js'; +import { assert } from '../../../../../base/common/assert.js'; +import { MarkdownComment } from '../tokens/markdownComment.js'; +import { TSimpleToken } from '../../simpleCodec/simpleDecoder.js'; +import { ExclamationMark } from '../../simpleCodec/tokens/exclamationMark.js'; +import { LeftAngleBracket, RightAngleBracket } from '../../simpleCodec/tokens/angleBrackets.js'; +import { assertNotConsumed, ParserBase, TAcceptTokenResult } from '../../simpleCodec/parserBase.js'; + +/** + * The parser responsible for parsing the ``. If it does, + * then the parser transitions to the {@link MarkdownComment} token. + */ +export class MarkdownCommentStart extends ParserBase { + constructor(tokens: [LeftAngleBracket, ExclamationMark, Dash, Dash]) { + super(tokens); + } + + @assertNotConsumed + public accept(token: TSimpleToken): TAcceptTokenResult { + // if received `>` while current token sequence ends with `--`, + // then this is the end of the comment sequence + if (token instanceof RightAngleBracket && this.endsWithDashes) { + this.currentTokens.push(token); + + return { + result: 'success', + nextParser: this.asMarkdownComment(), + wasTokenConsumed: true, + }; + } + + this.currentTokens.push(token); + + return { + result: 'success', + nextParser: this, + wasTokenConsumed: true, + }; + } + + /** + * Convert the current token sequence into a {@link MarkdownComment} token. + * + * Note! that this method marks the current parser object as "consumend" + * hence it should not be used after this method is called. + */ + public asMarkdownComment(): MarkdownComment { + this.isConsumed = true; + + const text = this.currentTokens + .map(pick('text')) + .join(''); + + return new MarkdownComment( + this.range, + text, + ); + } + + /** + * Get range of current token sequence. + */ + private get range(): Range { + const firstToken = this.currentTokens[0]; + const lastToken = this.currentTokens[this.currentTokens.length - 1]; + + const range = new Range( + firstToken.range.startLineNumber, + firstToken.range.startColumn, + lastToken.range.endLineNumber, + lastToken.range.endColumn, + ); + + return range; + } + + /** + * Whether the current token sequence ends with two dashes. + */ + private get endsWithDashes(): boolean { + const lastToken = this.currentTokens[this.currentTokens.length - 1]; + if (!(lastToken instanceof Dash)) { + return false; + } + + const secondLastToken = this.currentTokens[this.currentTokens.length - 2]; + if (!(secondLastToken instanceof Dash)) { + return false; + } + + return true; + } +} diff --git a/src/vs/editor/common/codecs/markdownCodec/parsers/markdownImage.ts b/src/vs/editor/common/codecs/markdownCodec/parsers/markdownImage.ts new file mode 100644 index 000000000000..a3264f7cbdd7 --- /dev/null +++ b/src/vs/editor/common/codecs/markdownCodec/parsers/markdownImage.ts @@ -0,0 +1,99 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { MarkdownLink } from '../tokens/markdownLink.js'; +import { MarkdownImage } from '../tokens/markdownImage.js'; +import { TSimpleToken } from '../../simpleCodec/simpleDecoder.js'; +import { LeftBracket } from '../../simpleCodec/tokens/brackets.js'; +import { ExclamationMark } from '../../simpleCodec/tokens/exclamationMark.js'; +import { assertNotConsumed, ParserBase, TAcceptTokenResult } from '../../simpleCodec/parserBase.js'; +import { MarkdownLinkCaption, PartialMarkdownLink, PartialMarkdownLinkCaption } from './markdownLink.js'; + +/** + * The parser responsible for parsing the `markdown image` sequence of characters. + * E.g., `![alt text](./path/to/image.jpeg)` syntax. + */ +export class PartialMarkdownImage extends ParserBase { + /** + * Current active parser instance, if in the mode of actively parsing the markdown link sequence. + */ + private markdownLinkParser: PartialMarkdownLinkCaption | MarkdownLinkCaption | PartialMarkdownLink | undefined; + + constructor(token: ExclamationMark) { + super([token]); + } + + /** + * Get all currently available tokens of the `markdown link` sequence. + */ + public override get tokens(): readonly TSimpleToken[] { + const linkTokens = this.markdownLinkParser?.tokens ?? []; + + return [ + ...this.currentTokens, + ...linkTokens, + ]; + } + + @assertNotConsumed + public accept(token: TSimpleToken): TAcceptTokenResult { + // on the first call we expect a character that begins `markdown link` sequence + // hence we initiate the markdown link parsing process, otherwise we fail + if (!this.markdownLinkParser) { + if (token instanceof LeftBracket) { + this.markdownLinkParser = new PartialMarkdownLinkCaption(token); + + return { + result: 'success', + nextParser: this, + wasTokenConsumed: true, + }; + } + + return { + result: 'failure', + wasTokenConsumed: false, + }; + } + + // handle subsequent tokens next + + const acceptResult = this.markdownLinkParser.accept(token); + const { result, wasTokenConsumed } = acceptResult; + + if (result === 'success') { + const { nextParser } = acceptResult; + + // if full markdown link was parsed out, the process completes + if (nextParser instanceof MarkdownLink) { + this.isConsumed = true; + + const firstToken = this.currentTokens[0]; + return { + result, + wasTokenConsumed, + nextParser: new MarkdownImage( + firstToken.range.startLineNumber, + firstToken.range.startColumn, + `${firstToken.text}${nextParser.caption}`, + nextParser.reference, + ), + }; + } + + // otherwise save new link parser reference and continue + this.markdownLinkParser = nextParser; + return { + result, + wasTokenConsumed, + nextParser: this, + }; + } + + // return the failure result + this.isConsumed = true; + return acceptResult; + } +} diff --git a/src/vs/editor/common/codecs/markdownCodec/parsers/markdownLink.ts b/src/vs/editor/common/codecs/markdownCodec/parsers/markdownLink.ts new file mode 100644 index 000000000000..e8163286fd2c --- /dev/null +++ b/src/vs/editor/common/codecs/markdownCodec/parsers/markdownLink.ts @@ -0,0 +1,213 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { MarkdownLink } from '../tokens/markdownLink.js'; +import { NewLine } from '../../linesCodec/tokens/newLine.js'; +import { assert } from '../../../../../base/common/assert.js'; +import { FormFeed } from '../../simpleCodec/tokens/formFeed.js'; +import { TSimpleToken } from '../../simpleCodec/simpleDecoder.js'; +import { VerticalTab } from '../../simpleCodec/tokens/verticalTab.js'; +import { CarriageReturn } from '../../linesCodec/tokens/carriageReturn.js'; +import { LeftBracket, RightBracket } from '../../simpleCodec/tokens/brackets.js'; +import { ParserBase, TAcceptTokenResult } from '../../simpleCodec/parserBase.js'; +import { LeftParenthesis, RightParenthesis } from '../../simpleCodec/tokens/parentheses.js'; + +/** + * List of characters that are not allowed in links so stop a markdown link sequence abruptly. + */ +const MARKDOWN_LINK_STOP_CHARACTERS: readonly string[] = [CarriageReturn, NewLine, VerticalTab, FormFeed] + .map((token) => { return token.symbol; }); + +/** + * The parser responsible for parsing a `markdown link caption` part of a markdown + * link (e.g., the `[caption text]` part of the `[caption text](./some/path)` link). + * + * The parsing process starts with single `[` token and collects all tokens until + * the first `]` token is encountered. In this successful case, the parser transitions + * into the {@linkcode MarkdownLinkCaption} parser type which continues the general + * parsing process of the markdown link. + * + * Otherwise, if one of the stop characters defined in the {@linkcode MARKDOWN_LINK_STOP_CHARACTERS} + * is encountered before the `]` token, the parsing process is aborted which is communicated to + * the caller by returning a `failure` result. In this case, the caller is assumed to be responsible + * for re-emitting the {@link tokens} accumulated so far as standalone entities since they are no + * longer represent a coherent token entity of a larger size. + */ +export class PartialMarkdownLinkCaption extends ParserBase { + constructor(token: LeftBracket) { + super([token]); + } + + public accept(token: TSimpleToken): TAcceptTokenResult { + // any of stop characters is are breaking a markdown link caption sequence + if (MARKDOWN_LINK_STOP_CHARACTERS.includes(token.text)) { + return { + result: 'failure', + wasTokenConsumed: false, + }; + } + + // the `]` character ends the caption of a markdown link + if (token instanceof RightBracket) { + return { + result: 'success', + nextParser: new MarkdownLinkCaption([...this.tokens, token]), + wasTokenConsumed: true, + }; + } + + // otherwise, include the token in the sequence + // and keep the current parser object instance + this.currentTokens.push(token); + return { + result: 'success', + nextParser: this, + wasTokenConsumed: true, + }; + } +} + +/** + * The parser responsible for transitioning from a {@linkcode PartialMarkdownLinkCaption} + * parser to the {@link PartialMarkdownLink} one, therefore serves a parser glue between + * the `[caption]` and the `(./some/path)` parts of the `[caption](./some/path)` link. + * + * The only successful case of this parser is the `(` token that initiated the process + * of parsing the `reference` part of a markdown link and in this case the parser + * transitions into the `PartialMarkdownLink` parser type. + * + * Any other character is considered a failure result. In this case, the caller is assumed + * to be responsible for re-emitting the {@link tokens} accumulated so far as standalone + * entities since they are no longer represent a coherent token entity of a larger size. + */ +export class MarkdownLinkCaption extends ParserBase { + public accept(token: TSimpleToken): TAcceptTokenResult { + // the `(` character starts the link part of a markdown link + // that is the only character that can follow the caption + if (token instanceof LeftParenthesis) { + return { + result: 'success', + wasTokenConsumed: true, + nextParser: new PartialMarkdownLink([...this.tokens], token), + }; + } + + return { + result: 'failure', + wasTokenConsumed: false, + }; + } +} + +/** + * The parser responsible for parsing a `link reference` part of a markdown link + * (e.g., the `(./some/path)` part of the `[caption text](./some/path)` link). + * + * The parsing process starts with tokens that represent the `[caption]` part of a markdown + * link, followed by the `(` token. The parser collects all subsequent tokens until final closing + * parenthesis (`)`) is encountered (*\*see [1] below*). In this successful case, the parser object + * transitions into the {@linkcode MarkdownLink} token type which signifies the end of the entire + * parsing process of the link text. + * + * Otherwise, if one of the stop characters defined in the {@linkcode MARKDOWN_LINK_STOP_CHARACTERS} + * is encountered before the final `)` token, the parsing process is aborted which is communicated to + * the caller by returning a `failure` result. In this case, the caller is assumed to be responsible + * for re-emitting the {@link tokens} accumulated so far as standalone entities since they are no + * longer represent a coherent token entity of a larger size. + * + * `[1]` The `reference` part of the markdown link can contain any number of nested parenthesis, e.g., + * `[caption](/some/p(th/file.md)` is a valid markdown link and a valid folder name, hence number + * of open parenthesis must match the number of closing ones and the path sequence is considered + * to be complete as soon as this requirement is met. Therefore the `final` word is used in + * the description comments above to highlight this important detail. + */ +export class PartialMarkdownLink extends ParserBase { + /** + * Number of open parenthesis in the sequence. + * See comment in the {@linkcode accept} method for more details. + */ + private openParensCount: number = 1; + + constructor( + protected readonly captionTokens: TSimpleToken[], + token: LeftParenthesis, + ) { + super([token]); + } + + public override get tokens(): readonly TSimpleToken[] { + return [...this.captionTokens, ...this.currentTokens]; + } + + public accept(token: TSimpleToken): TAcceptTokenResult { + // markdown links allow for nested parenthesis inside the link reference part, but + // the number of open parenthesis must match the number of closing parenthesis, e.g.: + // - `[caption](/some/p()th/file.md)` is a valid markdown link + // - `[caption](/some/p(th/file.md)` is an invalid markdown link + // hence we use the `openParensCount` variable to keep track of the number of open + // parenthesis encountered so far; then upon encountering a closing parenthesis we + // decrement the `openParensCount` and if it reaches 0 - we consider the link reference + // to be complete + + if (token instanceof LeftParenthesis) { + this.openParensCount += 1; + } + + if (token instanceof RightParenthesis) { + this.openParensCount -= 1; + + // sanity check! this must alway hold true because we return a complete markdown + // link as soon as we encounter matching number of closing parenthesis, hence + // we must never have `openParensCount` that is less than 0 + assert( + this.openParensCount >= 0, + `Unexpected right parenthesis token encountered: '${token}'.`, + ); + + // the markdown link is complete as soon as we get the same number of closing parenthesis + if (this.openParensCount === 0) { + const { startLineNumber, startColumn } = this.captionTokens[0].range; + + // create link caption string + const caption = this.captionTokens + .map((token) => { return token.text; }) + .join(''); + + // create link reference string + this.currentTokens.push(token); + const reference = this.currentTokens + .map((token) => { return token.text; }).join(''); + + // return complete markdown link object + return { + result: 'success', + wasTokenConsumed: true, + nextParser: new MarkdownLink( + startLineNumber, + startColumn, + caption, + reference, + ), + }; + } + } + + // any of stop characters is are breaking a markdown link reference sequence + if (MARKDOWN_LINK_STOP_CHARACTERS.includes(token.text)) { + return { + result: 'failure', + wasTokenConsumed: false, + }; + } + + // the rest of the tokens can be included in the sequence + this.currentTokens.push(token); + return { + result: 'success', + nextParser: this, + wasTokenConsumed: true, + }; + } +} diff --git a/src/vs/editor/common/codecs/markdownCodec/tokens/markdownComment.ts b/src/vs/editor/common/codecs/markdownCodec/tokens/markdownComment.ts new file mode 100644 index 000000000000..f7875d957a28 --- /dev/null +++ b/src/vs/editor/common/codecs/markdownCodec/tokens/markdownComment.ts @@ -0,0 +1,56 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { BaseToken } from '../../baseToken.js'; +import { Range } from '../../../core/range.js'; +import { MarkdownToken } from './markdownToken.js'; +import { assert } from '../../../../../base/common/assert.js'; + +/** + * A token that represent a `markdown comment` with a `range`. The `range` + * value reflects the position of the token in the original data. + */ +export class MarkdownComment extends MarkdownToken { + constructor( + range: Range, + public readonly text: string, + ) { + assert( + text.startsWith('`. + */ + public get hasEndMarker(): boolean { + return this.text.endsWith('-->'); + } + + /** + * Check if this token is equal to another one. + */ + public override equals(other: T): boolean { + if (!super.sameRange(other.range)) { + return false; + } + + if (!(other instanceof MarkdownComment)) { + return false; + } + + return this.text === other.text; + } + + /** + * Returns a string representation of the token. + */ + public override toString(): string { + return `md-comment("${this.text}")${this.range}`; + } +} diff --git a/src/vs/editor/common/codecs/markdownCodec/tokens/markdownImage.ts b/src/vs/editor/common/codecs/markdownCodec/tokens/markdownImage.ts new file mode 100644 index 000000000000..75e42e31aaf0 --- /dev/null +++ b/src/vs/editor/common/codecs/markdownCodec/tokens/markdownImage.ts @@ -0,0 +1,141 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { BaseToken } from '../../baseToken.js'; +import { MarkdownToken } from './markdownToken.js'; +import { IRange, Range } from '../../../core/range.js'; +import { assert } from '../../../../../base/common/assert.js'; + +/** + * A token that represent a `markdown image` with a `range`. The `range` + * value reflects the position of the token in the original data. + */ +export class MarkdownImage extends MarkdownToken { + /** + * Check if this `markdown image link` points to a valid URL address. + */ + public readonly isURL: boolean; + + constructor( + /** + * The starting line number of the image (1-based indexing). + */ + lineNumber: number, + /** + * The starting column number of the image (1-based indexing). + */ + columnNumber: number, + /** + * The caption of the image, including the `!` and `square brackets`. + */ + private readonly caption: string, + /** + * The reference of the image, including the parentheses. + */ + private readonly reference: string, + ) { + assert( + !isNaN(lineNumber), + `The line number must not be a NaN.`, + ); + + assert( + lineNumber > 0, + `The line number must be >= 1, got "${lineNumber}".`, + ); + + assert( + columnNumber > 0, + `The column number must be >= 1, got "${columnNumber}".`, + ); + + assert( + caption[0] === '!', + `The caption must start with '!' character, got "${caption}".`, + ); + + assert( + caption[1] === '[' && caption[caption.length - 1] === ']', + `The caption must be enclosed in square brackets, got "${caption}".`, + ); + + assert( + reference[0] === '(' && reference[reference.length - 1] === ')', + `The reference must be enclosed in parentheses, got "${reference}".`, + ); + + super( + new Range( + lineNumber, + columnNumber, + lineNumber, + columnNumber + caption.length + reference.length, + ), + ); + + // set up the `isURL` flag based on the current + try { + new URL(this.path); + this.isURL = true; + } catch { + this.isURL = false; + } + } + + public override get text(): string { + return `${this.caption}${this.reference}`; + } + + /** + * Returns the `reference` part of the link without enclosing parentheses. + */ + public get path(): string { + return this.reference.slice(1, this.reference.length - 1); + } + + /** + * Check if this token is equal to another one. + */ + public override equals(other: T): boolean { + if (!super.sameRange(other.range)) { + return false; + } + + if (!(other instanceof MarkdownImage)) { + return false; + } + + return this.text === other.text; + } + + /** + * Get the range of the `link part` of the token. + */ + public get linkRange(): IRange | undefined { + if (this.path.length === 0) { + return undefined; + } + + const { range } = this; + + // note! '+1' for openning `(` of the link + const startColumn = range.startColumn + this.caption.length + 1; + const endColumn = startColumn + this.path.length; + + return new Range( + range.startLineNumber, + startColumn, + range.endLineNumber, + endColumn, + ); + } + + /** + * Returns a string representation of the token. + */ + public override toString(): string { + return `md-image("${this.text}")${this.range}`; + } +} diff --git a/src/vs/editor/common/codecs/markdownCodec/tokens/markdownLink.ts b/src/vs/editor/common/codecs/markdownCodec/tokens/markdownLink.ts index b4c8947a2131..a4b157187172 100644 --- a/src/vs/editor/common/codecs/markdownCodec/tokens/markdownLink.ts +++ b/src/vs/editor/common/codecs/markdownCodec/tokens/markdownLink.ts @@ -28,13 +28,13 @@ export class MarkdownLink extends MarkdownToken { */ columnNumber: number, /** - * The caption of the link, including the square brackets. + * The caption of the original link, including the square brackets. */ - private readonly caption: string, + public readonly caption: string, /** - * The reference of the link, including the parentheses. + * The reference of the original link, including the parentheses. */ - private readonly reference: string, + public readonly reference: string, ) { assert( !isNaN(lineNumber), diff --git a/src/vs/editor/common/codecs/simpleCodec/parserBase.ts b/src/vs/editor/common/codecs/simpleCodec/parserBase.ts index 9e864177f9fd..e088a18f2641 100644 --- a/src/vs/editor/common/codecs/simpleCodec/parserBase.ts +++ b/src/vs/editor/common/codecs/simpleCodec/parserBase.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { BaseToken } from '../baseToken.js'; +import { assert } from '../../../../base/common/assert.js'; /** * Common interface for a result of accepting a next token @@ -47,12 +48,24 @@ export type TAcceptTokenResult = IAcceptTokenSuccess | IAcceptTokenFailure * tokens into a new single entity. */ export abstract class ParserBase { + /** + * Whether the parser object was "consumed" and should not be used anymore. + */ + protected isConsumed: boolean = false; + + /** + * Number of tokens at the initialization of the current parser. + */ + protected readonly startTokensCount: number; + constructor( /** * Set of tokens that were accumulated so far. */ protected readonly currentTokens: TToken[] = [], - ) { } + ) { + this.startTokensCount = this.currentTokens.length; + } /** * Get the tokens that were accumulated so far. @@ -70,4 +83,48 @@ export abstract class ParserBase { * @returns The parsing result. */ public abstract accept(token: TToken): TAcceptTokenResult; + + /** + * A helper method that validates that the current parser object was not yet consumed, + * hence can still be used to accept new tokens in the parsing process. + * + * @throws if the parser object is already consumed. + */ + protected assertNotConsumed(): void { + assert( + this.isConsumed === false, + `The parser object is already consumed and should not be used anymore.`, + ); + } +} + +/** + * Decorator that validates that the current parser object was not yet consumed, + * hence can still be used to accept new tokens in the parsing process. + * + * @throws the resulting decorated method throws if the parser object was already consumed. + */ +export function assertNotConsumed>( + _target: T, + propertyKey: 'accept', + descriptor: PropertyDescriptor, +) { + // store the original method reference + const originalMethod = descriptor.value; + + // validate that the current parser object was not yet consumed + // before invoking the original accept method + descriptor.value = function ( + this: T, + ...args: Parameters + ): ReturnType { + assert( + this.isConsumed === false, + `The parser object is already consumed and should not be used anymore.`, + ); + + return originalMethod.apply(this, args); + }; + + return descriptor; } diff --git a/src/vs/editor/common/codecs/simpleCodec/simpleDecoder.ts b/src/vs/editor/common/codecs/simpleCodec/simpleDecoder.ts index 88ad12985017..c32542f28da9 100644 --- a/src/vs/editor/common/codecs/simpleCodec/simpleDecoder.ts +++ b/src/vs/editor/common/codecs/simpleCodec/simpleDecoder.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Hash } from './tokens/hash.js'; +import { Dash } from './tokens/dash.js'; import { Colon } from './tokens/colon.js'; import { FormFeed } from './tokens/formFeed.js'; import { Tab } from '../simpleCodec/tokens/tab.js'; @@ -12,39 +13,44 @@ import { VerticalTab } from './tokens/verticalTab.js'; import { Space } from '../simpleCodec/tokens/space.js'; import { NewLine } from '../linesCodec/tokens/newLine.js'; import { VSBuffer } from '../../../../base/common/buffer.js'; -import { LeftBracket, RightBracket } from './tokens/brackets.js'; +import { ExclamationMark } from './tokens/exclamationMark.js'; import { ReadableStream } from '../../../../base/common/stream.js'; import { CarriageReturn } from '../linesCodec/tokens/carriageReturn.js'; import { LinesDecoder, TLineToken } from '../linesCodec/linesDecoder.js'; +import { LeftBracket, RightBracket, TBracket } from './tokens/brackets.js'; import { BaseDecoder } from '../../../../base/common/codecs/baseDecoder.js'; -import { LeftParenthesis, RightParenthesis } from './tokens/parentheses.js'; +import { LeftParenthesis, RightParenthesis, TParenthesis } from './tokens/parentheses.js'; +import { LeftAngleBracket, RightAngleBracket, TAngleBracket } from './tokens/angleBrackets.js'; /** * A token type that this decoder can handle. */ -export type TSimpleToken = Word | Space | Tab | VerticalTab | NewLine | FormFeed | CarriageReturn | LeftBracket - | RightBracket | LeftParenthesis | RightParenthesis | Colon | Hash; +export type TSimpleToken = Word | Space | Tab | VerticalTab | NewLine | FormFeed + | CarriageReturn | TBracket | TAngleBracket | TParenthesis + | Colon | Hash | Dash | ExclamationMark; /** * List of well-known distinct tokens that this decoder emits (excluding * the word stop characters defined below). Everything else is considered * an arbitrary "text" sequence and is emitted as a single `Word` token. */ -const WELL_KNOWN_TOKENS = [ - Space, Tab, VerticalTab, FormFeed, LeftBracket, RightBracket, - LeftParenthesis, RightParenthesis, Colon, Hash, -]; +const WELL_KNOWN_TOKENS = Object.freeze([ + Space, Tab, VerticalTab, FormFeed, + LeftBracket, RightBracket, LeftAngleBracket, RightAngleBracket, + LeftParenthesis, RightParenthesis, Colon, Hash, Dash, ExclamationMark, +]); /** * Characters that stop a "word" sequence. * Note! the `\r` and `\n` are excluded from the list because this decoder based on `LinesDecoder` which * already handles the `carriagereturn`/`newline` cases and emits lines that don't contain them. */ -const WORD_STOP_CHARACTERS = [ +const WORD_STOP_CHARACTERS: readonly string[] = Object.freeze([ Space.symbol, Tab.symbol, VerticalTab.symbol, FormFeed.symbol, - LeftBracket.symbol, RightBracket.symbol, LeftParenthesis.symbol, - RightParenthesis.symbol, Colon.symbol, Hash.symbol, -]; + LeftBracket.symbol, RightBracket.symbol, LeftAngleBracket.symbol, RightAngleBracket.symbol, + LeftParenthesis.symbol, RightParenthesis.symbol, + Colon.symbol, Hash.symbol, Dash.symbol, ExclamationMark.symbol, +]); /** * A decoder that can decode a stream of `Line`s into a stream diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/angleBrackets.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/angleBrackets.ts new file mode 100644 index 000000000000..70d264bdd993 --- /dev/null +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/angleBrackets.ts @@ -0,0 +1,102 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { BaseToken } from '../../baseToken.js'; +import { Range } from '../../../core/range.js'; +import { Position } from '../../../core/position.js'; +import { Line } from '../../linesCodec/tokens/line.js'; + +/** + * A token that represent a `<` with a `range`. The `range` + * value reflects the position of the token in the original data. + */ +export class LeftAngleBracket extends BaseToken { + /** + * The underlying symbol of the token. + */ + public static readonly symbol: string = '<'; + + /** + * Return text representation of the token. + */ + public get text(): string { + return LeftAngleBracket.symbol; + } + + /** + * Create new `LeftBracket` token with range inside + * the given `Line` at the given `column number`. + */ + public static newOnLine( + line: Line, + atColumnNumber: number, + ): LeftAngleBracket { + const { range } = line; + + const startPosition = new Position(range.startLineNumber, atColumnNumber); + const endPosition = new Position(range.startLineNumber, atColumnNumber + this.symbol.length); + + return new LeftAngleBracket(Range.fromPositions( + startPosition, + endPosition, + )); + } + + /** + * Returns a string representation of the token. + */ + public override toString(): string { + return `left-angle-bracket${this.range}`; + } +} + +/** + * A token that represent a `>` with a `range`. The `range` + * value reflects the position of the token in the original data. + */ +export class RightAngleBracket extends BaseToken { + /** + * The underlying symbol of the token. + */ + public static readonly symbol: string = '>'; + + /** + * Return text representation of the token. + */ + public get text(): string { + return RightAngleBracket.symbol; + } + + /** + * Create new `RightAngleBracket` token with range inside + * the given `Line` at the given `column number`. + */ + public static newOnLine( + line: Line, + atColumnNumber: number, + ): RightAngleBracket { + const { range } = line; + + const startPosition = new Position(range.startLineNumber, atColumnNumber); + const endPosition = new Position(range.startLineNumber, atColumnNumber + this.symbol.length); + + return new RightAngleBracket(Range.fromPositions( + startPosition, + endPosition, + )); + } + + /** + * Returns a string representation of the token. + */ + public override toString(): string { + return `right-angle-bracket${this.range}`; + } +} + +/** + * General angle bracket token type. + */ +export type TAngleBracket = LeftAngleBracket | RightAngleBracket; diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/brackets.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/brackets.ts index 5c6c1e46a5d3..16165cf64a71 100644 --- a/src/vs/editor/common/codecs/simpleCodec/tokens/brackets.ts +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/brackets.ts @@ -36,7 +36,6 @@ export class LeftBracket extends BaseToken { const { range } = line; const startPosition = new Position(range.startLineNumber, atColumnNumber); - // the tab token length is 1, hence `+ 1` const endPosition = new Position(range.startLineNumber, atColumnNumber + this.symbol.length); return new LeftBracket(Range.fromPositions( @@ -81,7 +80,6 @@ export class RightBracket extends BaseToken { const { range } = line; const startPosition = new Position(range.startLineNumber, atColumnNumber); - // the tab token length is 1, hence `+ 1` const endPosition = new Position(range.startLineNumber, atColumnNumber + this.symbol.length); return new RightBracket(Range.fromPositions( @@ -97,3 +95,8 @@ export class RightBracket extends BaseToken { return `right-bracket${this.range}`; } } + +/** + * General bracket token type. + */ +export type TBracket = LeftBracket | RightBracket; diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/colon.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/colon.ts index 2c4b89d9ce52..76e9f0cd2b4e 100644 --- a/src/vs/editor/common/codecs/simpleCodec/tokens/colon.ts +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/colon.ts @@ -14,7 +14,7 @@ import { Line } from '../../linesCodec/tokens/line.js'; */ export class Colon extends BaseToken { /** - * The underlying symbol of the `LeftBracket` token. + * The underlying symbol of the token. */ public static readonly symbol: string = ':'; @@ -26,7 +26,7 @@ export class Colon extends BaseToken { } /** - * Create new `LeftBracket` token with range inside + * Create new token with range inside * the given `Line` at the given `column number`. */ public static newOnLine( @@ -36,7 +36,6 @@ export class Colon extends BaseToken { const { range } = line; const startPosition = new Position(range.startLineNumber, atColumnNumber); - // the tab token length is 1, hence `+ 1` const endPosition = new Position(range.startLineNumber, atColumnNumber + this.symbol.length); return new Colon(Range.fromPositions( diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/dash.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/dash.ts new file mode 100644 index 000000000000..ebc0179eeef9 --- /dev/null +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/dash.ts @@ -0,0 +1,53 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { BaseToken } from '../../baseToken.js'; +import { Range } from '../../../core/range.js'; +import { Position } from '../../../core/position.js'; +import { Line } from '../../linesCodec/tokens/line.js'; + +/** + * A token that represent a `-` with a `range`. The `range` + * value reflects the position of the token in the original data. + */ +export class Dash extends BaseToken { + /** + * The underlying symbol of the token. + */ + public static readonly symbol: string = '-'; + + /** + * Return text representation of the token. + */ + public get text(): string { + return Dash.symbol; + } + + /** + * Create new token with range inside + * the given `Line` at the given `column number`. + */ + public static newOnLine( + line: Line, + atColumnNumber: number, + ): Dash { + const { range } = line; + + const startPosition = new Position(range.startLineNumber, atColumnNumber); + const endPosition = new Position(range.startLineNumber, atColumnNumber + this.symbol.length); + + return new Dash(Range.fromPositions( + startPosition, + endPosition, + )); + } + + /** + * Returns a string representation of the token. + */ + public override toString(): string { + return `dash${this.range}`; + } +} diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/exclamationMark.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/exclamationMark.ts new file mode 100644 index 000000000000..025edf702912 --- /dev/null +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/exclamationMark.ts @@ -0,0 +1,53 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { BaseToken } from '../../baseToken.js'; +import { Range } from '../../../core/range.js'; +import { Position } from '../../../core/position.js'; +import { Line } from '../../linesCodec/tokens/line.js'; + +/** + * A token that represent a `!` with a `range`. The `range` + * value reflects the position of the token in the original data. + */ +export class ExclamationMark extends BaseToken { + /** + * The underlying symbol of the token. + */ + public static readonly symbol: string = '!'; + + /** + * Return text representation of the token. + */ + public get text(): string { + return ExclamationMark.symbol; + } + + /** + * Create new token with range inside + * the given `Line` at the given `column number`. + */ + public static newOnLine( + line: Line, + atColumnNumber: number, + ): ExclamationMark { + const { range } = line; + + const startPosition = new Position(range.startLineNumber, atColumnNumber); + const endPosition = new Position(range.startLineNumber, atColumnNumber + this.symbol.length); + + return new ExclamationMark(Range.fromPositions( + startPosition, + endPosition, + )); + } + + /** + * Returns a string representation of the token. + */ + public override toString(): string { + return `exclamation-mark${this.range}`; + } +} diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/hash.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/hash.ts index 372e0b2ee3d9..ddca12a22792 100644 --- a/src/vs/editor/common/codecs/simpleCodec/tokens/hash.ts +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/hash.ts @@ -14,7 +14,7 @@ import { Line } from '../../linesCodec/tokens/line.js'; */ export class Hash extends BaseToken { /** - * The underlying symbol of the `LeftBracket` token. + * The underlying symbol of the token. */ public static readonly symbol: string = '#'; @@ -26,7 +26,7 @@ export class Hash extends BaseToken { } /** - * Create new `LeftBracket` token with range inside + * Create new token with range inside * the given `Line` at the given `column number`. */ public static newOnLine( @@ -36,7 +36,6 @@ export class Hash extends BaseToken { const { range } = line; const startPosition = new Position(range.startLineNumber, atColumnNumber); - // the tab token length is 1, hence `+ 1` const endPosition = new Position(range.startLineNumber, atColumnNumber + this.symbol.length); return new Hash(Range.fromPositions( diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/parentheses.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/parentheses.ts index b67f4e10f5c7..d3509824f539 100644 --- a/src/vs/editor/common/codecs/simpleCodec/tokens/parentheses.ts +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/parentheses.ts @@ -14,7 +14,7 @@ import { Line } from '../../linesCodec/tokens/line.js'; */ export class LeftParenthesis extends BaseToken { /** - * The underlying symbol of the `LeftParenthesis` token. + * The underlying symbol of the token. */ public static readonly symbol: string = '('; @@ -36,7 +36,6 @@ export class LeftParenthesis extends BaseToken { const { range } = line; const startPosition = new Position(range.startLineNumber, atColumnNumber); - // the tab token length is 1, hence `+ 1` const endPosition = new Position(range.startLineNumber, atColumnNumber + this.symbol.length); return new LeftParenthesis(Range.fromPositions( @@ -59,7 +58,7 @@ export class LeftParenthesis extends BaseToken { */ export class RightParenthesis extends BaseToken { /** - * The underlying symbol of the `RightParenthesis` token. + * The underlying symbol of the token. */ public static readonly symbol: string = ')'; @@ -81,7 +80,6 @@ export class RightParenthesis extends BaseToken { const { range } = line; const startPosition = new Position(range.startLineNumber, atColumnNumber); - // the tab token length is 1, hence `+ 1` const endPosition = new Position(range.startLineNumber, atColumnNumber + this.symbol.length); return new RightParenthesis(Range.fromPositions( @@ -97,3 +95,8 @@ export class RightParenthesis extends BaseToken { return `right-parenthesis${this.range}`; } } + +/** + * General parenthesis token type. + */ +export type TParenthesis = LeftParenthesis | RightParenthesis; diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/tab.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/tab.ts index 7f511c2626bf..c0d775ff8cd8 100644 --- a/src/vs/editor/common/codecs/simpleCodec/tokens/tab.ts +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/tab.ts @@ -14,7 +14,7 @@ import { Position } from '../../../../../editor/common/core/position.js'; */ export class Tab extends BaseToken { /** - * The underlying symbol of the `Tab` token. + * The underlying symbol of the token. */ public static readonly symbol: string = '\t'; @@ -26,7 +26,7 @@ export class Tab extends BaseToken { } /** - * Create new `Tab` token with range inside + * Create new token with range inside * the given `Line` at the given `column number`. */ public static newOnLine( @@ -36,7 +36,6 @@ export class Tab extends BaseToken { const { range } = line; const startPosition = new Position(range.startLineNumber, atColumnNumber); - // the tab token length is 1, hence `+ 1` const endPosition = new Position(range.startLineNumber, atColumnNumber + this.symbol.length); return new Tab(Range.fromPositions( diff --git a/src/vs/editor/common/codecs/simpleCodec/tokens/word.ts b/src/vs/editor/common/codecs/simpleCodec/tokens/word.ts index fc3cefa79be6..2ca5598ac4ba 100644 --- a/src/vs/editor/common/codecs/simpleCodec/tokens/word.ts +++ b/src/vs/editor/common/codecs/simpleCodec/tokens/word.ts @@ -67,6 +67,6 @@ export class Word extends BaseToken { * Returns a string representation of the token. */ public override toString(): string { - return `word("${this.text.slice(0, 8)}")${this.range}`; + return `word("${this.text}")${this.range}`; } } diff --git a/src/vs/editor/common/config/editorConfigurationSchema.ts b/src/vs/editor/common/config/editorConfigurationSchema.ts index 38aedd4868ed..c3613ee97256 100644 --- a/src/vs/editor/common/config/editorConfigurationSchema.ts +++ b/src/vs/editor/common/config/editorConfigurationSchema.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import type { IJSONSchemaSnippet } from '../../../base/common/jsonSchema.js'; import { diffEditorDefaultOptions } from './diffEditor.js'; import { editorOptionsRegistry } from './editorOptions.js'; import { EDITOR_MODEL_DEFAULTS } from '../core/textModelDefaults.js'; @@ -318,3 +319,15 @@ export function isDiffEditorConfigurationKey(key: string): boolean { const configurationRegistry = Registry.as(Extensions.Configuration); configurationRegistry.registerConfiguration(editorConfiguration); + +export async function registerEditorFontConfigurations(getFontSnippets: () => Promise) { + const editorKeysWithFont = ['editor.fontFamily']; + const fontSnippets = await getFontSnippets(); + for (const key of editorKeysWithFont) { + if ( + editorConfiguration.properties && editorConfiguration.properties[key] + ) { + editorConfiguration.properties[key].defaultSnippets = fontSnippets; + } + } +} diff --git a/src/vs/editor/common/config/editorOptions.ts b/src/vs/editor/common/config/editorOptions.ts index 7db0030e063b..4a137ce2d9d3 100644 --- a/src/vs/editor/common/config/editorOptions.ts +++ b/src/vs/editor/common/config/editorOptions.ts @@ -1082,6 +1082,9 @@ function applyUpdate(value: T | undefined, update: T): ApplyUpdateResult { let didChange = false; for (const key in update) { if ((update as T & object).hasOwnProperty(key)) { + if (key === '__proto__' || key === 'constructor' || key === 'prototype') { + continue; + } const result = applyUpdate(value[key], update[key]); if (result.didChange) { value[key] = result.newValue; @@ -1944,7 +1947,7 @@ class EffectiveExperimentalEditContextEnabled extends ComputedEditorOption m.original.startLineNumber, numberComparator)); const monotonousChanges = new MonotonousArray(changes); @@ -246,12 +252,10 @@ function computeUnchangedMoves( if (extendToTop > 0 || extendToBottom > 0) { moves[i] = new LineRangeMapping( new LineRange(move.original.startLineNumber - extendToTop, move.original.endLineNumberExclusive + extendToBottom), - new LineRange(move.modified.startLineNumber - extendToTop, move.modified.endLineNumberExclusive + extendToBottom), + new LineRange(move.modified.startLineNumber - extendToTop, move.modified.endLineNumberExclusive + extendToBottom) ); } } - - return moves; } function areLinesSimilar(line1: string, line2: string, timeout: ITimeout): boolean { diff --git a/src/vs/editor/common/languages.ts b/src/vs/editor/common/languages.ts index 286793eb242b..6ce3c2421736 100644 --- a/src/vs/editor/common/languages.ts +++ b/src/vs/editor/common/languages.ts @@ -110,7 +110,7 @@ export interface ITreeSitterTokenizationSupport { captureAtPosition(lineNumber: number, column: number, textModel: model.ITextModel): QueryCapture[]; captureAtPositionTree(lineNumber: number, column: number, tree: Parser.Tree): QueryCapture[]; onDidChangeTokens: Event<{ textModel: model.ITextModel; changes: IModelTokensChangedEvent }>; - onDidCompleteFirstTokenization: Event<{ textModel: model.ITextModel }>; + onDidChangeBackgroundTokenization: Event<{ textModel: model.ITextModel }>; tokenizeEncodedInstrumented(lineNumber: number, textModel: model.ITextModel): { result: Uint32Array; captureTime: number; metadataTime: number } | undefined; } @@ -431,6 +431,43 @@ export namespace CompletionItemKinds { return codicon; } + /** + * @internal + */ + export function toLabel(kind: CompletionItemKind): string { + switch (kind) { + case CompletionItemKind.Method: return localize('suggestWidget.kind.method', 'Method'); + case CompletionItemKind.Function: return localize('suggestWidget.kind.function', 'Function'); + case CompletionItemKind.Constructor: return localize('suggestWidget.kind.constructor', 'Constructor'); + case CompletionItemKind.Field: return localize('suggestWidget.kind.field', 'Field'); + case CompletionItemKind.Variable: return localize('suggestWidget.kind.variable', 'Variable'); + case CompletionItemKind.Class: return localize('suggestWidget.kind.class', 'Class'); + case CompletionItemKind.Struct: return localize('suggestWidget.kind.struct', 'Struct'); + case CompletionItemKind.Interface: return localize('suggestWidget.kind.interface', 'Interface'); + case CompletionItemKind.Module: return localize('suggestWidget.kind.module', 'Module'); + case CompletionItemKind.Property: return localize('suggestWidget.kind.property', 'Property'); + case CompletionItemKind.Event: return localize('suggestWidget.kind.event', 'Event'); + case CompletionItemKind.Operator: return localize('suggestWidget.kind.operator', 'Operator'); + case CompletionItemKind.Unit: return localize('suggestWidget.kind.unit', 'Unit'); + case CompletionItemKind.Value: return localize('suggestWidget.kind.value', 'Value'); + case CompletionItemKind.Constant: return localize('suggestWidget.kind.constant', 'Constant'); + case CompletionItemKind.Enum: return localize('suggestWidget.kind.enum', 'Enum'); + case CompletionItemKind.EnumMember: return localize('suggestWidget.kind.enumMember', 'Enum Member'); + case CompletionItemKind.Keyword: return localize('suggestWidget.kind.keyword', 'Keyword'); + case CompletionItemKind.Text: return localize('suggestWidget.kind.text', 'Text'); + case CompletionItemKind.Color: return localize('suggestWidget.kind.color', 'Color'); + case CompletionItemKind.File: return localize('suggestWidget.kind.file', 'File'); + case CompletionItemKind.Reference: return localize('suggestWidget.kind.reference', 'Reference'); + case CompletionItemKind.Customcolor: return localize('suggestWidget.kind.customcolor', 'Custom Color'); + case CompletionItemKind.Folder: return localize('suggestWidget.kind.folder', 'Folder'); + case CompletionItemKind.TypeParameter: return localize('suggestWidget.kind.typeParameter', 'Type Parameter'); + case CompletionItemKind.User: return localize('suggestWidget.kind.user', 'User'); + case CompletionItemKind.Issue: return localize('suggestWidget.kind.issue', 'Issue'); + case CompletionItemKind.Snippet: return localize('suggestWidget.kind.snippet', 'Snippet'); + default: return ''; + } + } + const data = new Map(); data.set('method', CompletionItemKind.Method); data.set('function', CompletionItemKind.Function); @@ -789,6 +826,7 @@ export interface InlineCompletion { /** * Is called the first time an inline completion is shown. + * @deprecated. Use `onDidShow` of the provider instead. */ readonly shownCommand?: Command; @@ -799,6 +837,7 @@ export interface InlineCompletion { readonly completeBracketPairs?: boolean; readonly isInlineEdit?: boolean; + readonly showInlineEditMenu?: boolean; readonly showRange?: IRange; diff --git a/src/vs/editor/common/languages/defaultDocumentColorsComputer.ts b/src/vs/editor/common/languages/defaultDocumentColorsComputer.ts index 74bb07579052..a2d59d587261 100644 --- a/src/vs/editor/common/languages/defaultDocumentColorsComputer.ts +++ b/src/vs/editor/common/languages/defaultDocumentColorsComputer.ts @@ -63,7 +63,7 @@ function _findHexColorInformation(range: IRange | undefined, hexValue: string) { }; } -function _findRGBColorInformation(range: IRange | undefined, matches: RegExpMatchArray[], isAlpha: boolean) { +function _findRGBColorInformation(range: IRange | undefined, matches: RegExpMatchArray[]) { if (!range || matches.length !== 1) { return; } @@ -72,18 +72,18 @@ function _findRGBColorInformation(range: IRange | undefined, matches: RegExpMatc const parsedRegex = _parseCaptureGroups(captureGroups); return { range: range, - color: _toIColor(parsedRegex[0], parsedRegex[1], parsedRegex[2], isAlpha ? parsedRegex[3] : 1) + color: _toIColor(parsedRegex[0], parsedRegex[1], parsedRegex[2], parsedRegex[3] !== undefined ? parsedRegex[3] : 1) }; } -function _findHSLColorInformation(range: IRange | undefined, matches: RegExpMatchArray[], isAlpha: boolean) { +function _findHSLColorInformation(range: IRange | undefined, matches: RegExpMatchArray[]) { if (!range || matches.length !== 1) { return; } const match = matches[0]!; const captureGroups = match.values(); const parsedRegex = _parseCaptureGroups(captureGroups); - const colorEquivalent = new Color(new HSLA(parsedRegex[0], parsedRegex[1] / 100, parsedRegex[2] / 100, isAlpha ? parsedRegex[3] : 1)); + const colorEquivalent = new Color(new HSLA(parsedRegex[0], parsedRegex[1] / 100, parsedRegex[2] / 100, parsedRegex[3] !== undefined ? parsedRegex[3] : 1)); return { range: range, color: _toIColor(colorEquivalent.rgba.r, colorEquivalent.rgba.g, colorEquivalent.rgba.b, colorEquivalent.rgba.a) @@ -101,7 +101,7 @@ function _findMatches(model: IDocumentColorComputerTarget | string, regex: RegEx function computeColors(model: IDocumentColorComputerTarget): IColorInformation[] { const result: IColorInformation[] = []; // Early validation for RGB and HSL - const initialValidationRegex = /\b(rgb|rgba|hsl|hsla)(\([0-9\s,.\%]*\))|\s+(#)([A-Fa-f0-9]{6})\b|\s+(#)([A-Fa-f0-9]{8})\b|^(#)([A-Fa-f0-9]{6})\b|^(#)([A-Fa-f0-9]{8})\b/gm; + const initialValidationRegex = /\b(rgb|hsl)(\([0-9\s.\/\%]*\))|\s+(#)([A-Fa-f0-9]{6})\b|\s+(#)([A-Fa-f0-9]{8})\b|^(#)([A-Fa-f0-9]{6})\b|^(#)([A-Fa-f0-9]{8})\b/gm; const initialValidationMatches = _findMatches(model, initialValidationRegex); // Potential colors have been found, validate the parameters @@ -115,17 +115,11 @@ function computeColors(model: IDocumentColorComputerTarget): IColorInformation[] } let colorInformation; if (colorScheme === 'rgb') { - const regexParameters = /^\(\s*(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])\s*,\s*(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])\s*,\s*(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])\s*\)$/gm; - colorInformation = _findRGBColorInformation(_findRange(model, initialMatch), _findMatches(colorParameters, regexParameters), false); - } else if (colorScheme === 'rgba') { - const regexParameters = /^\(\s*(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])\s*,\s*(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])\s*,\s*(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])\s*,\s*(0[.][0-9]+|[.][0-9]+|[01][.]|[01])\s*\)$/gm; - colorInformation = _findRGBColorInformation(_findRange(model, initialMatch), _findMatches(colorParameters, regexParameters), true); + const regexParameters = /^\(\s*(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])\s+(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])\s+(25[0-5]|2[0-4][0-9]|1[0-9]{2}|[1-9][0-9]|[0-9])(\s*\/\s*0[.][0-9]+|[.][0-9]+|[01][.]|[01])?\s*\)$/gm; + colorInformation = _findRGBColorInformation(_findRange(model, initialMatch), _findMatches(colorParameters, regexParameters)); } else if (colorScheme === 'hsl') { - const regexParameters = /^\(\s*(36[0]|3[0-5][0-9]|[12][0-9][0-9]|[1-9]?[0-9])\s*,\s*(100|\d{1,2}[.]\d*|\d{1,2})%\s*,\s*(100|\d{1,2}[.]\d*|\d{1,2})%\s*\)$/gm; - colorInformation = _findHSLColorInformation(_findRange(model, initialMatch), _findMatches(colorParameters, regexParameters), false); - } else if (colorScheme === 'hsla') { - const regexParameters = /^\(\s*(36[0]|3[0-5][0-9]|[12][0-9][0-9]|[1-9]?[0-9])\s*,\s*(100|\d{1,2}[.]\d*|\d{1,2})%\s*,\s*(100|\d{1,2}[.]\d*|\d{1,2})%\s*,\s*(0[.][0-9]+|[.][0-9]+|[01][.]|[01])\s*\)$/gm; - colorInformation = _findHSLColorInformation(_findRange(model, initialMatch), _findMatches(colorParameters, regexParameters), true); + const regexParameters = /^\(\s*(36[0]|3[0-5][0-9]|[12][0-9][0-9]|[1-9]?[0-9])\s+(100|\d{1,2}[.]\d*|\d{1,2})%\s+(100|\d{1,2}[.]\d*|\d{1,2})%\s+(\s*\/\s*0[.][0-9]+|[.][0-9]+|[01][.]|[01])?\s*\)$/gm; + colorInformation = _findHSLColorInformation(_findRange(model, initialMatch), _findMatches(colorParameters, regexParameters)); } else if (colorScheme === '#') { colorInformation = _findHexColorInformation(_findRange(model, initialMatch), colorScheme + colorParameters); } diff --git a/src/vs/editor/common/languages/highlights/typescript.scm b/src/vs/editor/common/languages/highlights/typescript.scm index 5e36868e5bee..813ce090dcc9 100644 --- a/src/vs/editor/common/languages/highlights/typescript.scm +++ b/src/vs/editor/common/languages/highlights/typescript.scm @@ -3,118 +3,202 @@ ; Variables -(identifier) @variable +(identifier) @variable.ts + +(_ + object: (identifier) @variable.other.object.ts) ; Literals -(this) @variable.language.this -(super) @variable.language.super +(this) @variable.language.this.ts +(super) @variable.language.super.ts -(comment) @comment +(comment) @comment.ts ; TODO: This doesn't seem to be working -(escape_sequence) @constant.character.escape +(escape_sequence) @constant.character.escape.ts -[ - (string) +((string) @string.quoted.single.ts + (#match? @string.quoted.single.ts "^'.*'$")) + +((string) @string.quoted.double.ts + (#match? @string.quoted.double.ts "^\".*\"$")) + +([ (template_string) (template_literal_type) -] @string +] @string.template.ts) + +(string . + ([ + "\"" + "'" + ]) @punctuation.definition.string.begin.ts) + +(string + ([ + "\"" + "'" + ]) @punctuation.definition.string.end.ts .) + +(template_string . ("`") @punctuation.definition.string.template.begin.ts) + +(template_string ("`") @punctuation.definition.string.template.end.ts .) ; NOTE: the typescript grammar doesn't break regex into nice parts so as to capture parts of it separately -(regex) @string.regexp -(number) @constant.numeric +(regex) @string.regexp.ts +(number) @constant.numeric.ts ; Properties (member_expression object: (this) - property: (property_identifier) @variable) + property: (property_identifier) @variable.ts) (member_expression - property: (property_identifier) @variable.other.constant - (#match? @variable.other.constant "^[A-Z][A-Z_]+$")) + property: (property_identifier) @variable.other.constant.ts + (#match? @variable.other.constant.ts "^[A-Z][A-Z_]+$")) [ (property_identifier) (shorthand_property_identifier) - (shorthand_property_identifier_pattern)] @variable + (shorthand_property_identifier_pattern)] @variable.ts ; Function and method definitions (function_expression - name: (identifier) @entity.name.function) + name: (identifier) @entity.name.function.ts) +(function_signature + name: (identifier) @entity.name.function.ts) (function_declaration - name: (identifier) @entity.name.function) + name: (identifier) @entity.name.function.ts) (method_definition - name: (property_identifier) @meta.definition.method @entity.name.function - (#not-eq? @entity.name.function "constructor")) + name: (property_identifier) @meta.definition.method.ts @entity.name.function.ts + (#not-eq? @entity.name.function.ts "constructor")) (method_definition - name: (property_identifier) @meta.definition.method @storage.type - (#eq? @storage.type "constructor")) + name: (property_identifier) @meta.definition.method.ts @storage.type.ts + (#eq? @storage.type.ts "constructor")) (method_signature - name: (property_identifier) @meta.definition.method @entity.name.function) + name: (property_identifier) @meta.definition.method.ts @entity.name.function.ts) +(generator_function_declaration + "*" @keyword.generator.asterisk.ts) +(generator_function_declaration + name: (identifier) @meta.definition.function.ts @entity.name.function.ts) (pair - key: (property_identifier) @entity.name.function + key: (property_identifier) @entity.name.function.ts value: [(function_expression) (arrow_function)]) (assignment_expression left: (member_expression - property: (property_identifier) @entity.name.function) + property: (property_identifier) @entity.name.function.ts) right: [(function_expression) (arrow_function)]) (variable_declarator - name: (identifier) @entity.name.function + name: (identifier) @entity.name.function.ts value: [(function_expression) (arrow_function)]) (assignment_expression - left: (identifier) @entity.name.function + left: (identifier) @entity.name.function.ts right: [(function_expression) (arrow_function)]) (required_parameter - (identifier) @variable.parameter) + (identifier) @variable.parameter.ts) + +(required_parameter + (_ + ([ + (identifier) + (shorthand_property_identifier_pattern) + ]) @variable.parameter.ts)) + +(optional_parameter + (identifier) @variable.parameter.ts) (optional_parameter - (identifier) @variable.parameter) + (_ + ([ + (identifier) + (shorthand_property_identifier_pattern) + ]) @variable.parameter.ts)) + +(catch_clause + parameter: (identifier) @variable.parameter.ts) + +(index_signature + name: (identifier) @variable.parameter.ts) + +(arrow_function + parameter: (identifier) @variable.parameter.ts) ; Function and method calls (call_expression - function: (identifier) @entity.name.function) + function: (identifier) @entity.name.function.ts) (call_expression function: (member_expression - object: (identifier) @support.class.promise) - (#eq? @support.class.promise "Promise")) + object: (identifier) @support.class.promise.ts) + (#eq? @support.class.promise.ts "Promise")) (call_expression function: (member_expression - property: (property_identifier) @entity.name.function)) + property: (property_identifier) @entity.name.function.ts)) -(new_expression) @new.expr +(new_expression) @new.expr.ts (new_expression - constructor: (identifier) @entity.name.function) + constructor: (identifier) @entity.name.function.ts) ; Special identifiers -(predefined_type) @support.type -(predefined_type (["string" "boolean" "number" "any" "unknown"])) @support.type.primitive -(type_identifier) @entity.name.type +(predefined_type) @support.type.ts +(predefined_type (["string" "boolean" "number" "any" "unknown" "never" "void"])) @support.type.primitive.ts -([ - (identifier) - (shorthand_property_identifier) - (shorthand_property_identifier_pattern)] @variable.other.constant - (#match? @variable.other.constant "^[A-Z][A-Z_]+$")) +(_ + (type_identifier) @entity.name.type.ts) + +(type_annotation + ([ + (type_identifier) + (nested_type_identifier) + ]) @meta.type.annotation.ts @entity.name.type.ts) + +(class_declaration + (type_identifier) @entity.name.type.class.ts) + +(type_alias_declaration + (type_identifier) @entity.name.type.alias.ts) +(type_alias_declaration + value: (_ + (type_identifier) @entity.name.type.ts)) + +(interface_declaration + (type_identifier) @entity.name.type.interface.ts) + +(internal_module + name: (identifier) @entity.name.type.ts) + +(enum_declaration + name: (identifier) @entity.name.type.enum.ts) + +( + [ + (_ name: (identifier)) + (shorthand_property_identifier) + (shorthand_property_identifier_pattern) + ] @variable.other.constant.ts + (#match? @variable.other.constant.ts "^[A-Z][A-Z_]+$")) (extends_clause - value: (identifier) @entity.other.inherited-class) + value: (identifier) @entity.other.inherited-class.ts) + +(extends_type_clause + type: (type_identifier) @entity.other.inherited-class.ts) (implements_clause - (type_identifier) @entity.other.inherited-class) + (type_identifier) @entity.other.inherited-class.ts) ; Tokens @@ -125,7 +209,7 @@ "," ":" "?" -] @punctuation.delimiter +] @punctuation.delimiter.ts [ "!" @@ -135,7 +219,7 @@ "&&" "||" "??" -] @keyword.operator.logical +] @keyword.operator.logical.ts (binary_expression ([ "-" @@ -144,18 +228,22 @@ "/" "%" "^" -]) @keyword.operator.arithmetic) +]) @keyword.operator.arithmetic.ts) (binary_expression ([ "<" "<=" ">" ">=" -]) @keyword.operator.relational) +]) @keyword.operator.relational.ts) + +(unary_expression ([ + "-" +]) @keyword.operator.arithmetic.ts) [ "=" -] @keyword.operator.assignment +] @keyword.operator.assignment.ts (augmented_assignment_expression ([ "-=" @@ -169,15 +257,15 @@ "&&=" "||=" "??=" -]) @keyword.operator.assignment.compound) +]) @keyword.operator.assignment.compound.ts) [ "++" -] @keyword.operator.increment +] @keyword.operator.increment.ts [ "--" -] @keyword.operator.decrement +] @keyword.operator.decrement.ts [ "**" @@ -193,13 +281,28 @@ "~" "&" "|" -] @keyword.operator +] @keyword.operator.ts (union_type - ("|") @keyword.operator.type) + ("|") @keyword.operator.type.ts) (intersection_type - ("&") @keyword.operator.type) + ("&") @keyword.operator.type.ts) + +(type_annotation + (":") @keyword.operator.type.annotation.ts) + +(index_signature + (":") @keyword.operator.type.annotation.ts) + +(type_predicate_annotation + (":") @keyword.operator.type.annotation.ts) + +(conditional_type + ([ + "?" + ":" + ]) @keyword.operator.ternary.ts) [ "{" @@ -208,36 +311,40 @@ ")" "[" "]" -] @punctuation +] @punctuation.ts (template_substitution - "${" @punctuation.definition.template-expression.begin - "}" @punctuation.definition.template-expression.end) + "${" @punctuation.definition.template-expression.begin.ts + "}" @punctuation.definition.template-expression.end.ts) (template_type - "${" @punctuation.definition.template-expression.begin - "}" @punctuation.definition.template-expression.end) + "${" @punctuation.definition.template-expression.begin.ts + "}" @punctuation.definition.template-expression.end.ts) (type_arguments - "<" @punctuation.definition.typeparameters - ">" @punctuation.definition.typeparameters) + "<" @punctuation.definition.typeparameters.begin.ts + ">" @punctuation.definition.typeparameters.end.ts) + +(type_parameters + "<" @punctuation.definition.typeparameters.begin.ts + ">" @punctuation.definition.typeparameters.end.ts) ; Keywords -("typeof") @keyword.operator.expression.typeof +("typeof") @keyword.operator.expression.typeof.ts -(binary_expression "instanceof" @keyword.operator.expression.instanceof) +(binary_expression "instanceof" @keyword.operator.expression.instanceof.ts) -("of") @keyword.operator.expression.of +("of") @keyword.operator.expression.of.ts -("is") @keyword.operator.expression.is +("is") @keyword.operator.expression.is.ts [ "delete" "in" "infer" "keyof" -] @keyword.operator.expression +] @keyword.operator.expression.ts [ "as" @@ -261,10 +368,9 @@ "switch" "throw" "try" - "type" "while" "yield" -] @keyword.control +] @keyword.control.ts [ "abstract" @@ -278,7 +384,7 @@ "public" "readonly" "static" -] @storage.modifier +] @storage.modifier.ts [ "=>" @@ -292,70 +398,92 @@ "namespace" "set" "var" -] @storage.type +] @storage.type.ts + +("type") @storage.type.type.ts + +[ + "module" +] @storage.type.namespace.ts [ "debugger" "target" "with" -] @keyword +] @keyword.ts -(regex_flags) @keyword +(regex_flags) @keyword.ts -[ - "void" -] @support.type.primitive +(unary_expression + "void" @keyword.operator.expression.void.ts) [ "new" -] @keyword.operator.new +] @keyword.operator.new.ts (public_field_definition - ("?") @keyword.operator.optional) + ("?") @keyword.operator.optional.ts) + +(property_signature + ("?") @keyword.operator.optional.ts) + +(method_signature + ("?") @keyword.operator.optional.ts) (optional_parameter ([ "?" ":" - ]) @keyword.operator.optional) + ]) @keyword.operator.optional.ts) (ternary_expression ([ "?" ":" - ]) @keyword.operator.ternary) + ]) @keyword.operator.ternary.ts) (optional_chain - ("?.") @punctuation.accessor.optional) + ("?.") @punctuation.accessor.optional.ts) -(rest_pattern) @keyword.operator.rest +(rest_pattern + ("...") @keyword.operator.rest.ts) +(rest_type + ("...") @keyword.operator.rest.ts) (spread_element - ("...") @keyword.operator.spread) + ("...") @keyword.operator.spread.ts) ; Language constants [ (null) -] @constant.language.null +] @constant.language.null.ts [ (undefined) -] @constant.language.undefined +] @constant.language.undefined.ts - ((identifier) @constant.language.nan - (#eq? @constant.language.nan "NaN")) +((identifier) @constant.language.nan.ts + (#eq? @constant.language.nan.ts "NaN")) - ((identifier) @constant.language.infinity - (#eq? @constant.language.infinity "Infinity")) +((identifier) @constant.language.infinity.ts + (#eq? @constant.language.infinity.ts "Infinity")) [ (true) -] @constant.language.boolean.true +] @constant.language.boolean.true.ts [ (false) -] @constant.language.boolean.false +] @constant.language.boolean.false.ts + +(literal_type + [ + (null) + (undefined) + (true) + (false) + ] @support.type.builtin.ts) (namespace_import - "*" @constant.language) + "*" @constant.language.ts) diff --git a/src/vs/editor/common/model/tokenStore.ts b/src/vs/editor/common/model/tokenStore.ts index 49264b40ae61..c180e1b15a3e 100644 --- a/src/vs/editor/common/model/tokenStore.ts +++ b/src/vs/editor/common/model/tokenStore.ts @@ -35,7 +35,9 @@ class ListNode implements IDisposable { this._length += node.length; this._updateParentLength(node.length); - node.parent = this; + if (!isLeaf(node)) { + node.parent = this; + } } private _updateParentLength(delta: number) { @@ -61,7 +63,9 @@ class ListNode implements IDisposable { this._length += node.length; this._updateParentLength(node.length); - node.parent = this; + if (!isLeaf(node)) { + node.parent = this; + } } unprependChild(): Node { @@ -91,7 +95,6 @@ type Node = ListNode | LeafNode; interface LeafNode { readonly length: number; - parent?: ListNode; token: number; tokenQuality: TokenQuality; height: 0; @@ -270,7 +273,9 @@ export class TokenStore implements IDisposable { const currentOffset = node.offset; if (currentOffset < updateOffsetStart && currentOffset + node.node.length <= updateOffsetStart) { - node.node.parent = undefined; + if (!isLeaf(node.node)) { + node.node.parent = undefined; + } precedingNodes.push(node.node); continue; } else if (isLeaf(node.node) && (currentOffset < updateOffsetStart)) { @@ -284,7 +289,9 @@ export class TokenStore implements IDisposable { } if (currentOffset >= firstUnchangedOffsetAfterUpdate) { - node.node.parent = undefined; + if (!isLeaf(node.node)) { + node.node.parent = undefined; + } postcedingNodes.push(node.node); continue; } else if (isLeaf(node.node) && (currentOffset + node.node.length >= firstUnchangedOffsetAfterUpdate)) { @@ -492,7 +499,7 @@ export class TokenStore implements IDisposable { while (stack.length > 0) { const [node, visited] = stack.pop()!; if (isLeaf(node)) { - node.parent = undefined; + // leaf node does not need to be disposed } else if (!visited) { stack.push([node, true]); for (let i = node.children.length - 1; i >= 0; i--) { diff --git a/src/vs/editor/common/model/treeSitterTokenStoreService.ts b/src/vs/editor/common/model/treeSitterTokenStoreService.ts index 8c544cdb4164..c304ad9309c8 100644 --- a/src/vs/editor/common/model/treeSitterTokenStoreService.ts +++ b/src/vs/editor/common/model/treeSitterTokenStoreService.ts @@ -9,16 +9,19 @@ import { TokenQuality, TokenStore, TokenUpdate } from './tokenStore.js'; import { InstantiationType, registerSingleton } from '../../../platform/instantiation/common/extensions.js'; import { createDecorator } from '../../../platform/instantiation/common/instantiation.js'; import { DisposableStore, IDisposable } from '../../../base/common/lifecycle.js'; +import { IModelContentChangedEvent } from '../textModelEvents.js'; export interface ITreeSitterTokenizationStoreService { readonly _serviceBrand: undefined; setTokens(model: ITextModel, tokens: TokenUpdate[], tokenQuality: TokenQuality): void; + handleContentChanged(model: ITextModel, e: IModelContentChangedEvent): void; getTokens(model: ITextModel, line: number): Uint32Array | undefined; updateTokens(model: ITextModel, version: number, updates: { oldRangeLength?: number; newTokens: TokenUpdate[] }[], tokenQuality: TokenQuality): void; markForRefresh(model: ITextModel, range: Range): void; getNeedsRefresh(model: ITextModel): { range: Range; startOffset: number; endOffset: number }[]; hasTokens(model: ITextModel, accurateForRange?: Range): boolean; rangeHasTokens(model: ITextModel, range: Range, minimumTokenQuality: TokenQuality): boolean; + delete(model: ITextModel): void; } export const ITreeSitterTokenizationStoreService = createDecorator('treeSitterTokenizationStoreService'); @@ -41,37 +44,6 @@ class TreeSitterTokenizationStoreService implements ITreeSitterTokenizationStore this.tokens.set(model, { store: store, accurateVersion: model.getVersionId(), disposables, guessVersion: model.getVersionId() }); store.buildStore(tokens, tokenQuality); - disposables.add(model.onDidChangeContent(e => { - const storeInfo = this.tokens.get(model); - if (!storeInfo) { - return; - } - - storeInfo.guessVersion = e.versionId; - for (const change of e.changes) { - if (change.text.length > change.rangeLength) { - // If possible, use the token before the change as the starting point for the new token. - // This is more likely to let the new text be the correct color as typeing is usually at the end of the token. - const offset = change.rangeOffset > 0 ? change.rangeOffset - 1 : change.rangeOffset; - const oldToken = storeInfo.store.getTokenAt(offset); - let newToken: TokenUpdate; - if (oldToken) { - // Insert. Just grow the token at this position to include the insert. - newToken = { startOffsetInclusive: oldToken.startOffsetInclusive, length: oldToken.length + change.text.length - change.rangeLength, token: oldToken.token }; - } else { - // The document got larger and the change is at the end of the document. - newToken = { startOffsetInclusive: offset, length: change.text.length + 1, token: 0 }; - } - storeInfo.store.update(oldToken?.length ?? 0, [newToken], TokenQuality.EditGuess); - } else if (change.text.length < change.rangeLength) { - // Delete. Delete the tokens at the corresponding range. - const deletedCharCount = change.rangeLength - change.text.length; - storeInfo.store.delete(deletedCharCount, change.rangeOffset); - } - const refreshLength = change.rangeLength > change.text.length ? change.rangeLength : change.text.length; - storeInfo.store.markForRefresh(change.rangeOffset, change.rangeOffset + refreshLength); - } - })); disposables.add(model.onWillDispose(() => { const storeInfo = this.tokens.get(model); if (storeInfo) { @@ -81,6 +53,39 @@ class TreeSitterTokenizationStoreService implements ITreeSitterTokenizationStore })); } + handleContentChanged(model: ITextModel, e: IModelContentChangedEvent): void { + const storeInfo = this.tokens.get(model); + if (!storeInfo) { + return; + } + + storeInfo.guessVersion = e.versionId; + for (const change of e.changes) { + if (change.text.length > change.rangeLength) { + // If possible, use the token before the change as the starting point for the new token. + // This is more likely to let the new text be the correct color as typeing is usually at the end of the token. + const offset = change.rangeOffset > 0 ? change.rangeOffset - 1 : change.rangeOffset; + const oldToken = storeInfo.store.getTokenAt(offset); + let newToken: TokenUpdate; + if (oldToken) { + // Insert. Just grow the token at this position to include the insert. + newToken = { startOffsetInclusive: oldToken.startOffsetInclusive, length: oldToken.length + change.text.length - change.rangeLength, token: oldToken.token }; + } else { + // The document got larger and the change is at the end of the document. + newToken = { startOffsetInclusive: offset, length: change.text.length, token: 0 }; + } + storeInfo.store.update(oldToken?.length ?? 0, [newToken], TokenQuality.EditGuess); + } else if (change.text.length < change.rangeLength) { + // Delete. Delete the tokens at the corresponding range. + const deletedCharCount = change.rangeLength - change.text.length; + storeInfo.store.delete(deletedCharCount, change.rangeOffset); + } + const refreshLength = change.rangeLength > change.text.length ? change.rangeLength : change.text.length; + storeInfo.store.markForRefresh(change.rangeOffset, change.rangeOffset + refreshLength); + } + + } + rangeHasTokens(model: ITextModel, range: Range, minimumTokenQuality: TokenQuality): boolean { const tokens = this.tokens.get(model); if (!tokens) { @@ -158,6 +163,14 @@ class TreeSitterTokenizationStoreService implements ITreeSitterTokenizationStore })); } + delete(model: ITextModel): void { + const storeInfo = this.tokens.get(model); + if (storeInfo) { + storeInfo.disposables.dispose(); + this.tokens.delete(model); + } + } + dispose(): void { for (const [, value] of this.tokens) { value.disposables.dispose(); diff --git a/src/vs/editor/common/model/treeSitterTokens.ts b/src/vs/editor/common/model/treeSitterTokens.ts index 105a81b2f07d..566de4466fb8 100644 --- a/src/vs/editor/common/model/treeSitterTokens.ts +++ b/src/vs/editor/common/model/treeSitterTokens.ts @@ -24,7 +24,7 @@ export class TreeSitterTokens extends AbstractTokens { private _lastLanguageId: string | undefined; private readonly _tokensChangedListener: MutableDisposable = this._register(new MutableDisposable()); - private readonly _firstTokenizationCompleteListener: MutableDisposable = this._register(new MutableDisposable()); + private readonly _onDidChangeBackgroundTokenization: MutableDisposable = this._register(new MutableDisposable()); constructor(languageIdCodec: ILanguageIdCodec, textModel: TextModel, @@ -45,11 +45,10 @@ export class TreeSitterTokens extends AbstractTokens { this._onDidChangeTokens.fire(e.changes); } }); - this._firstTokenizationCompleteListener.value = this._tokenizationSupport?.onDidCompleteFirstTokenization(e => { + this._onDidChangeBackgroundTokenization.value = this._tokenizationSupport?.onDidChangeBackgroundTokenization(e => { if (e.textModel === this._textModel) { this._backgroundTokenizationState = BackgroundTokenizationState.Completed; this._onDidChangeBackgroundTokenizationState.fire(); - this._firstTokenizationCompleteListener.clear(); } }); } @@ -57,7 +56,7 @@ export class TreeSitterTokens extends AbstractTokens { public getLineTokens(lineNumber: number): LineTokens { const content = this._textModel.getLineContent(lineNumber); - if (this._tokenizationSupport) { + if (this._tokenizationSupport && content.length > 0) { const rawTokens = this._tokenStore.getTokens(this._textModel, lineNumber); if (rawTokens) { return new LineTokens(rawTokens, content, this._languageIdCodec); @@ -89,6 +88,8 @@ export class TreeSitterTokens extends AbstractTokens { if (e.isFlush) { // Don't fire the event, as the view might not have got the text change event yet this.resetTokenization(false); + } else { + this._tokenStore.handleContentChanged(this._textModel, e); } } diff --git a/src/vs/editor/common/services/treeSitter/cursorUtils.ts b/src/vs/editor/common/services/treeSitter/cursorUtils.ts new file mode 100644 index 000000000000..b5b6e701dd3f --- /dev/null +++ b/src/vs/editor/common/services/treeSitter/cursorUtils.ts @@ -0,0 +1,84 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import type * as Parser from '@vscode/tree-sitter-wasm'; + +export function gotoNextSibling(newCursor: Parser.TreeCursor, oldCursor: Parser.TreeCursor) { + const n = newCursor.gotoNextSibling(); + const o = oldCursor.gotoNextSibling(); + if (n !== o) { + throw new Error('Trees are out of sync'); + } + return n && o; +} + +export function gotoParent(newCursor: Parser.TreeCursor, oldCursor: Parser.TreeCursor) { + const n = newCursor.gotoParent(); + const o = oldCursor.gotoParent(); + if (n !== o) { + throw new Error('Trees are out of sync'); + } + return n && o; +} + +export function gotoNthChild(newCursor: Parser.TreeCursor, oldCursor: Parser.TreeCursor, index: number) { + const n = newCursor.gotoFirstChild(); + const o = oldCursor.gotoFirstChild(); + if (n !== o) { + throw new Error('Trees are out of sync'); + } + if (index === 0) { + return n && o; + } + for (let i = 1; i <= index; i++) { + const nn = newCursor.gotoNextSibling(); + const oo = oldCursor.gotoNextSibling(); + if (nn !== oo) { + throw new Error('Trees are out of sync'); + } + if (!nn || !oo) { + return false; + } + } + return n && o; +} + +export function nextSiblingOrParentSibling(newCursor: Parser.TreeCursor, oldCursor: Parser.TreeCursor) { + do { + if (newCursor.currentNode.nextSibling) { + return gotoNextSibling(newCursor, oldCursor); + } + if (newCursor.currentNode.parent) { + gotoParent(newCursor, oldCursor); + } + } while (newCursor.currentNode.nextSibling || newCursor.currentNode.parent); + return false; +} + +export function getClosestPreviousNodes(cursor: Parser.TreeCursor, tree: Parser.Tree): Parser.Node | undefined { + // Go up parents until the end of the parent is before the start of the current. + const findPrev = tree.walk(); + findPrev.resetTo(cursor); + + const startingNode = cursor.currentNode; + do { + if (findPrev.currentNode.previousSibling && ((findPrev.currentNode.endIndex - findPrev.currentNode.startIndex) !== 0)) { + findPrev.gotoPreviousSibling(); + } else { + while (!findPrev.currentNode.previousSibling && findPrev.currentNode.parent) { + findPrev.gotoParent(); + } + findPrev.gotoPreviousSibling(); + } + } while ((findPrev.currentNode.endIndex > startingNode.startIndex) + && (findPrev.currentNode.parent || findPrev.currentNode.previousSibling) + + && (findPrev.currentNode.id !== startingNode.id)); + + if ((findPrev.currentNode.id !== startingNode.id) && findPrev.currentNode.endIndex <= startingNode.startIndex) { + return findPrev.currentNode; + } else { + return undefined; + } +} diff --git a/src/vs/editor/common/services/treeSitter/treeSitterParserService.ts b/src/vs/editor/common/services/treeSitter/treeSitterParserService.ts index 0205c524395a..985207918cd5 100644 --- a/src/vs/editor/common/services/treeSitter/treeSitterParserService.ts +++ b/src/vs/editor/common/services/treeSitter/treeSitterParserService.ts @@ -22,9 +22,9 @@ import { IEnvironmentService } from '../../../../platform/environment/common/env import { CancellationError, isCancellationError } from '../../../../base/common/errors.js'; import { PromiseResult } from '../../../../base/common/observable.js'; import { Range } from '../../core/range.js'; -import { Position } from '../../core/position.js'; import { LimitedQueue } from '../../../../base/common/async.js'; import { TextLength } from '../../core/textLength.js'; +import { getClosestPreviousNodes, gotoNthChild, gotoParent, nextSiblingOrParentSibling } from './cursorUtils.js'; const EDITOR_TREESITTER_TELEMETRY = 'editor.experimental.treeSitterTelemetry'; const MODULE_LOCATION_SUBPATH = `@vscode/tree-sitter-wasm/wasm`; @@ -88,7 +88,7 @@ export class TextModelTreeSitter extends Disposable implements ITextModelTreeSit const treeSitterTree = this._parseSessionDisposables.add(new TreeSitterParseResult(new Parser(), language, this._logService, this._telemetryService)); this._parseResult = treeSitterTree; this._parseSessionDisposables.add(treeSitterTree.onDidUpdate(e => { - if (e.ranges && (e.versionId > this._versionId)) { + if (e.ranges && (e.versionId >= this._versionId)) { this._versionId = e.versionId; this._onDidChangeParseResult.fire({ ranges: e.ranges, versionId: e.versionId }); } @@ -133,16 +133,6 @@ const enum TelemetryParseType { Incremental = 'incrementalParse' } -interface ChangedRange { - newNodeId: number; - newStartPosition: Position; - newEndPosition: Position; - newStartIndex: number; - newEndIndex: number; - oldStartIndex: number; - oldEndIndex: number; -} - export class TreeSitterParseResult implements IDisposable, ITreeSitterParseResult { private _tree: Parser.Tree | undefined; private _lastFullyParsed: Parser.Tree | undefined; @@ -172,91 +162,13 @@ export class TreeSitterParseResult implements IDisposable, ITreeSitterParseResul get tree() { return this._lastFullyParsed; } get isDisposed() { return this._isDisposed; } - private findChangedNodes(newTree: Parser.Tree, oldTree: Parser.Tree): ChangedRange[] { + private findChangedNodes(newTree: Parser.Tree, oldTree: Parser.Tree): Parser.Node[] { const newCursor = newTree.walk(); const oldCursor = oldTree.walk(); - const gotoNextSibling = () => { - const n = newCursor.gotoNextSibling(); - const o = oldCursor.gotoNextSibling(); - if (n !== o) { - throw new Error('Trees are out of sync'); - } - return n && o; - }; - const gotoParent = () => { - const n = newCursor.gotoParent(); - const o = oldCursor.gotoParent(); - if (n !== o) { - throw new Error('Trees are out of sync'); - } - return n && o; - }; - const gotoNthChild = (index: number) => { - const n = newCursor.gotoFirstChild(); - const o = oldCursor.gotoFirstChild(); - if (n !== o) { - throw new Error('Trees are out of sync'); - } - if (index === 0) { - return n && o; - } - for (let i = 1; i <= index; i++) { - const nn = newCursor.gotoNextSibling(); - const oo = oldCursor.gotoNextSibling(); - if (nn !== oo) { - throw new Error('Trees are out of sync'); - } - if (!nn || !oo) { - return false; - } - } - return n && o; - }; - const changedRanges: ChangedRange[] = []; + const nodes: Parser.Node[] = []; let next = true; - const nextSiblingOrParentSibling = () => { - do { - if (newCursor.currentNode.nextSibling) { - return gotoNextSibling(); - } - if (newCursor.currentNode.parent) { - gotoParent(); - } - } while (newCursor.currentNode.nextSibling || newCursor.currentNode.parent); - return false; - }; - - const getClosestPreviousNodes = (): { old: Parser.Node; new: Parser.Node } | undefined => { - // Go up parents until the end of the parent is before the start of the current. - const newFindPrev = newTree.walk(); - newFindPrev.resetTo(newCursor); - const oldFindPrev = oldTree.walk(); - oldFindPrev.resetTo(oldCursor); - const startingNode = newCursor.currentNode; - do { - if (newFindPrev.currentNode.previousSibling && ((newFindPrev.currentNode.endIndex - newFindPrev.currentNode.startIndex) !== 0)) { - newFindPrev.gotoPreviousSibling(); - oldFindPrev.gotoPreviousSibling(); - } else { - while (!newFindPrev.currentNode.previousSibling && newFindPrev.currentNode.parent) { - newFindPrev.gotoParent(); - oldFindPrev.gotoParent(); - } - newFindPrev.gotoPreviousSibling(); - oldFindPrev.gotoPreviousSibling(); - } - } while ((newFindPrev.currentNode.endIndex > startingNode.startIndex) - && (newFindPrev.currentNode.parent || newFindPrev.currentNode.previousSibling) - - && (newFindPrev.currentNode.id !== startingNode.id)); - if ((newFindPrev.currentNode.id !== startingNode.id) && newFindPrev.currentNode.endIndex <= startingNode.startIndex) { - return { old: oldFindPrev.currentNode, new: newFindPrev.currentNode }; - } else { - return undefined; - } - }; do { if (newCursor.currentNode.hasChanges) { // Check if only one of the children has changes. @@ -272,63 +184,110 @@ export class TreeSitterParseResult implements IDisposable, ITreeSitterParseResul return c?.hasChanges; }); // If we have changes and we *had* an error, the whole node should be refreshed. - if ((changedChildren.length === 0) || oldCursor.currentNode.hasError) { + if ((changedChildren.length === 0)) { // walk up again until we get to the first one that's named as unnamed nodes can be too granular while (newCursor.currentNode.parent && !newCursor.currentNode.isNamed && next) { - next = gotoParent(); + next = gotoParent(newCursor, oldCursor); } const newNode = newCursor.currentNode; - const oldNode = oldCursor.currentNode; - - const newEndPosition = new Position(newNode.endPosition.row + 1, newNode.endPosition.column + 1); - const oldEndIndex = oldNode.endIndex; - - // Fill holes between nodes. - const closestPrev = getClosestPreviousNodes(); - const newStartPosition = new Position(closestPrev ? closestPrev.new.endPosition.row + 1 : newNode.startPosition.row + 1, closestPrev ? closestPrev.new.endPosition.column + 1 : newNode.startPosition.column + 1); - const newStartIndex = closestPrev ? closestPrev.new.endIndex : newNode.startIndex; - const oldStartIndex = closestPrev ? closestPrev.old.endIndex : oldNode.startIndex; - - changedRanges.push({ newStartPosition, newEndPosition, oldStartIndex, oldEndIndex, newNodeId: newNode.id, newStartIndex, newEndIndex: newNode.endIndex }); - next = nextSiblingOrParentSibling(); + nodes.push(newNode); + next = nextSiblingOrParentSibling(newCursor, oldCursor); } else if (changedChildren.length >= 1) { - next = gotoNthChild(indexChangedChildren[0]); + next = gotoNthChild(newCursor, oldCursor, indexChangedChildren[0]); } } else { - next = nextSiblingOrParentSibling(); + next = nextSiblingOrParentSibling(newCursor, oldCursor); } } while (next); - if (changedRanges.length === 0 && newTree.rootNode.hasChanges) { - return [{ newStartPosition: new Position(newTree.rootNode.startPosition.row + 1, newTree.rootNode.startPosition.column + 1), newEndPosition: new Position(newTree.rootNode.endPosition.row + 1, newTree.rootNode.endPosition.column + 1), oldStartIndex: oldTree.rootNode.startIndex, oldEndIndex: oldTree.rootNode.endIndex, newStartIndex: newTree.rootNode.startIndex, newEndIndex: newTree.rootNode.endIndex, newNodeId: newTree.rootNode.id }]; - } else { - return changedRanges; - } + return nodes; } - private calculateRangeChange(changedNodes: ChangedRange[] | undefined): RangeChange[] | undefined { - if (!changedNodes) { - return undefined; - } + private findTreeChanges(newTree: Parser.Tree, changedNodes: Parser.Node[]): RangeChange[] { + const mergedChanges: RangeChange[] = []; + + // Find the parent in the new tree of the changed node + for (let nodeIndex = 0; nodeIndex < changedNodes.length; nodeIndex++) { + const node = changedNodes[nodeIndex]; + + if (mergedChanges.length > 0) { + if ((node.startIndex > mergedChanges[mergedChanges.length - 1].newRangeStartOffset) && (node.endIndex < mergedChanges[mergedChanges.length - 1].newRangeEndOffset)) { + // This node is within the previous range, skip it + continue; + } + } - // Collapse conginguous ranges - const ranges: RangeChange[] = []; - for (let i = 0; i < changedNodes.length; i++) { - const node = changedNodes[i]; - - // Check if contiguous with previous - const prevNode = changedNodes[i - 1]; - if ((i > 0) && prevNode.newEndPosition.equals(node.newStartPosition)) { - const prevRangeChange = ranges[ranges.length - 1]; - prevRangeChange.newRange = new Range(prevRangeChange.newRange.startLineNumber, prevRangeChange.newRange.startColumn, node.newEndPosition.lineNumber, node.newEndPosition.column); - prevRangeChange.oldRangeLength = node.oldEndIndex - prevNode.oldStartIndex; - prevRangeChange.newRangeEndOffset = node.newEndIndex; + const cursor = newTree.walk(); + const cursorContainersNode = () => cursor.startIndex <= node.startIndex && cursor.endIndex >= node.endIndex; + + while (cursorContainersNode()) { + // See if we can go to a child + let child = cursor.gotoFirstChild(); + let foundChild = false; + while (child) { + if (cursorContainersNode()) { + foundChild = true; + break; + } else { + child = cursor.gotoNextSibling(); + } + } + if (!foundChild) { + cursor.gotoParent(); + break; + } + if (cursor.currentNode.childCount === 0) { + break; + } + } + + let nodesInRange: Parser.Node[]; + // It's possible we end up with a really large range if the parent node is big + // Try to avoid this large range by finding several smaller nodes that together encompass the range of the changed node. + const foundNodeSize = cursor.endIndex - cursor.startIndex; + if (foundNodeSize > 5000) { + // Try to find 3 consecutive nodes that together encompass the changed node. + let child = cursor.gotoFirstChild(); + nodesInRange = []; + while (child) { + if (cursor.startIndex <= node.startIndex && cursor.endIndex > node.startIndex) { + // Found the starting point of our nodes + nodesInRange.push(cursor.currentNode); + do { + child = cursor.gotoNextSibling(); + } while (child && (cursor.endIndex < node.endIndex)); + + nodesInRange.push(cursor.currentNode); + break; + } + child = cursor.gotoNextSibling(); + } + } else { + nodesInRange = [cursor.currentNode]; + } + + // Fill in gaps between nodes + // Reset the cursor to the first node in the range; + while (cursor.currentNode.id !== nodesInRange[0].id) { + cursor.gotoPreviousSibling(); + } + const previousNode = getClosestPreviousNodes(cursor, newTree); + const startingPosition = previousNode ? previousNode.endPosition : nodesInRange[0].startPosition; + const startingIndex = previousNode ? previousNode.endIndex : nodesInRange[0].startIndex; + const endingPosition = nodesInRange[nodesInRange.length - 1].endPosition; + const endingIndex = nodesInRange[nodesInRange.length - 1].endIndex; + + const newChange = { newRange: new Range(startingPosition.row + 1, startingPosition.column + 1, endingPosition.row + 1, endingPosition.column + 1), newRangeStartOffset: startingIndex, newRangeEndOffset: endingIndex }; + if ((mergedChanges.length > 0) && (mergedChanges[mergedChanges.length - 1].newRangeEndOffset >= newChange.newRangeStartOffset)) { + // Merge the changes + mergedChanges[mergedChanges.length - 1].newRange = Range.fromPositions(mergedChanges[mergedChanges.length - 1].newRange.getStartPosition(), newChange.newRange.getEndPosition()); + mergedChanges[mergedChanges.length - 1].newRangeEndOffset = newChange.newRangeEndOffset; } else { - ranges.push({ newRange: Range.fromPositions(node.newStartPosition, node.newEndPosition), oldRangeLength: node.oldEndIndex - node.oldStartIndex, newRangeStartOffset: node.newStartIndex, newRangeEndOffset: node.newEndIndex }); + mergedChanges.push(newChange); } } - return ranges; + return mergedChanges; } private _onDidChangeContentQueue: LimitedQueue = new LimitedQueue(); @@ -346,15 +305,19 @@ export class TreeSitterParseResult implements IDisposable, ITreeSitterParseResul return; } - let ranges: RangeChange[] | undefined; + const oldTree = this._lastFullyParsed; + let changedNodes: Parser.Node[] | undefined; if (this._lastFullyParsedWithEdits && this._lastFullyParsed) { - ranges = this.calculateRangeChange(this.findChangedNodes(this._lastFullyParsedWithEdits, this._lastFullyParsed)); + changedNodes = this.findChangedNodes(this._lastFullyParsedWithEdits, this._lastFullyParsed); } const completed = await this._parseAndUpdateTree(model, version); if (completed) { - if (!ranges) { - ranges = [{ newRange: model.getFullModelRange(), oldRangeLength: model.getValueLength(), newRangeStartOffset: 0, newRangeEndOffset: model.getValueLength() }]; + let ranges: RangeChange[] | undefined; + if (!changedNodes) { + ranges = [{ newRange: model.getFullModelRange(), newRangeStartOffset: 0, newRangeEndOffset: model.getValueLength() }]; + } else if (oldTree && changedNodes) { + ranges = this.findTreeChanges(completed, changedNodes); } this._onDidUpdate.fire({ ranges, versionId: version }); } @@ -456,7 +419,7 @@ export class TreeSitterParseResult implements IDisposable, ITreeSitterParseResul private sendParseTimeTelemetry(parseType: TelemetryParseType, languageId: string, time: number, passes: number): void { this._logService.debug(`Tree parsing (${parseType}) took ${time} ms and ${passes} passes.`); type ParseTimeClassification = { - owner: 'alros'; + owner: 'alexr00'; comment: 'Used to understand how long it takes to parse a tree-sitter tree'; languageId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The programming language ID.' }; time: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; isMeasurement: true; comment: 'The ms it took to parse' }; diff --git a/src/vs/editor/common/services/treeSitterParserService.ts b/src/vs/editor/common/services/treeSitterParserService.ts index 5e96ebd1617e..64e5a870d8fd 100644 --- a/src/vs/editor/common/services/treeSitterParserService.ts +++ b/src/vs/editor/common/services/treeSitterParserService.ts @@ -23,7 +23,6 @@ export interface RangeWithOffsets { export interface RangeChange { newRange: Range; - oldRangeLength: number; newRangeStartOffset: number; newRangeEndOffset: number; } diff --git a/src/vs/editor/common/standaloneStrings.ts b/src/vs/editor/common/standaloneStrings.ts index 2a82f208c622..f1b89210a4bf 100644 --- a/src/vs/editor/common/standaloneStrings.ts +++ b/src/vs/editor/common/standaloneStrings.ts @@ -12,7 +12,8 @@ export namespace AccessibilityHelpNLS { export const editableDiffEditor = nls.localize("editableDiffEditor", "You are in a pane of a diff editor."); export const readonlyEditor = nls.localize("readonlyEditor", "You are in a read-only code editor."); export const editableEditor = nls.localize("editableEditor", "You are in a code editor."); - export const activeEditorState = nls.localize("activeEditorState", "Get information about the active editor such as Modified, Problems, and more by setting activeEditorState as a part of the window.title setting."); + export const defaultWindowTitleIncludesEditorState = nls.localize("defaultWindowTitleIncludesEditorState", "activeEditorState - such as modified, problems, and more, is included as a part of the window.title setting by default. Disable it with accessibility.windowTitleOptimized."); + export const defaultWindowTitleExcludingEditorState = nls.localize("defaultWindowTitleExcludingEditorState", "activeEditorState - such as modified, problems, and more, is currently not included as a part of the window.title setting by default. Enable it with accessibility.windowTitleOptimized."); export const toolbar = nls.localize("toolbar", "Around the workbench, when the screen reader announces you've landed in a toolbar, use narrow keys to navigate between the toolbar's actions."); export const changeConfigToOnMac = nls.localize("changeConfigToOnMac", "Configure the application to be optimized for usage with a Screen Reader (Command+E)."); export const changeConfigToOnWinLinux = nls.localize("changeConfigToOnWinLinux", "Configure the application to be optimized for usage with a Screen Reader (Control+E)."); @@ -41,7 +42,7 @@ export namespace AccessibilityHelpNLS { export const debugExecuteSelection = nls.localize('debugConsole.executeSelection', "The Debug: Execute Selection command{0} will execute the selected text in the debug console.", ''); export const chatEditorModification = nls.localize('chatEditorModification', "The editor contains pending modifications that have been made by chat."); export const chatEditorRequestInProgress = nls.localize('chatEditorRequestInProgress', "The editor is currently waiting for modifications to be made by chat."); - export const chatEditActions = nls.localize('chatEditing.navigation', 'Navigate between edits in the editor with navigate previous{0} and next{1} and accept{2}, reject{3} or view the diff{4} for the current change.', '', '', '', '', ''); + export const chatEditActions = nls.localize('chatEditing.navigation', 'Navigate between edits in the editor with navigate previous{0} and next{1} and accept{2}, reject{3} or view the diff{4} for the current change.', '', '', '', '', ''); } export namespace InspectTokensNLS { diff --git a/src/vs/editor/common/tokens/lineTokens.ts b/src/vs/editor/common/tokens/lineTokens.ts index 72d94d120d40..5546834b94dc 100644 --- a/src/vs/editor/common/tokens/lineTokens.ts +++ b/src/vs/editor/common/tokens/lineTokens.ts @@ -9,6 +9,7 @@ import { IPosition } from '../core/position.js'; import { ITextModel } from '../model.js'; import { OffsetRange } from '../core/offsetRange.js'; import { TokenArray, TokenArrayBuilder } from './tokenArray.js'; +import { BugIndicatingError } from '../../../base/common/errors.js'; export interface IViewLineTokens { @@ -101,6 +102,10 @@ export class LineTokens implements IViewLineTokens { ) >>> 0; constructor(tokens: Uint32Array, text: string, decoder: ILanguageIdCodec) { + const tokensLength = tokens.length > 1 ? tokens[tokens.length - 2] : 0; + if (tokensLength !== text.length) { + throw new BugIndicatingError('Token length and text length do not match!'); + } this._tokens = tokens; this._tokensCount = (this._tokens.length >>> 1); this._text = text; diff --git a/src/vs/editor/common/viewModel.ts b/src/vs/editor/common/viewModel.ts index 444bac5d610e..1aefa04abeb7 100644 --- a/src/vs/editor/common/viewModel.ts +++ b/src/vs/editor/common/viewModel.ts @@ -40,6 +40,7 @@ export interface IViewModel extends ICursorSimpleModel { setViewport(startLineNumber: number, endLineNumber: number, centeredLineNumber: number): void; visibleLinesStabilized(): void; setHasFocus(hasFocus: boolean): void; + setHasWidgetFocus(hasWidgetFocus: boolean): void; onCompositionStart(): void; onCompositionEnd(): void; diff --git a/src/vs/editor/common/viewModel/viewModelImpl.ts b/src/vs/editor/common/viewModel/viewModelImpl.ts index 96043722d84c..8918d324ce7c 100644 --- a/src/vs/editor/common/viewModel/viewModelImpl.ts +++ b/src/vs/editor/common/viewModel/viewModelImpl.ts @@ -36,7 +36,7 @@ import { ILineBreaksComputer, ILineBreaksComputerFactory, InjectedText } from '. import { ViewEventHandler } from '../viewEventHandler.js'; import { ICoordinatesConverter, InlineDecoration, IViewModel, IWhitespaceChangeAccessor, MinimapLinesRenderingData, OverviewRulerDecorationsGroup, ViewLineData, ViewLineRenderingData, ViewModelDecoration } from '../viewModel.js'; import { ViewModelDecorations } from './viewModelDecorations.js'; -import { FocusChangedEvent, HiddenAreasChangedEvent, ModelContentChangedEvent, ModelDecorationsChangedEvent, ModelLanguageChangedEvent, ModelLanguageConfigurationChangedEvent, ModelOptionsChangedEvent, ModelTokensChangedEvent, OutgoingViewModelEvent, ReadOnlyEditAttemptEvent, ScrollChangedEvent, ViewModelEventDispatcher, ViewModelEventsCollector, ViewZonesChangedEvent } from '../viewModelEventDispatcher.js'; +import { FocusChangedEvent, HiddenAreasChangedEvent, ModelContentChangedEvent, ModelDecorationsChangedEvent, ModelLanguageChangedEvent, ModelLanguageConfigurationChangedEvent, ModelOptionsChangedEvent, ModelTokensChangedEvent, OutgoingViewModelEvent, ReadOnlyEditAttemptEvent, ScrollChangedEvent, ViewModelEventDispatcher, ViewModelEventsCollector, ViewZonesChangedEvent, WidgetFocusChangedEvent } from '../viewModelEventDispatcher.js'; import { IViewModelLines, ViewModelLinesFromModelAsIs, ViewModelLinesFromProjectedModel } from './viewModelLines.js'; import { IThemeService } from '../../../platform/theme/common/themeService.js'; import { GlyphMarginLanesModel } from './glyphLanesModel.js'; @@ -153,9 +153,9 @@ export class ViewModel extends Disposable implements IViewModel { this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewTokensColorsChangedEvent()); })); - this._register(this._themeService.onDidColorThemeChange((theme) => { + this._register(this._themeService.onDidColorThemeChange((e) => { this._invalidateDecorationsColorCache(); - this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewThemeChangedEvent(theme)); + this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewThemeChangedEvent(e.theme)); })); this._updateConfigurationViewLineCountNow(); @@ -216,6 +216,10 @@ export class ViewModel extends Disposable implements IViewModel { this._eventDispatcher.emitOutgoingEvent(new FocusChangedEvent(!hasFocus, hasFocus)); } + public setHasWidgetFocus(hasWidgetFocus: boolean): void { + this._eventDispatcher.emitOutgoingEvent(new WidgetFocusChangedEvent(!hasWidgetFocus, hasWidgetFocus)); + } + public onCompositionStart(): void { this._eventDispatcher.emitSingleViewEvent(new viewEvents.ViewCompositionStartEvent()); } diff --git a/src/vs/editor/common/viewModelEventDispatcher.ts b/src/vs/editor/common/viewModelEventDispatcher.ts index 7fddb80251ff..fc91875b3196 100644 --- a/src/vs/editor/common/viewModelEventDispatcher.ts +++ b/src/vs/editor/common/viewModelEventDispatcher.ts @@ -176,6 +176,7 @@ export class ViewModelEventsCollector { export type OutgoingViewModelEvent = ( ContentSizeChangedEvent | FocusChangedEvent + | WidgetFocusChangedEvent | ScrollChangedEvent | ViewZonesChangedEvent | HiddenAreasChangedEvent @@ -192,6 +193,7 @@ export type OutgoingViewModelEvent = ( export const enum OutgoingViewModelEventKind { ContentSizeChanged, FocusChanged, + WidgetFocusChanged, ScrollChanged, ViewZonesChanged, HiddenAreasChanged, @@ -262,6 +264,30 @@ export class FocusChangedEvent { } } +export class WidgetFocusChangedEvent { + + public readonly kind = OutgoingViewModelEventKind.WidgetFocusChanged; + + readonly oldHasFocus: boolean; + readonly hasFocus: boolean; + + constructor(oldHasFocus: boolean, hasFocus: boolean) { + this.oldHasFocus = oldHasFocus; + this.hasFocus = hasFocus; + } + + public isNoOp(): boolean { + return (this.oldHasFocus === this.hasFocus); + } + + public attemptToMerge(other: OutgoingViewModelEvent): OutgoingViewModelEvent | null { + if (other.kind !== this.kind) { + return null; + } + return new FocusChangedEvent(this.oldHasFocus, other.hasFocus); + } +} + export class ScrollChangedEvent { public readonly kind = OutgoingViewModelEventKind.ScrollChanged; diff --git a/src/vs/editor/contrib/clipboard/browser/clipboard.ts b/src/vs/editor/contrib/clipboard/browser/clipboard.ts index a5bc65bc1008..aaf909057813 100644 --- a/src/vs/editor/contrib/clipboard/browser/clipboard.ts +++ b/src/vs/editor/contrib/clipboard/browser/clipboard.ts @@ -157,7 +157,7 @@ class ExecCommandCopyWithSyntaxHighlightingAction extends EditorAction { constructor() { super({ id: 'editor.action.clipboardCopyWithSyntaxHighlightingAction', - label: nls.localize2('actions.clipboard.copyWithSyntaxHighlightingLabel', "Copy With Syntax Highlighting"), + label: nls.localize2('actions.clipboard.copyWithSyntaxHighlightingLabel', "Copy with Syntax Highlighting"), precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.textInputFocus, diff --git a/src/vs/editor/contrib/codeAction/browser/codeActionController.ts b/src/vs/editor/contrib/codeAction/browser/codeActionController.ts index de55ee700d9a..e65f9ab38da3 100644 --- a/src/vs/editor/contrib/codeAction/browser/codeActionController.ts +++ b/src/vs/editor/contrib/codeAction/browser/codeActionController.ts @@ -10,20 +10,9 @@ import { IAction } from '../../../../base/common/actions.js'; import { CancellationToken } from '../../../../base/common/cancellation.js'; import { Color } from '../../../../base/common/color.js'; import { onUnexpectedError } from '../../../../base/common/errors.js'; +import { HierarchicalKind } from '../../../../base/common/hierarchicalKind.js'; import { Lazy } from '../../../../base/common/lazy.js'; import { Disposable, MutableDisposable } from '../../../../base/common/lifecycle.js'; -import { ICodeEditor } from '../../../browser/editorBrowser.js'; -import { IPosition, Position } from '../../../common/core/position.js'; -import { IEditorContribution, ScrollType } from '../../../common/editorCommon.js'; -import { CodeActionTriggerType } from '../../../common/languages.js'; -import { IModelDeltaDecoration } from '../../../common/model.js'; -import { ModelDecorationOptions } from '../../../common/model/textModel.js'; -import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; -import { ApplyCodeActionReason, applyCodeAction } from './codeAction.js'; -import { CodeActionKeybindingResolver } from './codeActionKeybindingResolver.js'; -import { toMenuItems } from './codeActionMenu.js'; -import { LightBulbWidget } from './lightBulbWidget.js'; -import { MessageController } from '../../message/browser/messageController.js'; import { localize } from '../../../../nls.js'; import { IActionListDelegate } from '../../../../platform/actionWidget/browser/actionList.js'; import { IActionWidgetService } from '../../../../platform/actionWidget/browser/actionWidget.js'; @@ -36,10 +25,20 @@ import { IEditorProgressService } from '../../../../platform/progress/common/pro import { editorFindMatchHighlight, editorFindMatchHighlightBorder } from '../../../../platform/theme/common/colorRegistry.js'; import { isHighContrast } from '../../../../platform/theme/common/theme.js'; import { registerThemingParticipant } from '../../../../platform/theme/common/themeService.js'; +import { ICodeEditor } from '../../../browser/editorBrowser.js'; +import { IPosition, Position } from '../../../common/core/position.js'; +import { IEditorContribution, ScrollType } from '../../../common/editorCommon.js'; +import { CodeActionTriggerType } from '../../../common/languages.js'; +import { IModelDeltaDecoration } from '../../../common/model.js'; +import { ModelDecorationOptions } from '../../../common/model/textModel.js'; +import { ILanguageFeaturesService } from '../../../common/services/languageFeatures.js'; +import { MessageController } from '../../message/browser/messageController.js'; import { CodeActionAutoApply, CodeActionFilter, CodeActionItem, CodeActionKind, CodeActionSet, CodeActionTrigger, CodeActionTriggerSource } from '../common/types.js'; +import { ApplyCodeActionReason, applyCodeAction } from './codeAction.js'; +import { CodeActionKeybindingResolver } from './codeActionKeybindingResolver.js'; +import { toMenuItems } from './codeActionMenu.js'; import { CodeActionModel, CodeActionsState } from './codeActionModel.js'; -import { HierarchicalKind } from '../../../../base/common/hierarchicalKind.js'; -import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; +import { LightBulbWidget } from './lightBulbWidget.js'; interface IActionShowOptions { readonly includeDisabledActions?: boolean; @@ -79,13 +78,12 @@ export class CodeActionController extends Disposable implements IEditorContribut @IConfigurationService private readonly _configurationService: IConfigurationService, @IActionWidgetService private readonly _actionWidgetService: IActionWidgetService, @IInstantiationService private readonly _instantiationService: IInstantiationService, - @ITelemetryService private readonly _telemetryService: ITelemetryService, @IEditorProgressService private readonly _progressService: IEditorProgressService, ) { super(); this._editor = editor; - this._model = this._register(new CodeActionModel(this._editor, languageFeaturesService.codeActionProvider, markerService, contextKeyService, progressService, _configurationService, this._telemetryService)); + this._model = this._register(new CodeActionModel(this._editor, languageFeaturesService.codeActionProvider, markerService, contextKeyService, progressService, _configurationService)); this._register(this._model.onDidChangeState(newState => this.update(newState))); this._lightBulbWidget = new Lazy(() => { diff --git a/src/vs/editor/contrib/codeAction/browser/codeActionModel.ts b/src/vs/editor/contrib/codeAction/browser/codeActionModel.ts index 4922ba46c4aa..d5e249add65d 100644 --- a/src/vs/editor/contrib/codeAction/browser/codeActionModel.ts +++ b/src/vs/editor/contrib/codeAction/browser/codeActionModel.ts @@ -9,13 +9,11 @@ import { Emitter } from '../../../../base/common/event.js'; import { HierarchicalKind } from '../../../../base/common/hierarchicalKind.js'; import { Disposable, IDisposable, MutableDisposable } from '../../../../base/common/lifecycle.js'; import { isEqual } from '../../../../base/common/resources.js'; -import { StopWatch } from '../../../../base/common/stopwatch.js'; import { URI } from '../../../../base/common/uri.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { IContextKey, IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; import { IMarkerService } from '../../../../platform/markers/common/markers.js'; import { IEditorProgressService, Progress } from '../../../../platform/progress/common/progress.js'; -import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; import { ICodeEditor } from '../../../browser/editorBrowser.js'; import { EditorOption, ShowLightbulbIconMode } from '../../../common/config/editorOptions.js'; import { Position } from '../../../common/core/position.js'; @@ -176,7 +174,6 @@ export class CodeActionModel extends Disposable { contextKeyService: IContextKeyService, private readonly _progressService?: IEditorProgressService, private readonly _configurationService?: IConfigurationService, - private readonly _telemetryService?: ITelemetryService ) { super(); this._supportedCodeActions = SUPPORTED_CODE_ACTIONS.bindTo(contextKeyService); @@ -328,29 +325,7 @@ export class CodeActionModel extends Disposable { // Case for manual triggers - specifically Source Actions and Refactors if (trigger.trigger.type === CodeActionTriggerType.Invoke) { - const sw = new StopWatch(); const codeActions = await getCodeActions(this._registry, model, trigger.selection, trigger.trigger, Progress.None, token); - - // Telemetry for duration of each code action on save. - if (this._telemetryService) { - type RenderActionMenu = { - codeActions: number; - duration: number; - }; - - type RenderActionMenuClassification = { - owner: 'justschen'; - comment: 'Information about how long it took for code actions to be received from the provider and shown in the UI.'; - codeActions: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Number of valid code actions received from TS.' }; - duration: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Duration it took for TS to return the action to run for each kind. ' }; - }; - - this._telemetryService.publicLog2('codeAction.invokedDurations', { - codeActions: codeActions.validActions.length, - duration: sw.elapsed() - }); - } - return codeActions; } diff --git a/src/vs/editor/contrib/codelens/browser/codelensController.ts b/src/vs/editor/contrib/codelens/browser/codelensController.ts index 85cc79d7ca39..bd54868ea99f 100644 --- a/src/vs/editor/contrib/codelens/browser/codelensController.ts +++ b/src/vs/editor/contrib/codelens/browser/codelensController.ts @@ -465,7 +465,7 @@ registerEditorAction(class ShowLensesInCurrentLine extends EditorAction { super({ id: 'codelens.showLensesInCurrentLine', precondition: EditorContextKeys.hasCodeLensProvider, - label: localize2('showLensOnLine', "Show CodeLens Commands For Current Line"), + label: localize2('showLensOnLine', "Show CodeLens Commands for Current Line"), }); } diff --git a/src/vs/editor/contrib/colorPicker/browser/colorDetector.ts b/src/vs/editor/contrib/colorPicker/browser/colorDetector.ts index 3a00c1d8f96d..e6043ebbd332 100644 --- a/src/vs/editor/contrib/colorPicker/browser/colorDetector.ts +++ b/src/vs/editor/contrib/colorPicker/browser/colorDetector.ts @@ -15,7 +15,7 @@ import { DynamicCssRules } from '../../../browser/editorDom.js'; import { EditorOption } from '../../../common/config/editorOptions.js'; import { Position } from '../../../common/core/position.js'; import { Range } from '../../../common/core/range.js'; -import { IEditorContribution } from '../../../common/editorCommon.js'; +import { IEditorContribution, IEditorDecorationsCollection } from '../../../common/editorCommon.js'; import { IModelDecoration, IModelDeltaDecoration } from '../../../common/model.js'; import { ModelDecorationOptions } from '../../../common/model/textModel.js'; import { IFeatureDebounceInformation, ILanguageFeatureDebounceService } from '../../../common/services/languageFeatureDebounce.js'; @@ -40,12 +40,12 @@ export class ColorDetector extends Disposable implements IEditorContribution { private _decorationsIds: string[] = []; private _colorDatas = new Map(); - private readonly _colorDecoratorIds = this._editor.createDecorationsCollection(); + private readonly _colorDecoratorIds: IEditorDecorationsCollection; private _isColorDecoratorsEnabled: boolean; private _defaultColorDecoratorsEnablement: 'auto' | 'always' | 'never'; - private readonly _ruleFactory = new DynamicCssRules(this._editor); + private readonly _ruleFactory: DynamicCssRules; private readonly _decoratorLimitReporter = new DecoratorLimitReporter(); @@ -56,6 +56,8 @@ export class ColorDetector extends Disposable implements IEditorContribution { @ILanguageFeatureDebounceService languageFeatureDebounceService: ILanguageFeatureDebounceService, ) { super(); + this._colorDecoratorIds = this._editor.createDecorationsCollection(); + this._ruleFactory = new DynamicCssRules(this._editor); this._debounceInformation = languageFeatureDebounceService.for(_languageFeaturesService.colorProvider, 'Document Colors', { min: ColorDetector.RECOMPUTE_TIME }); this._register(_editor.onDidChangeModel(() => { this._isColorDecoratorsEnabled = this.isEnabled(); diff --git a/src/vs/editor/contrib/colorPicker/browser/colorPickerParts/colorPickerHeader.ts b/src/vs/editor/contrib/colorPicker/browser/colorPickerParts/colorPickerHeader.ts index 8522f41e361e..8d11bdf3ac76 100644 --- a/src/vs/editor/contrib/colorPicker/browser/colorPickerParts/colorPickerHeader.ts +++ b/src/vs/editor/contrib/colorPicker/browser/colorPickerParts/colorPickerHeader.ts @@ -43,8 +43,8 @@ export class ColorPickerHeader extends Disposable { this._originalColorNode.style.backgroundColor = Color.Format.CSS.format(this.model.originalColor) || ''; this.backgroundColor = themeService.getColorTheme().getColor(editorHoverBackground) || Color.white; - this._register(themeService.onDidColorThemeChange(theme => { - this.backgroundColor = theme.getColor(editorHoverBackground) || Color.white; + this._register(themeService.onDidColorThemeChange(e => { + this.backgroundColor = e.theme.getColor(editorHoverBackground) || Color.white; })); this._register(dom.addDisposableListener(this._pickedColorNode, dom.EventType.CLICK, () => this.model.selectNextColorPresentation())); diff --git a/src/vs/editor/contrib/colorPicker/browser/defaultDocumentColorProvider.ts b/src/vs/editor/contrib/colorPicker/browser/defaultDocumentColorProvider.ts index 63a231f23ea8..34fc72162b24 100644 --- a/src/vs/editor/contrib/colorPicker/browser/defaultDocumentColorProvider.ts +++ b/src/vs/editor/contrib/colorPicker/browser/defaultDocumentColorProvider.ts @@ -27,7 +27,7 @@ export class DefaultDocumentColorProvider implements DocumentColorProvider { const alpha = colorFromInfo.alpha; const color = new Color(new RGBA(Math.round(255 * colorFromInfo.red), Math.round(255 * colorFromInfo.green), Math.round(255 * colorFromInfo.blue), alpha)); - const rgb = alpha ? Color.Format.CSS.formatRGB(color) : Color.Format.CSS.formatRGBA(color); + const rgb = Color.Format.CSS.formatRGB(color); const hsl = alpha ? Color.Format.CSS.formatHSL(color) : Color.Format.CSS.formatHSLA(color); const hex = alpha ? Color.Format.CSS.formatHex(color) : Color.Format.CSS.formatHexA(color); diff --git a/src/vs/editor/contrib/find/browser/findController.ts b/src/vs/editor/contrib/find/browser/findController.ts index 6b6102ea5ac9..9e00a1d5d47a 100644 --- a/src/vs/editor/contrib/find/browser/findController.ts +++ b/src/vs/editor/contrib/find/browser/findController.ts @@ -584,7 +584,7 @@ export class StartFindWithArgsAction extends EditorAction { constructor() { super({ id: FIND_IDS.StartFindWithArgs, - label: nls.localize2('startFindWithArgsAction', "Find With Arguments"), + label: nls.localize2('startFindWithArgsAction', "Find with Arguments"), precondition: undefined, kbOpts: { kbExpr: null, @@ -633,7 +633,7 @@ export class StartFindWithSelectionAction extends EditorAction { constructor() { super({ id: FIND_IDS.StartFindWithSelection, - label: nls.localize2('startFindWithSelectionAction', "Find With Selection"), + label: nls.localize2('startFindWithSelectionAction', "Find with Selection"), precondition: undefined, kbOpts: { kbExpr: null, diff --git a/src/vs/editor/contrib/find/browser/findWidget.css b/src/vs/editor/contrib/find/browser/findWidget.css index 5d3ef3277d79..d34b3d89a582 100644 --- a/src/vs/editor/contrib/find/browser/findWidget.css +++ b/src/vs/editor/contrib/find/browser/findWidget.css @@ -47,7 +47,7 @@ /* This outline-color rule is used to override the outline color for synthetic-focus find input. */ .monaco-editor .find-widget .monaco-inputbox.synthetic-focus { - outline: 1px solid -webkit-focus-ring-color; + outline: 2px solid -webkit-focus-ring-color; outline-offset: -1px; outline-color: var(--vscode-focusBorder); } diff --git a/src/vs/editor/contrib/gotoError/browser/gotoErrorWidget.ts b/src/vs/editor/contrib/gotoError/browser/gotoErrorWidget.ts index 5a8bc4fb7ed7..d8828c069154 100644 --- a/src/vs/editor/contrib/gotoError/browser/gotoErrorWidget.ts +++ b/src/vs/editor/contrib/gotoError/browser/gotoErrorWidget.ts @@ -26,9 +26,9 @@ import { IInstantiationService } from '../../../../platform/instantiation/common import { ILabelService } from '../../../../platform/label/common/label.js'; import { IMarker, IRelatedInformation, MarkerSeverity } from '../../../../platform/markers/common/markers.js'; import { IOpenerService } from '../../../../platform/opener/common/opener.js'; -import { SeverityIcon } from '../../../../platform/severityIcon/browser/severityIcon.js'; +import { SeverityIcon } from '../../../../base/browser/ui/severityIcon/severityIcon.js'; import { contrastBorder, editorBackground, editorErrorBorder, editorErrorForeground, editorInfoBorder, editorInfoForeground, editorWarningBorder, editorWarningForeground, oneOf, registerColor, transparent } from '../../../../platform/theme/common/colorRegistry.js'; -import { IColorTheme, IThemeService } from '../../../../platform/theme/common/themeService.js'; +import { IColorTheme, IThemeChangeEvent, IThemeService } from '../../../../platform/theme/common/themeService.js'; class MessageWidget { @@ -255,11 +255,15 @@ export class MarkerNavigationWidget extends PeekViewWidget { this._backgroundColor = Color.white; this._applyTheme(_themeService.getColorTheme()); - this._callOnDispose.add(_themeService.onDidColorThemeChange(this._applyTheme.bind(this))); + this._callOnDispose.add(_themeService.onDidColorThemeChange(this._ondDidColorThemeChange.bind(this))); this.create(); } + private _ondDidColorThemeChange(e: IThemeChangeEvent) { + this._applyTheme(e.theme); + } + private _applyTheme(theme: IColorTheme) { this._backgroundColor = theme.getColor(editorMarkerNavigationBackground); let colorId = editorMarkerNavigationError; diff --git a/src/vs/editor/contrib/gotoSymbol/browser/peek/referencesWidget.ts b/src/vs/editor/contrib/gotoSymbol/browser/peek/referencesWidget.ts index 9581b5cf6ff6..6c7da6476db1 100644 --- a/src/vs/editor/contrib/gotoSymbol/browser/peek/referencesWidget.ts +++ b/src/vs/editor/contrib/gotoSymbol/browser/peek/referencesWidget.ts @@ -32,7 +32,7 @@ import { IInstantiationService } from '../../../../../platform/instantiation/com import { IKeybindingService } from '../../../../../platform/keybinding/common/keybinding.js'; import { ILabelService } from '../../../../../platform/label/common/label.js'; import { IWorkbenchAsyncDataTreeOptions, WorkbenchAsyncDataTree } from '../../../../../platform/list/browser/listService.js'; -import { IColorTheme, IThemeService } from '../../../../../platform/theme/common/themeService.js'; +import { IColorTheme, IThemeChangeEvent, IThemeService } from '../../../../../platform/theme/common/themeService.js'; import { FileReferences, OneReference, ReferencesModel } from '../referencesModel.js'; import { ITreeDragAndDrop, ITreeDragOverReaction } from '../../../../../base/browser/ui/tree/tree.js'; import { DataTransfers, IDragAndDropData } from '../../../../../base/browser/dnd.js'; @@ -276,7 +276,7 @@ export class ReferenceWidget extends peekView.PeekViewWidget { super(editor, { showFrame: false, showArrow: true, isResizeable: true, isAccessible: true, supportOnTitleClick: true }, _instantiationService); this._applyTheme(themeService.getColorTheme()); - this._callOnDispose.add(themeService.onDidColorThemeChange(this._applyTheme.bind(this))); + this._callOnDispose.add(themeService.onDidColorThemeChange(this._onDidColorThemeChange.bind(this))); this._peekViewService.addExclusiveWidget(editor, this); this.create(); } @@ -298,6 +298,10 @@ export class ReferenceWidget extends peekView.PeekViewWidget { super.dispose(); } + private _onDidColorThemeChange(e: IThemeChangeEvent): void { + this._applyTheme(e.theme); + } + private _applyTheme(theme: IColorTheme) { const borderColor = theme.getColor(peekView.peekViewBorder) || Color.transparent; this.style({ diff --git a/src/vs/editor/contrib/hover/browser/contentHoverWidget.ts b/src/vs/editor/contrib/hover/browser/contentHoverWidget.ts index 1b30ad9357a7..f39b90341602 100644 --- a/src/vs/editor/contrib/hover/browser/contentHoverWidget.ts +++ b/src/vs/editor/contrib/hover/browser/contentHoverWidget.ts @@ -116,16 +116,19 @@ export class ContentHoverWidget extends ResizableContentWidget { private _setContentsDomNodeDimensions(width: number | string, height: number | string): void { const contentsDomNode = this._hover.contentsDomNode; + console.log('contentsDomNode : ', contentsDomNode); return ContentHoverWidget._applyDimensions(contentsDomNode, width, height); } private _setContainerDomNodeDimensions(width: number | string, height: number | string): void { const containerDomNode = this._hover.containerDomNode; + console.log('containerDomNode : ', containerDomNode); return ContentHoverWidget._applyDimensions(containerDomNode, width, height); } private _setScrollableElementDimensions(width: number | string, height: number | string): void { const scrollbarDomElement = this._hover.scrollbar.getDomNode(); + console.log('scrollbarDomElement : ', scrollbarDomElement); return ContentHoverWidget._applyDimensions(scrollbarDomElement, width, height); } @@ -147,7 +150,6 @@ export class ContentHoverWidget extends ResizableContentWidget { ContentHoverWidget._applyMaxDimensions(this._hover.contentsDomNode, width, height); ContentHoverWidget._applyMaxDimensions(this._hover.scrollbar.getDomNode(), width, height); ContentHoverWidget._applyMaxDimensions(this._hover.containerDomNode, width, height); - this._hover.containerDomNode.style.setProperty('--vscode-hover-maxWidth', typeof width === 'number' ? `${width}px` : width); this._layoutContentWidget(); } @@ -290,9 +292,7 @@ export class ContentHoverWidget extends ResizableContentWidget { private _updateContent(node: DocumentFragment): void { const contentsDomNode = this._hover.contentsDomNode; - contentsDomNode.style.paddingBottom = ''; - contentsDomNode.textContent = ''; - contentsDomNode.appendChild(node); + contentsDomNode.replaceChildren(node); } private _layoutContentWidget(): void { @@ -373,11 +373,19 @@ export class ContentHoverWidget extends ResizableContentWidget { } private _removeConstraintsRenderNormally(): void { + console.log('_removeConstraintsRenderNormally'); // Added because otherwise the initial size of the hover content is smaller than should be const layoutInfo = this._editor.getLayoutInfo(); this._resizableNode.layout(layoutInfo.height, layoutInfo.width); this._setHoverWidgetDimensions('auto', 'auto'); - this._updateMaxDimensions(); + + console.log('ContentHoverWidget._lastDimensions : ', ContentHoverWidget._lastDimensions); + const height = Math.max(this._editor.getLayoutInfo().height / 4, 250, ContentHoverWidget._lastDimensions.height); + const width = Math.max(this._editor.getLayoutInfo().width * 0.66, 750, ContentHoverWidget._lastDimensions.width); + console.log('max width : ', width); + console.log('max height : ', height); + this._resizableNode.maxSize = new dom.Dimension(width, height); + this._setHoverWidgetMaxDimensions(width, height); } public setMinimumDimensions(dimensions: dom.Dimension): void { @@ -399,21 +407,38 @@ export class ContentHoverWidget extends ResizableContentWidget { this._resizableNode.minSize = new dom.Dimension(width, this._minimumSize.height); } - public onContentsChanged(): void { - this._removeConstraintsRenderNormally(); + public onContentsChanged(opts: IContentsChangeOptions = { allowPositionPreferenceRecomputation: true }): void { + console.log('onContentsChanged'); + const contentsDomNode = this._hover.contentsDomNode; let height = dom.getTotalHeight(contentsDomNode); - let width = dom.getTotalWidth(contentsDomNode) + 2; + let width = dom.getTotalWidth(contentsDomNode); + console.log('height : ', height); + console.log('width : ', width); + + this._removeConstraintsRenderNormally(); + + height = dom.getTotalHeight(contentsDomNode); + width = dom.getTotalWidth(contentsDomNode); + console.log('height : ', height); + console.log('width : ', width); + this._resizableNode.layout(height, width); this._setHoverWidgetDimensions(width, height); height = dom.getTotalHeight(contentsDomNode); width = dom.getTotalWidth(contentsDomNode); + console.log('height : ', height); + console.log('width : ', width); + this._contentWidth = width; - this._updateMinimumWidth(); this._resizableNode.layout(height, width); + this._setHoverWidgetDimensions(width, height); + + this._updateMinimumWidth(); + this._updateResizableNodeMaxDimensions(); if (this._renderedHover?.showAtPosition) { const widgetHeight = dom.getTotalHeight(this._hover.containerDomNode); diff --git a/src/vs/editor/contrib/hover/browser/markdownHoverParticipant.ts b/src/vs/editor/contrib/hover/browser/markdownHoverParticipant.ts index bc663ef46bb6..03b585a99b10 100644 --- a/src/vs/editor/contrib/hover/browser/markdownHoverParticipant.ts +++ b/src/vs/editor/contrib/hover/browser/markdownHoverParticipant.ts @@ -327,7 +327,7 @@ class MarkdownRenderedHoverParts implements IRenderedHoverParts { const isActionIncrease = action === HoverVerbosityAction.Increase; const actionElement = dom.append(container, $(ThemeIcon.asCSSSelector(isActionIncrease ? increaseHoverVerbosityIcon : decreaseHoverVerbosityIcon))); actionElement.tabIndex = 0; - const hoverDelegate = new WorkbenchHoverDelegate('mouse', false, { target: container, position: { hoverPosition: HoverPosition.LEFT } }, this._configurationService, this._hoverService); + const hoverDelegate = new WorkbenchHoverDelegate('mouse', undefined, { target: container, position: { hoverPosition: HoverPosition.LEFT } }, this._configurationService, this._hoverService); store.add(this._hoverService.setupManagedHover(hoverDelegate, actionElement, labelForHoverVerbosityAction(this._keybindingService, action))); if (!actionEnabled) { actionElement.classList.add('disabled'); diff --git a/src/vs/editor/contrib/inlineCompletions/browser/controller/commands.ts b/src/vs/editor/contrib/inlineCompletions/browser/controller/commands.ts index 501d1fcb6f14..cb1b72e21253 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/controller/commands.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/controller/commands.ts @@ -261,6 +261,25 @@ export class JumpToNextInlineEdit extends EditorAction { } } +export class AcceptNextInlineEditPart extends EditorAction { + constructor() { + super({ + id: 'editor.action.inlineSuggest.acceptNextInlineEditPart', + label: nls.localize2('action.inlineSuggest.acceptNextInlineEditPart', "Accept Next Inline Edit Part"), + precondition: ContextKeyExpr.and(EditorContextKeys.writable, InlineCompletionContextKeys.inlineEditVisible), + kbOpts: { + weight: KeybindingWeight.EditorContrib + 1, + kbExpr: ContextKeyExpr.and(EditorContextKeys.writable, InlineCompletionContextKeys.inlineEditVisible), + }, + }); + } + + public async run(accessor: ServicesAccessor | undefined, editor: ICodeEditor): Promise { + const controller = InlineCompletionsController.get(editor); + await controller?.model.get()?.acceptNextInlineEditPart(controller.editor); + } +} + export class HideInlineCompletion extends EditorAction { public static ID = hideInlineCompletionId; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.ts b/src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.ts index bc5d723722fc..1658944cc6d5 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.ts @@ -243,7 +243,7 @@ export class InlineCompletionsController extends Disposable { const currentInlineCompletionBySemanticId = derivedObservableWithCache(this, (reader, last) => { const model = this.model.read(reader); - const state = model?.inlineCompletionState.read(reader); + const state = model?.state.read(reader); if (this._suggestWidgetAdapter.selectedItem.get()) { return last; } @@ -256,16 +256,20 @@ export class InlineCompletionsController extends Disposable { }), async (_value, _, _deltas, store) => { /** @description InlineCompletionsController.playAccessibilitySignalAndReadSuggestion */ const model = this.model.get(); - const state = model?.inlineCompletionState.get(); + const state = model?.state.get(); if (!state || !model) { return; } - const lineText = model.textModel.getLineContent(state.primaryGhostText.lineNumber); + const lineText = state.kind === 'ghostText' ? model.textModel.getLineContent(state.primaryGhostText.lineNumber) : ''; await timeout(50, cancelOnDispose(store)); await waitForState(this._suggestWidgetAdapter.selectedItem, isUndefined, () => false, cancelOnDispose(store)); await this._accessibilitySignalService.playSignal(AccessibilitySignal.inlineSuggestion); if (this.editor.getOption(EditorOption.screenReaderAnnounceInlineSuggestion)) { - this._provideScreenReaderUpdate(state.primaryGhostText.renderForScreenReader(lineText)); + if (state.kind === 'ghostText') { + this._provideScreenReaderUpdate(state.primaryGhostText.renderForScreenReader(lineText)); + } else { + this._provideScreenReaderUpdate(''); // Only announce Alt+F2 + } } })); diff --git a/src/vs/editor/contrib/inlineCompletions/browser/hintsWidget/inlineCompletionsHintsWidget.ts b/src/vs/editor/contrib/inlineCompletions/browser/hintsWidget/inlineCompletionsHintsWidget.ts index 084715904702..d5b54d82af8b 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/hintsWidget/inlineCompletionsHintsWidget.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/hintsWidget/inlineCompletionsHintsWidget.ts @@ -172,9 +172,9 @@ export class InlineSuggestionHintsContentWidget extends Disposable implements IC return action; } - private readonly previousAction = this.createCommandAction(showPreviousInlineSuggestionActionId, localize('previous', 'Previous'), ThemeIcon.asClassName(inlineSuggestionHintsPreviousIcon)); - private readonly availableSuggestionCountAction = new Action('inlineSuggestionHints.availableSuggestionCount', '', undefined, false); - private readonly nextAction = this.createCommandAction(showNextInlineSuggestionActionId, localize('next', 'Next'), ThemeIcon.asClassName(inlineSuggestionHintsNextIcon)); + private readonly previousAction = this._register(this.createCommandAction(showPreviousInlineSuggestionActionId, localize('previous', 'Previous'), ThemeIcon.asClassName(inlineSuggestionHintsPreviousIcon))); + private readonly availableSuggestionCountAction = this._register(new Action('inlineSuggestionHints.availableSuggestionCount', '', undefined, false)); + private readonly nextAction = this._register(this.createCommandAction(showNextInlineSuggestionActionId, localize('next', 'Next'), ThemeIcon.asClassName(inlineSuggestionHintsNextIcon))); private readonly toolBar: CustomizedMenuWorkbenchToolBar; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletions.contribution.ts b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletions.contribution.ts index 06a0b9d92041..5bf486b87759 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletions.contribution.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletions.contribution.ts @@ -8,7 +8,7 @@ import { registerAction2 } from '../../../../platform/actions/common/actions.js' import { wrapInHotClass1 } from '../../../../platform/observable/common/wrapInHotClass.js'; import { EditorContributionInstantiation, registerEditorAction, registerEditorCommand, registerEditorContribution } from '../../../browser/editorExtensions.js'; import { HoverParticipantRegistry } from '../../hover/browser/hoverTypes.js'; -import { AcceptInlineCompletion, AcceptNextLineOfInlineCompletion, AcceptNextWordOfInlineCompletion, DevExtractReproSample, HideInlineCompletion, JumpToNextInlineEdit, ShowNextInlineSuggestionAction, ShowPreviousInlineSuggestionAction, ToggleAlwaysShowInlineSuggestionToolbar, ExplicitTriggerInlineEditAction, TriggerInlineSuggestionAction, TriggerInlineEditAction, ToggleInlineCompletionShowCollapsed } from './controller/commands.js'; +import { AcceptInlineCompletion, AcceptNextLineOfInlineCompletion, AcceptNextWordOfInlineCompletion, DevExtractReproSample, HideInlineCompletion, JumpToNextInlineEdit, ShowNextInlineSuggestionAction, ShowPreviousInlineSuggestionAction, ToggleAlwaysShowInlineSuggestionToolbar, ExplicitTriggerInlineEditAction, TriggerInlineSuggestionAction, TriggerInlineEditAction, ToggleInlineCompletionShowCollapsed, AcceptNextInlineEditPart } from './controller/commands.js'; import { InlineCompletionsController } from './controller/inlineCompletionsController.js'; import { InlineCompletionsHoverParticipant } from './hintsWidget/hoverParticipant.js'; import { InlineCompletionsAccessibleView } from './inlineCompletionsAccessibleView.js'; @@ -29,6 +29,7 @@ registerEditorAction(AcceptNextLineOfInlineCompletion); registerEditorAction(AcceptInlineCompletion); registerEditorAction(ToggleInlineCompletionShowCollapsed); registerEditorAction(HideInlineCompletion); +registerEditorAction(AcceptNextInlineEditPart); registerEditorAction(JumpToNextInlineEdit); registerAction2(ToggleAlwaysShowInlineSuggestionToolbar); registerEditorAction(DevExtractReproSample); diff --git a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsAccessibleView.ts b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsAccessibleView.ts index 0a4e864d2c67..959a99ea366c 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsAccessibleView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/inlineCompletionsAccessibleView.ts @@ -14,12 +14,16 @@ import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextke import { ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; import { Disposable } from '../../../../base/common/lifecycle.js'; import { InlineCompletionsModel } from './model/inlineCompletionsModel.js'; +import { TextEdit } from '../../../common/core/textEdit.js'; +import { LineEdit } from '../../../common/core/lineEdit.js'; +import { TextModelText } from '../../../common/model/textModelText.js'; +import { localize } from '../../../../nls.js'; export class InlineCompletionsAccessibleView implements IAccessibleViewImplementation { readonly type = AccessibleViewType.View; readonly priority = 95; readonly name = 'inline-completions'; - readonly when = ContextKeyExpr.and(InlineCompletionContextKeys.inlineSuggestionVisible); + readonly when = ContextKeyExpr.or(InlineCompletionContextKeys.inlineSuggestionVisible, InlineCompletionContextKeys.inlineEditVisible); getProvider(accessor: ServicesAccessor) { const codeEditorService = accessor.get(ICodeEditorService); const editor = codeEditorService.getActiveCodeEditor() || codeEditorService.getFocusedCodeEditor(); @@ -28,7 +32,7 @@ export class InlineCompletionsAccessibleView implements IAccessibleViewImplement } const model = InlineCompletionsController.get(editor)?.model.get(); - if (!model?.inlineCompletionState.get()) { + if (!model?.state.get()) { return; } @@ -51,16 +55,23 @@ class InlineCompletionsAccessibleViewContentProvider extends Disposable implemen public readonly options = { language: this._editor.getModel()?.getLanguageId() ?? undefined, type: AccessibleViewType.View }; public provideContent(): string { - const state = this._model.inlineCompletionState.get(); + const state = this._model.state.get(); if (!state) { throw new Error('Inline completion is visible but state is not available'); } - const lineText = this._model.textModel.getLineContent(state.primaryGhostText.lineNumber); - const ghostText = state.primaryGhostText.renderForScreenReader(lineText); - if (!ghostText) { - throw new Error('Inline completion is visible but ghost text is not available'); + if (state.kind === 'ghostText') { + + const lineText = this._model.textModel.getLineContent(state.primaryGhostText.lineNumber); + const ghostText = state.primaryGhostText.renderForScreenReader(lineText); + if (!ghostText) { + throw new Error('Inline completion is visible but ghost text is not available'); + } + return lineText + ghostText; + } else { + const text = new TextModelText(this._model.textModel); + const lineEdit = LineEdit.fromTextEdit(new TextEdit(state.edits), text); + return localize('inlineEditAvailable', 'There is an inline edit available:') + '\n' + lineEdit.humanReadablePatch(text.getLines()); } - return lineText + ghostText; } public provideNextContent(): string | undefined { // asynchronously update the model and fire the event diff --git a/src/vs/editor/contrib/inlineCompletions/browser/model/changeRecorder.ts b/src/vs/editor/contrib/inlineCompletions/browser/model/changeRecorder.ts index 2e91840d292b..91d80882aaf5 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/model/changeRecorder.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/model/changeRecorder.ts @@ -8,7 +8,7 @@ import { autorunWithStore } from '../../../../../base/common/observable.js'; import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; import { ICodeEditor } from '../../../../browser/editorBrowser.js'; import { CodeEditorWidget } from '../../../../browser/widget/codeEditor/codeEditorWidget.js'; -import { IRecordableEditorLogEntry, StructuredLogger } from './inlineCompletionsSource.js'; +import { IRecordableEditorLogEntry, StructuredLogger } from '../structuredLogger.js'; export class TextModelChangeRecorder extends Disposable { private readonly _structuredLogger = this._register(this._instantiationService.createInstance(StructuredLogger.cast(), diff --git a/src/vs/editor/contrib/inlineCompletions/browser/model/ghostText.ts b/src/vs/editor/contrib/inlineCompletions/browser/model/ghostText.ts index ac3939847e1f..700bc76bf640 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/model/ghostText.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/model/ghostText.ts @@ -47,7 +47,7 @@ export class GhostText { const text = new TextEdit([ ...this.parts.map(p => new SingleTextEdit( Range.fromPositions(new Position(1, p.column)), - p.lines.join('\n') + p.lines.map(line => line.line).join('\n') )), ]).applyToString(cappedLineText); @@ -68,7 +68,11 @@ export interface IGhostTextLine { lineDecorations: LineDecoration[]; } + export class GhostTextPart { + + readonly lines: IGhostTextLine[]; + constructor( readonly column: number, readonly text: string, @@ -78,13 +82,12 @@ export class GhostTextPart { readonly preview: boolean, private _inlineDecorations: InlineDecoration[] = [], ) { + this.lines = splitLines(this.text).map((line, i) => ({ + line, + lineDecorations: LineDecoration.filter(this._inlineDecorations, i + 1, 1, line.length + 1) + })); } - readonly lines: IGhostTextLine[] = splitLines(this.text).map((line, i) => ({ - line, - lineDecorations: LineDecoration.filter(this._inlineDecorations, i + 1, 1, line.length + 1) - })); - equals(other: GhostTextPart): boolean { return this.column === other.column && this.lines.length === other.lines.length && @@ -96,22 +99,24 @@ export class GhostTextPart { } export class GhostTextReplacement { - public readonly parts: ReadonlyArray = [ - new GhostTextPart( - this.columnRange.endColumnExclusive, - this.text, - false - ), - ]; + public readonly parts: ReadonlyArray; + readonly newLines: string[]; constructor( readonly lineNumber: number, readonly columnRange: ColumnRange, readonly text: string, public readonly additionalReservedLineCount: number = 0, - ) { } - - readonly newLines = splitLines(this.text); + ) { + this.parts = [ + new GhostTextPart( + this.columnRange.endColumnExclusive, + this.text, + false + ), + ]; + this.newLines = splitLines(this.text); + } renderForScreenReader(_lineText: string): string { return this.newLines.join('\n'); diff --git a/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts b/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts index 5bb02e8c8f46..73bfd7977bf9 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts @@ -32,7 +32,7 @@ import { TextModelText } from '../../../../common/model/textModelText.js'; import { IFeatureDebounceInformation } from '../../../../common/services/languageFeatureDebounce.js'; import { IModelContentChangedEvent } from '../../../../common/textModelEvents.js'; import { SnippetController2 } from '../../../snippet/browser/snippetController2.js'; -import { addPositions, getEndPositionsAfterApplying, substringPos, subtractPositions } from '../utils.js'; +import { addPositions, getEndPositionsAfterApplying, getModifiedRangesAfterApplying, substringPos, subtractPositions } from '../utils.js'; import { AnimatedValue, easeOutCubic, ObservableAnimatedValue } from './animation.js'; import { computeGhostText } from './computeGhostText.js'; import { GhostText, GhostTextOrReplacement, ghostTextOrReplacementEquals, ghostTextsOrReplacementsEqual } from './ghostText.js'; @@ -62,7 +62,7 @@ export class InlineCompletionsModel extends Disposable { private readonly _suggestPreviewMode = this._editorObs.getOption(EditorOption.suggest).map(v => v.previewMode); private readonly _inlineSuggestMode = this._editorObs.getOption(EditorOption.inlineSuggest).map(v => v.mode); private readonly _inlineEditsEnabled = this._editorObs.getOption(EditorOption.inlineSuggest).map(v => !!v.edits.enabled); - private readonly _inlineEditsShowCollapsed = this._editorObs.getOption(EditorOption.inlineSuggest).map(s => s.edits.showCollapsed); + private readonly _inlineEditsShowCollapsedEnabled = this._editorObs.getOption(EditorOption.inlineSuggest).map(s => s.edits.showCollapsed); constructor( public readonly textModel: ITextModel, @@ -269,6 +269,7 @@ export class InlineCompletionsModel extends Disposable { } } + this._inAcceptPartialFlow.set(false, tx); this._isActive.set(false, tx); this._source.clear(tx); }); @@ -390,8 +391,7 @@ export class InlineCompletionsModel extends Disposable { } const commands = inlineEditResult.inlineCompletion.source.inlineCompletions.commands; - const renderExplicitly = this._jumpedTo.read(reader); - const inlineEdit = new InlineEdit(edit, renderExplicitly, commands ?? [], inlineEditResult.inlineCompletion); + const inlineEdit = new InlineEdit(edit, commands ?? [], inlineEditResult.inlineCompletion); const edits = inlineEditResult.updatedEdit.read(reader); const e = edits ? TextEdit.fromOffsetEdit(edits, new TextModelText(this.textModel)).edits : [edit]; @@ -509,6 +509,18 @@ export class InlineCompletionsModel extends Disposable { return v?.primaryGhostText; }); + public readonly showCollapsed = derived(this, reader => { + const state = this.state.read(reader); + if (!state || state.kind !== 'inlineEdit') { + return false; + } + + const isCurrentModelVersion = state.inlineCompletion.updatedEditModelVersion === this._textModelVersionId.read(reader); // TODO: this is a hack + return (this._inlineEditsShowCollapsedEnabled.read(reader) || !isCurrentModelVersion) + && !this._jumpedTo.read(reader) + && !this._inAcceptFlow.read(reader); + }); + private readonly _tabShouldIndent = derived(this, reader => { if (this._inAcceptFlow.read(reader)) { return false; @@ -545,8 +557,8 @@ export class InlineCompletionsModel extends Disposable { return false; } - if (this._inlineEditsShowCollapsed.read(reader)) { - return !this._jumpedTo.read(reader); + if (this.showCollapsed.read(reader)) { + return true; } return !s.cursorAtInlineEdit; @@ -557,8 +569,8 @@ export class InlineCompletionsModel extends Disposable { if (!s) { return false; } - if (this._inlineEditsShowCollapsed.read(reader)) { - return this._jumpedTo.read(reader); + if (this.showCollapsed.read(reader)) { + return false; } if (s.inlineEdit.range.startLineNumber === this._editorObs.cursorLineNumber.read(reader)) { return true; @@ -594,6 +606,12 @@ export class InlineCompletionsModel extends Disposable { throw new BugIndicatingError(); } + if (this._inAcceptPartialFlow.get()) { + this._inAcceptPartialFlow.set(false, undefined); + this.jump(); + return; + } + let completionWithUpdatedRange: InlineCompletionWithUpdatedRange; const state = this.state.get(); @@ -762,6 +780,68 @@ export class InlineCompletionsModel extends Disposable { } } + // TODO: clean this up if we keep it + private readonly _inAcceptPartialFlow = observableValue(this, false); + public readonly inPartialAcceptFlow: IObservable = this._inAcceptPartialFlow; + public async acceptNextInlineEditPart(editor: ICodeEditor): Promise { + if (editor.getModel() !== this.textModel) { + throw new BugIndicatingError(); + } + + const state = this.inlineEditState.get(); + const updatedEdit = state?.inlineCompletion.updatedEdit.get(); + const completion = state?.inlineCompletion.toInlineCompletion(undefined); + if (!updatedEdit || updatedEdit.isEmpty || !completion) { + return; + } + + const nextPart = updatedEdit.edits[0]; + + const edit = new SingleTextEdit(Range.fromPositions( + this.textModel.getPositionAt(nextPart.replaceRange.start), + this.textModel.getPositionAt(nextPart.replaceRange.endExclusive) + ), nextPart.newText); + + const cursorAtStartPosition = this._editor.getSelection()?.getStartPosition().equals(edit.range.getStartPosition()); + if (!cursorAtStartPosition || !this._inAcceptPartialFlow.get()) { + this._inAcceptPartialFlow.set(true, undefined); + this.jump(); + return; + } + + const partToJumpToNext = updatedEdit.edits[1] ?? undefined; + const editToJumpToNext = partToJumpToNext ? new SingleTextEdit(Range.fromPositions( + this.textModel.getPositionAt(partToJumpToNext.replaceRange.start), + this.textModel.getPositionAt(partToJumpToNext.replaceRange.endExclusive) + ), partToJumpToNext.newText) : undefined; + + // Executing the edit might free the completion, so we have to hold a reference on it. + completion.source.addRef(); + try { + this._isAcceptingPartially = true; + try { + editor.pushUndoStop(); + + let selections; + if (editToJumpToNext) { + const [_, rangeOfEditToJumpTo] = getModifiedRangesAfterApplying([edit, editToJumpToNext]); + selections = [Selection.fromPositions(rangeOfEditToJumpTo.getStartPosition())]; + } else { + selections = getEndPositionsAfterApplying([edit]).map(p => Selection.fromPositions(p)); + } + + const edits = [edit]; + editor.executeEdits('inlineSuggestion.accept', edits.map(edit => EditOperation.replace(edit.range, edit.text))); + editor.setSelections(selections, 'inlineCompletionPartialAccept'); + editor.revealPositionInCenterIfOutsideViewport(editor.getPosition()!, ScrollType.Immediate); + } finally { + this._isAcceptingPartially = false; + } + } finally { + completion.source.removeRef(); + } + } + public handleSuggestAccepted(item: SuggestItemInfo) { const itemEdit = singleTextRemoveCommonPrefix(item.toSingleTextEdit(), this.textModel); const augmentedCompletion = this._computeAugmentation(itemEdit, undefined); @@ -822,15 +902,17 @@ export class InlineCompletionsModel extends Disposable { }); } - public async handleInlineCompletionShown(inlineCompletion: InlineCompletionItem): Promise { - if (!inlineCompletion.shownCommand) { - return; - } + public async handleInlineEditShown(inlineCompletion: InlineCompletionItem): Promise { if (inlineCompletion.didShow) { return; } inlineCompletion.markAsShown(); - await this._commandService.executeCommand(inlineCompletion.shownCommand.id, ...(inlineCompletion.shownCommand.arguments || [])); + + inlineCompletion.source.provider.handleItemDidShow?.(inlineCompletion.source.inlineCompletions, inlineCompletion.sourceInlineCompletion, inlineCompletion.insertText); + + if (inlineCompletion.shownCommand) { + await this._commandService.executeCommand(inlineCompletion.shownCommand.id, ...(inlineCompletion.shownCommand.arguments || [])); + } } } diff --git a/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsSource.ts b/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsSource.ts index d37c28d4cf88..141271269958 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsSource.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsSource.ts @@ -10,11 +10,9 @@ import { equalsIfDefined, itemEquals } from '../../../../../base/common/equals.j import { BugIndicatingError } from '../../../../../base/common/errors.js'; import { matchesSubString } from '../../../../../base/common/filters.js'; import { Disposable, IDisposable, MutableDisposable } from '../../../../../base/common/lifecycle.js'; -import { IObservable, IObservableWithChange, IReader, ITransaction, derived, derivedHandleChanges, disposableObservableValue, observableFromEvent, observableValue, transaction } from '../../../../../base/common/observable.js'; +import { IObservable, IObservableWithChange, IReader, ITransaction, derived, derivedHandleChanges, disposableObservableValue, observableValue, transaction } from '../../../../../base/common/observable.js'; import { commonPrefixLength, commonSuffixLength, splitLines } from '../../../../../base/common/strings.js'; -import { ICommandService } from '../../../../../platform/commands/common/commands.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; -import { IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js'; import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; import { ILogService } from '../../../../../platform/log/common/log.js'; import { observableConfigValue } from '../../../../../platform/observable/common/platformObservableUtils.js'; @@ -34,6 +32,7 @@ import { ILanguageFeaturesService } from '../../../../common/services/languageFe import { IModelContentChangedEvent } from '../../../../common/textModelEvents.js'; import { InlineCompletionItem, InlineCompletionProviderResult, provideInlineCompletions } from './provideInlineCompletions.js'; import { singleTextRemoveCommonPrefix } from './singleTextEditHelpers.js'; +import { StructuredLogger, IRecordableEditorLogEntry, IRecordableLogEntry, formatRecordableLogEntry } from '../structuredLogger.js'; export class InlineCompletionsSource extends Disposable { private static _requestId = 0; @@ -145,7 +144,7 @@ export class InlineCompletionsSource extends Disposable { const result = updatedCompletions?.completions.map(c => ({ range: c.range.toString(), text: c.insertText, - isInlineEdit: !!c.sourceInlineCompletion.isInlineEdit, + isInlineEdit: !!c.isInlineEdit, source: c.source.provider.groupId, })); this._log({ sourceId: 'InlineCompletions.fetch', kind: 'end', requestId, durationMs: (Date.now() - startTime.getTime()), error, result, time: Date.now() }); @@ -318,25 +317,13 @@ export class InlineCompletionWithUpdatedRange extends Disposable { return this.source.inlineCompletions.enableForwardStability ?? false; } - private _updatedEdit: UpdatedEdit; // helper as derivedHandleChanges can not access previous value - - public get updatedEdit(): IObservable { return this._updatedEdit.offsetEdit; } - - private readonly _updatedRange = derived(reader => { - const edit = this.updatedEdit.read(reader); - if (!edit || edit.edits.length === 0) { - return undefined; - } - - return Range.fromPositions( - this._textModel.getPositionAt(edit.edits[0].replaceRange.start), - this._textModel.getPositionAt(edit.edits[edit.edits.length - 1].replaceRange.endExclusive) - ); - }); + private readonly _updatedEditObj: UpdatedEdit; // helper as derivedHandleChanges can not access previous value + public get updatedEdit(): IObservable { return this._updatedEditObj.offsetEdit; } + public get updatedEditModelVersion() { return this._updatedEditObj.modelVersion; } public get source() { return this.inlineCompletion.source; } public get sourceInlineCompletion() { return this.inlineCompletion.sourceInlineCompletion; } - public get isInlineEdit() { return this.inlineCompletion.sourceInlineCompletion.isInlineEdit; } + public get isInlineEdit() { return this.inlineCompletion.isInlineEdit; } constructor( public readonly inlineCompletion: InlineCompletionItem, @@ -347,68 +334,7 @@ export class InlineCompletionWithUpdatedRange extends Disposable { ) { super(); - this._updatedEdit = this._register(this._toUpdatedEdit(updatedRange ?? this.inlineCompletion.range, this.inlineCompletion.insertText)); - } - - private _toInlineCompletionEdit(editRange: Range, replaceText: string): UpdatedEdit { - const startOffset = this._textModel.getOffsetAt(editRange.getStartPosition()); - const endOffset = this._textModel.getOffsetAt(editRange.getEndPosition()); - const originalRange = OffsetRange.ofStartAndLength(startOffset, endOffset - startOffset); - - const offsetEdit = new OffsetEdit([new SingleOffsetEdit(originalRange, replaceText)]); - - return new UpdatedEdit(offsetEdit, this._textModel, this._modelVersion, false); - } - - private _toUpdatedEdit(editRange: Range, replaceText: string): UpdatedEdit { - if (!this.isInlineEdit) { - return this._toInlineCompletionEdit(editRange, replaceText); - } - - const eol = this._textModel.getEOL(); - const editOriginalText = this._textModel.getValueInRange(editRange); - const editReplaceText = replaceText.replace(/\r\n|\r|\n/g, eol); - - const diffAlgorithm = linesDiffComputers.getDefault(); - const lineDiffs = diffAlgorithm.computeDiff( - splitLines(editOriginalText), - splitLines(editReplaceText), - { - ignoreTrimWhitespace: false, - computeMoves: false, - extendToSubwords: true, - maxComputationTimeMs: 500, - } - ); - - const innerChanges = lineDiffs.changes.flatMap(c => c.innerChanges ?? []); - - function addRangeToPos(pos: Position, range: Range): Range { - const start = TextLength.fromPosition(range.getStartPosition()); - return TextLength.ofRange(range).createRange(start.addToPosition(pos)); - } - - const modifiedText = new StringText(editReplaceText); - - const offsetEdit = new OffsetEdit( - innerChanges.map(c => { - const range = addRangeToPos(editRange.getStartPosition(), c.originalRange); - const startOffset = this._textModel.getOffsetAt(range.getStartPosition()); - const endOffset = this._textModel.getOffsetAt(range.getEndPosition()); - const originalRange = OffsetRange.ofStartAndLength(startOffset, endOffset - startOffset); - - // TODO: EOL are not properly trimmed by the diffAlgorithm #12680 - const replaceText = modifiedText.getValueOfRange(c.modifiedRange); - const oldText = this._textModel.getValueInRange(range); - if (replaceText.endsWith(eol) && oldText.endsWith(eol)) { - return new SingleOffsetEdit(originalRange.deltaEnd(-eol.length), replaceText.slice(0, -eol.length)); - } - - return new SingleOffsetEdit(originalRange, replaceText); - }) - ); - - return new UpdatedEdit(offsetEdit, this._textModel, this._modelVersion, true); + this._updatedEditObj = this._register(this._toUpdatedEdit(updatedRange ?? this.inlineCompletion.range, this.inlineCompletion.insertText)); } public toInlineCompletion(reader: IReader | undefined): InlineCompletionItem { @@ -441,7 +367,7 @@ export class InlineCompletionWithUpdatedRange extends Disposable { } public isVisible(model: ITextModel, cursorPosition: Position, reader: IReader | undefined): boolean { - const minimizedReplacement = singleTextRemoveCommonPrefix(this._toFilterTextReplacement(reader), model); + const minimizedReplacement = singleTextRemoveCommonPrefix(this.toSingleTextEdit(reader), model); const updatedRange = this._updatedRange.read(reader); if ( !updatedRange @@ -487,7 +413,7 @@ export class InlineCompletionWithUpdatedRange extends Disposable { } if (this.sourceInlineCompletion.isInlineEdit) { - return this._updatedEdit.lastChangePartOfInlineEdit; + return this._updatedEditObj.lastChangePartOfInlineEdit; } const updatedRange = this._updatedRange.read(undefined); @@ -498,16 +424,83 @@ export class InlineCompletionWithUpdatedRange extends Disposable { return result; } - private _toFilterTextReplacement(reader: IReader | undefined): SingleTextEdit { - const inlineCompletion = this.toInlineCompletion(reader); - return new SingleTextEdit(inlineCompletion.range, inlineCompletion.filterText); + private readonly _updatedRange = derived(reader => { + const edit = this.updatedEdit.read(reader); + if (!edit || edit.edits.length === 0) { + return undefined; + } + + return Range.fromPositions( + this._textModel.getPositionAt(edit.edits[0].replaceRange.start), + this._textModel.getPositionAt(edit.edits[edit.edits.length - 1].replaceRange.endExclusive) + ); + }); + + private _toUpdatedEdit(editRange: Range, replaceText: string): UpdatedEdit { + return this.isInlineEdit + ? this._toInlineEditEdit(editRange, replaceText) + : this._toInlineCompletionEdit(editRange, replaceText); + } + + private _toInlineCompletionEdit(editRange: Range, replaceText: string): UpdatedEdit { + const startOffset = this._textModel.getOffsetAt(editRange.getStartPosition()); + const endOffset = this._textModel.getOffsetAt(editRange.getEndPosition()); + const originalRange = OffsetRange.ofStartAndLength(startOffset, endOffset - startOffset); + const offsetEdit = new OffsetEdit([new SingleOffsetEdit(originalRange, replaceText)]); + return new UpdatedEdit(offsetEdit, this._textModel, this._modelVersion, false); + } + + private _toInlineEditEdit(editRange: Range, replaceText: string): UpdatedEdit { + const eol = this._textModel.getEOL(); + const editOriginalText = this._textModel.getValueInRange(editRange); + const editReplaceText = replaceText.replace(/\r\n|\r|\n/g, eol); + + const diffAlgorithm = linesDiffComputers.getDefault(); + const lineDiffs = diffAlgorithm.computeDiff( + splitLines(editOriginalText), + splitLines(editReplaceText), + { + ignoreTrimWhitespace: false, + computeMoves: false, + extendToSubwords: true, + maxComputationTimeMs: 500, + } + ); + + const innerChanges = lineDiffs.changes.flatMap(c => c.innerChanges ?? []); + + function addRangeToPos(pos: Position, range: Range): Range { + const start = TextLength.fromPosition(range.getStartPosition()); + return TextLength.ofRange(range).createRange(start.addToPosition(pos)); + } + + const modifiedText = new StringText(editReplaceText); + + const offsetEdit = new OffsetEdit( + innerChanges.map(c => { + const range = addRangeToPos(editRange.getStartPosition(), c.originalRange); + const startOffset = this._textModel.getOffsetAt(range.getStartPosition()); + const endOffset = this._textModel.getOffsetAt(range.getEndPosition()); + const originalRange = OffsetRange.ofStartAndLength(startOffset, endOffset - startOffset); + + const replaceText = modifiedText.getValueOfRange(c.modifiedRange); + const originalText = this._textModel.getValueInRange(range); + const edit = new SingleOffsetEdit(originalRange, replaceText); + + return reshapeEdit(edit, originalText, innerChanges.length, this._textModel); + }) + ); + + return new UpdatedEdit(offsetEdit, this._textModel, this._modelVersion, true); } } class UpdatedEdit extends Disposable { private _innerEdits: SingleUpdatedEdit[]; - private _invalidationTime: number | undefined = Date.now() + 3000; + + private _inlineEditModelVersion: number; + public get modelVersion() { return this._inlineEditModelVersion; } private _lastChangePartOfInlineEdit = false; public get lastChangePartOfInlineEdit() { return this._lastChangePartOfInlineEdit; } @@ -529,10 +522,6 @@ class UpdatedEdit extends Disposable { this._innerEdits = this._applyTextModelChanges(change, this._innerEdits); } - if (this._hasInvalidationTimePassed()) { - return undefined; - } - if (this._innerEdits.length === 0) { return undefined; } @@ -554,6 +543,8 @@ class UpdatedEdit extends Disposable { ) { super(); + this._inlineEditModelVersion = this._modelVersion.get() ?? -1; + this._innerEdits = offsetEdit.edits.map(edit => { if (isInlineEdit) { const replacedRange = Range.fromPositions(textModel.getPositionAt(edit.replaceRange.start), textModel.getPositionAt(edit.replaceRange.endExclusive)); @@ -578,7 +569,7 @@ class UpdatedEdit extends Disposable { this._lastChangePartOfInlineEdit = edits.some(edit => edit.lastChangeUpdatedEdit); if (this._lastChangePartOfInlineEdit) { - this._cancelInvalidationTimer(); + this._inlineEditModelVersion = this._modelVersion.get() ?? -1; } edits = edits.filter(innerEdit => !innerEdit.edit!.isEmpty); @@ -588,14 +579,6 @@ class UpdatedEdit extends Disposable { return edits; } - - private _cancelInvalidationTimer() { - this._invalidationTime = undefined; - } - - private _hasInvalidationTimePassed(): boolean { - return !!this._invalidationTime && this._invalidationTime < Date.now(); - } } abstract class SingleUpdatedEdit { @@ -702,6 +685,14 @@ class SingleUpdatedNextEdit extends SingleUpdatedEdit { continue; } + // user did exactly the edit + if (change.equals(edit)) { + editHasChanged = true; + editStart = change.replaceRange.endExclusive; + editReplaceText = ''; + continue; + } + // MOVE EDIT if (change.replaceRange.start > editEnd) { // the change happens after the completion range @@ -729,50 +720,59 @@ class SingleUpdatedNextEdit extends SingleUpdatedEdit { const emptyRange = new Range(1, 1, 1, 1); -interface IRecordableLogEntry { - sourceId: string; - time: number; -} +function reshapeEdit(edit: SingleOffsetEdit, originalText: string, totalInnerEdits: number, textModel: ITextModel): SingleOffsetEdit { + // TODO: EOL are not properly trimmed by the diffAlgorithm #12680 + const eol = textModel.getEOL(); + if (edit.newText.endsWith(eol) && originalText.endsWith(eol)) { + edit = new SingleOffsetEdit(edit.replaceRange.deltaEnd(-eol.length), edit.newText.slice(0, -eol.length)); + } -export interface IRecordableEditorLogEntry extends IRecordableLogEntry { - modelUri: string; - modelVersion: number; -} + // INSERTION + // If the insertion ends with a new line and is inserted at the start of a line which has text, + // we move the insertion to the end of the previous line if possible + if (totalInnerEdits === 1 && edit.replaceRange.isEmpty && edit.newText.includes(eol)) { + edit = reshapeMultiLineInsertion(edit, textModel); + } -/** - * The sourceLabel must not contain '@'! -*/ -export function formatRecordableLogEntry(entry: T): string { - return entry.sourceId + ' @@ ' + JSON.stringify({ ...entry, sourceId: undefined }); -} + // The diff algorithm extended a simple edit to the entire word + // shrink it back to a simple edit if it is deletion/insertion only + if (totalInnerEdits === 1) { + const prefixLength = commonPrefixLength(originalText, edit.newText); + const suffixLength = commonSuffixLength(originalText.slice(prefixLength), edit.newText.slice(prefixLength)); -export class StructuredLogger extends Disposable { - public static cast(): typeof StructuredLogger { - return this as typeof StructuredLogger; + // reshape it back to an insertion + if (prefixLength + suffixLength === originalText.length) { + return new SingleOffsetEdit(edit.replaceRange.deltaStart(prefixLength).deltaEnd(-suffixLength), edit.newText.substring(prefixLength, edit.newText.length - suffixLength)); + } + + // reshape it back to a deletion + if (prefixLength + suffixLength === edit.newText.length) { + return new SingleOffsetEdit(edit.replaceRange.deltaStart(prefixLength).deltaEnd(-suffixLength), ''); + } } - private readonly _contextKeyValue = observableContextKey(this._contextKey, this._contextKeyService).recomputeInitiallyAndOnChange(this._store); + return edit; +} - constructor( - private readonly _contextKey: string, - @IContextKeyService private readonly _contextKeyService: IContextKeyService, - @ICommandService private readonly _commandService: ICommandService, - ) { - super(); +function reshapeMultiLineInsertion(edit: SingleOffsetEdit, textModel: ITextModel): SingleOffsetEdit { + if (!edit.replaceRange.isEmpty) { + throw new BugIndicatingError('Unexpected original range'); } - public readonly isEnabled = this._contextKeyValue.map(v => v !== undefined); + if (edit.replaceRange.start === 0) { + return edit; + } - public log(data: T): boolean { - const commandId = this._contextKeyValue.get(); - if (!commandId) { - return false; - } - this._commandService.executeCommand(commandId, data); - return true; + const eol = textModel.getEOL(); + const startPosition = textModel.getPositionAt(edit.replaceRange.start); + const startColumn = startPosition.column; + const startLineNumber = startPosition.lineNumber; + + // If the insertion ends with a new line and is inserted at the start of a line which has text, + // we move the insertion to the end of the previous line if possible + if (startColumn === 1 && startLineNumber > 1 && textModel.getLineLength(startLineNumber) !== 0 && edit.newText.endsWith(eol) && !edit.newText.startsWith(eol)) { + return new SingleOffsetEdit(edit.replaceRange.delta(-1), eol + edit.newText.slice(0, -eol.length)); } -} -export function observableContextKey(key: string, contextKeyService: IContextKeyService): IObservable { - return observableFromEvent(contextKeyService.onDidChangeContext, () => contextKeyService.getContextKeyValue(key)); + return edit; } diff --git a/src/vs/editor/contrib/inlineCompletions/browser/model/inlineEdit.ts b/src/vs/editor/contrib/inlineCompletions/browser/model/inlineEdit.ts index 63e45b69ac7d..13e6d6edf742 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/model/inlineEdit.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/model/inlineEdit.ts @@ -10,7 +10,6 @@ import { InlineCompletionItem } from './provideInlineCompletions.js'; export class InlineEdit { constructor( public readonly edit: SingleTextEdit, - public readonly renderExplicitly: boolean, public readonly commands: readonly Command[], public readonly inlineCompletion: InlineCompletionItem, ) { } @@ -25,7 +24,6 @@ export class InlineEdit { public equals(other: InlineEdit): boolean { return this.edit.equals(other.edit) - && this.renderExplicitly === other.renderExplicitly && this.inlineCompletion === other.inlineCompletion; } } diff --git a/src/vs/editor/contrib/inlineCompletions/browser/model/provideInlineCompletions.ts b/src/vs/editor/contrib/inlineCompletions/browser/model/provideInlineCompletions.ts index b85881e6b886..47cdd05852af 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/model/provideInlineCompletions.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/model/provideInlineCompletions.ts @@ -180,10 +180,10 @@ async function addRefAndCreateResult( completions.addRef(); lists.push(completions); for (const item of completions.inlineCompletions.items) { - if (!context.includeInlineEdits && item.isInlineEdit) { + if (!context.includeInlineEdits && (item.isInlineEdit || item.showInlineEditMenu)) { continue; } - if (!context.includeInlineCompletions && !item.isInlineEdit) { + if (!context.includeInlineCompletions && !(item.isInlineEdit || item.showInlineEditMenu)) { continue; } const inlineCompletionItem = InlineCompletionItem.from( @@ -197,7 +197,7 @@ async function addRefAndCreateResult( itemsByHash.set(inlineCompletionItem.hash(), inlineCompletionItem); // Stop after first visible inline completion - if (!item.isInlineEdit && context.triggerKind === InlineCompletionTriggerKind.Automatic) { + if (!(item.isInlineEdit || item.showInlineEditMenu) && context.triggerKind === InlineCompletionTriggerKind.Automatic) { const minifiedEdit = inlineCompletionItem.toSingleTextEdit().removeCommonPrefix(new TextModelText(model)); if (!minifiedEdit.isEmpty) { shouldStop = true; @@ -345,6 +345,7 @@ export class InlineCompletionItem { constructor( readonly filterText: string, readonly command: Command | undefined, + /** @deprecated. Use handleItemDidShow */ readonly shownCommand: Command | undefined, readonly action: Command | undefined, readonly range: Range, @@ -369,9 +370,10 @@ export class InlineCompletionItem { readonly id = `InlineCompletion:${InlineCompletionItem.ID++}`, ) { - // TODO: these statements are no-ops - filterText = filterText.replace(/\r\n|\r/g, '\n'); - insertText = filterText.replace(/\r\n|\r/g, '\n'); + } + + get isInlineEdit(): boolean { + return this.sourceInlineCompletion.isInlineEdit!!; } public get didShow(): boolean { @@ -381,23 +383,6 @@ export class InlineCompletionItem { this._didCallShow = true; } - public withRange(updatedRange: Range): InlineCompletionItem { - return new InlineCompletionItem( - this.filterText, - this.command, - this.shownCommand, - this.action, - updatedRange, - this.insertText, - this.snippetInfo, - this.cursorShowRange, - this.additionalTextEdits, - this.sourceInlineCompletion, - this.source, - this.id, - ); - } - public withRangeInsertTextAndFilterText(updatedRange: Range, updatedInsertText: string, updatedFilterText: string): InlineCompletionItem { return new InlineCompletionItem( updatedFilterText, diff --git a/src/vs/editor/contrib/inlineCompletions/browser/structuredLogger.ts b/src/vs/editor/contrib/inlineCompletions/browser/structuredLogger.ts new file mode 100644 index 000000000000..8a307df3bd30 --- /dev/null +++ b/src/vs/editor/contrib/inlineCompletions/browser/structuredLogger.ts @@ -0,0 +1,58 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable } from '../../../../base/common/lifecycle.js'; +import { IObservable, observableFromEvent } from '../../../../base/common/observable.js'; +import { ICommandService } from '../../../../platform/commands/common/commands.js'; +import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; + +export interface IRecordableLogEntry { + sourceId: string; + time: number; +} + +export interface IRecordableEditorLogEntry extends IRecordableLogEntry { + modelUri: string; + modelVersion: number; +} + +/** + * The sourceLabel must not contain '@'! +*/ +export function formatRecordableLogEntry(entry: T): string { + return entry.sourceId + ' @@ ' + JSON.stringify({ ...entry, sourceId: undefined }); +} + +export class StructuredLogger extends Disposable { + public static cast(): typeof StructuredLogger { + return this as typeof StructuredLogger; + } + + public readonly isEnabled; + private readonly _contextKeyValue; + + constructor( + private readonly _contextKey: string, + @IContextKeyService private readonly _contextKeyService: IContextKeyService, + @ICommandService private readonly _commandService: ICommandService + ) { + super(); + this._contextKeyValue = observableContextKey(this._contextKey, this._contextKeyService).recomputeInitiallyAndOnChange(this._store); + this.isEnabled = this._contextKeyValue.map(v => v !== undefined); + } + + public log(data: T): boolean { + const commandId = this._contextKeyValue.get(); + if (!commandId) { + return false; + } + this._commandService.executeCommand(commandId, data); + return true; + } +} + +function observableContextKey(key: string, contextKeyService: IContextKeyService): IObservable { + return observableFromEvent(contextKeyService.onDidChangeContext, () => contextKeyService.getContextKeyValue(key)); +} diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/ghostText/ghostTextView.css b/src/vs/editor/contrib/inlineCompletions/browser/view/ghostText/ghostTextView.css index da6682130bc1..16148bd87b09 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/ghostText/ghostTextView.css +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/ghostText/ghostTextView.css @@ -24,10 +24,21 @@ font-size: 0; } -.monaco-editor .ghost-text-decoration, .monaco-editor .suggest-preview-text .ghost-text { +.monaco-editor .ghost-text-decoration, +.monaco-editor .suggest-preview-text .ghost-text { font-style: italic; } +.monaco-editor .suggest-preview-text.clickable .view-line { + z-index: 1; +} + +.monaco-editor .ghost-text-decoration.clickable, +.monaco-editor .ghost-text-decoration-preview.clickable, +.monaco-editor .suggest-preview-text.clickable .ghost-text { + cursor: pointer; +} + .monaco-editor .inline-completion-text-to-replace { text-decoration: underline; text-underline-position: under; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/ghostText/ghostTextView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/ghostText/ghostTextView.ts index 1185bd8e82e8..d592eca09ea4 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/ghostText/ghostTextView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/ghostText/ghostTextView.ts @@ -6,13 +6,13 @@ import { createTrustedTypesPolicy } from '../../../../../../base/browser/trustedTypes.js'; import { renderIcon } from '../../../../../../base/browser/ui/iconLabel/iconLabels.js'; import { Codicon } from '../../../../../../base/common/codicons.js'; -import { Event } from '../../../../../../base/common/event.js'; +import { Emitter, Event } from '../../../../../../base/common/event.js'; import { createHotClass } from '../../../../../../base/common/hotReloadHelpers.js'; -import { Disposable, toDisposable } from '../../../../../../base/common/lifecycle.js'; +import { Disposable, DisposableStore, MutableDisposable, toDisposable } from '../../../../../../base/common/lifecycle.js'; import { IObservable, autorun, autorunWithStore, constObservable, derived, observableSignalFromEvent, observableValue } from '../../../../../../base/common/observable.js'; import * as strings from '../../../../../../base/common/strings.js'; import { applyFontInfo } from '../../../../../browser/config/domFontInfo.js'; -import { ContentWidgetPositionPreference, ICodeEditor, IContentWidgetPosition, IViewZoneChangeAccessor } from '../../../../../browser/editorBrowser.js'; +import { ContentWidgetPositionPreference, ICodeEditor, IContentWidgetPosition, IViewZoneChangeAccessor, MouseTargetType } from '../../../../../browser/editorBrowser.js'; import { observableCodeEditor } from '../../../../../browser/observableCodeEditor.js'; import { EditorFontLigatures, EditorOption, IComputedEditorOptions } from '../../../../../common/config/editorOptions.js'; import { OffsetEdit, SingleOffsetEdit } from '../../../../../common/core/offsetEdit.js'; @@ -28,8 +28,10 @@ import { RenderLineInput, renderViewLine } from '../../../../../common/viewLayou import { InlineDecorationType } from '../../../../../common/viewModel.js'; import { GhostText, GhostTextReplacement, IGhostTextLine } from '../../model/ghostText.js'; import { ColumnRange } from '../../utils.js'; -import { n } from '../../../../../../base/browser/dom.js'; +import { addDisposableListener, getWindow, isHTMLElement, n } from '../../../../../../base/browser/dom.js'; import './ghostTextView.css'; +import { IMouseEvent, StandardMouseEvent } from '../../../../../../base/browser/mouseEvent.js'; +import { CodeEditorWidget } from '../../../../../browser/widget/codeEditor/codeEditorWidget.js'; export interface IGhostTextWidgetModel { readonly targetTextModel: IObservable; @@ -39,6 +41,7 @@ export interface IGhostTextWidgetModel { } const USE_SQUIGGLES_FOR_WARNING = true; +const GHOST_TEXT_CLASS_NAME = 'ghost-text'; export class GhostTextView extends Disposable { private readonly _isDisposed = observableValue(this, false); @@ -53,6 +56,9 @@ export class GhostTextView extends Disposable { return { lineNumber: gt.lineNumber, position: new Position(gt.lineNumber, gt.parts[0].column), icon: warning.icon }; }); + private readonly _onDidClick = this._register(new Emitter()); + public readonly onDidClick = this._onDidClick.event; + constructor( private readonly _editor: ICodeEditor, private readonly _model: IGhostTextWidgetModel, @@ -61,6 +67,7 @@ export class GhostTextView extends Disposable { syntaxHighlightingEnabled: boolean; }>, private readonly _shouldKeepCursorStable: boolean, + private readonly _isClickable: boolean, @ILanguageService private readonly _languageService: ILanguageService, ) { super(); @@ -68,6 +75,19 @@ export class GhostTextView extends Disposable { this._register(toDisposable(() => { this._isDisposed.set(true, undefined); })); this._register(this._editorObs.setDecorations(this.decorations)); + if (this._isClickable) { + this._register(this._additionalLinesWidget.onDidClick((e) => this._onDidClick.fire(e))); + this._register(this._editor.onMouseUp(e => { + if (e.target.type !== MouseTargetType.CONTENT_TEXT) { + return; + } + const a = e.target.detail.injectedText?.options.attachedData; + if (a instanceof GhostTextAttachedData && a.owner === this) { + this._onDidClick.fire(e.event); + } + })); + } + this._register(autorunWithStore((reader, store) => { if (USE_SQUIGGLES_FOR_WARNING) { return; @@ -150,7 +170,7 @@ export class GhostTextView extends Disposable { const syntaxHighlightingEnabled = this._useSyntaxHighlighting.read(reader); const extraClassNames = this._extraClassNames.read(reader); - const { inlineTexts, additionalLines, hiddenRange } = computeGhostTextViewData(ghostText, textModel, 'ghost-text' + extraClassNames); + const { inlineTexts, additionalLines, hiddenRange } = computeGhostTextViewData(ghostText, textModel, GHOST_TEXT_CLASS_NAME + extraClassNames); const currentLine = textModel.getLineContent(ghostText.lineNumber); const edit = new OffsetEdit(inlineTexts.map(t => SingleOffsetEdit.insert(t.column - 1, t.text))); @@ -205,8 +225,12 @@ export class GhostTextView extends Disposable { after: { content: p.text, tokens: p.tokens, - inlineClassName: p.preview ? 'ghost-text-decoration-preview' : 'ghost-text-decoration' + extraClassNames + p.lineDecorations.map(d => ' ' + d.className).join(' '), // TODO: take the ranges into account for line decorations + inlineClassName: (p.preview ? 'ghost-text-decoration-preview' : 'ghost-text-decoration') + + (this._isClickable ? ' clickable' : '') + + extraClassNames + + p.lineDecorations.map(d => ' ' + d.className).join(' '), // TODO: take the ranges into account for line decorations cursorStops: InjectedTextCursorStops.Left, + attachedData: new GhostTextAttachedData(this), }, showIfCollapsed: true, } @@ -216,7 +240,7 @@ export class GhostTextView extends Disposable { return decorations; }); - private readonly additionalLinesWidget = this._register( + private readonly _additionalLinesWidget = this._register( new AdditionalLinesWidget( this._editor, derived(reader => { @@ -229,20 +253,37 @@ export class GhostTextView extends Disposable { targetTextModel: uiState.targetTextModel, } : undefined; }), - this._shouldKeepCursorStable + this._shouldKeepCursorStable, + this._isClickable ) ); + private readonly _isInlineTextHovered = this._editorObs.isTargetHovered( + p => p.target.type === MouseTargetType.CONTENT_TEXT && + p.target.detail.injectedText?.options.attachedData instanceof GhostTextAttachedData && + p.target.detail.injectedText.options.attachedData.owner === this, + this._store + ); + + public readonly isHovered = derived(this, reader => { + if (this._isDisposed.read(reader)) { return false; } + return this._isInlineTextHovered.read(reader) || this._additionalLinesWidget.isHovered.read(reader); + }); + public readonly height = derived(this, reader => { const lineHeight = this._editorObs.getOption(EditorOption.lineHeight).read(reader); - return lineHeight + (this.additionalLinesWidget.viewZoneHeight.read(reader) ?? 0); + return lineHeight + (this._additionalLinesWidget.viewZoneHeight.read(reader) ?? 0); }); public ownsViewZone(viewZoneId: string): boolean { - return this.additionalLinesWidget.viewZoneId === viewZoneId; + return this._additionalLinesWidget.viewZoneId === viewZoneId; } } +class GhostTextAttachedData { + constructor(public readonly owner: GhostTextView) { } +} + interface WidgetDomElement { ghostTextViewWarningWidgetData?: { range: Range; @@ -324,7 +365,7 @@ export class AdditionalLinesWidget extends Disposable { public get viewZoneHeight(): IObservable { return this._viewZoneHeight; } private readonly editorOptionsChanged = observableSignalFromEvent('editorOptionChanged', Event.filter( - this.editor.onDidChangeConfiguration, + this._editor.onDidChangeConfiguration, e => e.hasChanged(EditorOption.disableMonospaceOptimizations) || e.hasChanged(EditorOption.stopRenderingLineAfter) || e.hasChanged(EditorOption.renderWhitespace) @@ -334,24 +375,42 @@ export class AdditionalLinesWidget extends Disposable { || e.hasChanged(EditorOption.lineHeight) )); + private readonly _onDidClick = this._register(new Emitter()); + public readonly onDidClick = this._onDidClick.event; + + private readonly _viewZoneListener = this._register(new MutableDisposable()); + + readonly isHovered = observableCodeEditor(this._editor).isTargetHovered( + p => isTargetGhostText(p.target.element), + this._store + ); + + private hasBeenAccepted = false; + constructor( - private readonly editor: ICodeEditor, - private readonly lines: IObservable<{ + private readonly _editor: ICodeEditor, + private readonly _lines: IObservable<{ targetTextModel: ITextModel; lineNumber: number; additionalLines: LineData[]; minReservedLineCount: number; } | undefined>, - private readonly shouldKeepCursorStable: boolean + private readonly _shouldKeepCursorStable: boolean, + private readonly _isClickable: boolean, ) { super(); + if (this._editor instanceof CodeEditorWidget && this._shouldKeepCursorStable) { + this._register(this._editor.onBeforeExecuteEdit(e => this.hasBeenAccepted = e.source === 'inlineSuggestion.accept')); + } + this._register(autorun(reader => { /** @description update view zone */ - const lines = this.lines.read(reader); + const lines = this._lines.read(reader); this.editorOptionsChanged.read(reader); if (lines) { + this.hasBeenAccepted = false; this.updateLines(lines.lineNumber, lines.additionalLines, lines.minReservedLineCount); } else { this.clear(); @@ -365,29 +424,46 @@ export class AdditionalLinesWidget extends Disposable { } private clear(): void { - this.editor.changeViewZones((changeAccessor) => { + this._viewZoneListener.clear(); + + this._editor.changeViewZones((changeAccessor) => { this.removeActiveViewZone(changeAccessor); }); } private updateLines(lineNumber: number, additionalLines: LineData[], minReservedLineCount: number): void { - const textModel = this.editor.getModel(); + const textModel = this._editor.getModel(); if (!textModel) { return; } const { tabSize } = textModel.getOptions(); - this.editor.changeViewZones((changeAccessor) => { + this._editor.changeViewZones((changeAccessor) => { + const store = new DisposableStore(); + this.removeActiveViewZone(changeAccessor); const heightInLines = Math.max(additionalLines.length, minReservedLineCount); if (heightInLines > 0) { const domNode = document.createElement('div'); - renderLines(domNode, tabSize, additionalLines, this.editor.getOptions()); + renderLines(domNode, tabSize, additionalLines, this._editor.getOptions(), this._isClickable); + + if (this._isClickable) { + store.add(addDisposableListener(domNode, 'mousedown', (e) => { + e.preventDefault(); // This prevents that the editor loses focus + })); + store.add(addDisposableListener(domNode, 'click', (e) => { + if (isTargetGhostText(e.target)) { + this._onDidClick.fire(new StandardMouseEvent(getWindow(e), e)); + } + })); + } this.addViewZone(changeAccessor, lineNumber, heightInLines, domNode); } + + this._viewZoneListener.value = store; }); } @@ -411,7 +487,9 @@ export class AdditionalLinesWidget extends Disposable { if (this._viewZoneInfo) { changeAccessor.removeZone(this._viewZoneInfo.viewZoneId); - this.keepCursorStable(this._viewZoneInfo.lineNumber, -this._viewZoneInfo.heightInLines); + if (!this.hasBeenAccepted) { + this.keepCursorStable(this._viewZoneInfo.lineNumber, -this._viewZoneInfo.heightInLines); + } this._viewZoneInfo = undefined; this._viewZoneHeight.set(undefined, undefined); @@ -419,23 +497,27 @@ export class AdditionalLinesWidget extends Disposable { } private keepCursorStable(lineNumber: number, heightInLines: number): void { - if (!this.shouldKeepCursorStable) { + if (!this._shouldKeepCursorStable) { return; } - const cursorLineNumber = this.editor.getSelection()?.getStartPosition()?.lineNumber; + const cursorLineNumber = this._editor.getSelection()?.getStartPosition()?.lineNumber; if (cursorLineNumber !== undefined && lineNumber < cursorLineNumber) { - this.editor.setScrollTop(this.editor.getScrollTop() + heightInLines * this.editor.getOption(EditorOption.lineHeight)); + this._editor.setScrollTop(this._editor.getScrollTop() + heightInLines * this._editor.getOption(EditorOption.lineHeight)); } } } +function isTargetGhostText(target: EventTarget | null): boolean { + return isHTMLElement(target) && target.classList.contains(GHOST_TEXT_CLASS_NAME); +} + export interface LineData { content: LineTokens; // Must not contain a linebreak! decorations: LineDecoration[]; } -function renderLines(domNode: HTMLElement, tabSize: number, lines: LineData[], opts: IComputedEditorOptions): void { +function renderLines(domNode: HTMLElement, tabSize: number, lines: LineData[], opts: IComputedEditorOptions, isClickable: boolean): void { const disableMonospaceOptimizations = opts.get(EditorOption.disableMonospaceOptimizations); const stopRenderingLineAfter = opts.get(EditorOption.stopRenderingLineAfter); // To avoid visual confusion, we don't want to render visible whitespace @@ -445,8 +527,13 @@ function renderLines(domNode: HTMLElement, tabSize: number, lines: LineData[], o const fontInfo = opts.get(EditorOption.fontInfo); const lineHeight = opts.get(EditorOption.lineHeight); + let classNames = 'suggest-preview-text'; + if (isClickable) { + classNames += ' clickable'; + } + const sb = new StringBuilder(10000); - sb.appendString('
'); + sb.appendString(`
`); for (let i = 0, len = lines.length; i < len; i++) { const lineData = lines[i]; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineCompletionsView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineCompletionsView.ts index 8fcf2a1b05c0..bcf120f49964 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineCompletionsView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineCompletionsView.ts @@ -14,7 +14,7 @@ import { InlineCompletionsHintsWidget } from '../hintsWidget/inlineCompletionsHi import { InlineCompletionsModel } from '../model/inlineCompletionsModel.js'; import { convertItemsToStableObservables } from '../utils.js'; import { GhostTextView } from './ghostText/ghostTextView.js'; -import { InlineEditsViewAndDiffProducer } from './inlineEdits/viewAndDiffProducer.js'; +import { InlineEditsViewAndDiffProducer } from './inlineEdits/inlineEditsViewProducer.js'; export class InlineCompletionsView extends Disposable { private readonly _ghostTexts = derived(this, (reader) => { @@ -39,12 +39,13 @@ export class InlineCompletionsView extends Disposable { }, this._editorObs.getOption(EditorOption.inlineSuggest).map(v => ({ syntaxHighlightingEnabled: v.syntaxHighlightingEnabled })), false, + false ) ).recomputeInitiallyAndOnChange(store) ).recomputeInitiallyAndOnChange(this._store); private readonly _inlineEdit = derived(this, reader => this._model.read(reader)?.inlineEditState.read(reader)?.inlineEdit); - private readonly _everHadInlineEdit = derivedObservableWithCache(this, (reader, last) => last || !!this._inlineEdit.read(reader)); + private readonly _everHadInlineEdit = derivedObservableWithCache(this, (reader, last) => last || !!this._inlineEdit.read(reader) || !!this._model.read(reader)?.inlineCompletionState.read(reader)?.inlineCompletion?.sourceInlineCompletion.showInlineEditMenu); protected readonly _inlineEditWidget = derivedDisposable(reader => { if (!this._everHadInlineEdit.read(reader)) { return undefined; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/components/gutterIndicatorMenu.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/components/gutterIndicatorMenu.ts index 23860ee0574f..848af4d26139 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/components/gutterIndicatorMenu.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/components/gutterIndicatorMenu.ts @@ -22,21 +22,22 @@ import { asCssVariable, descriptionForeground, editorActionListForeground, edito import { ObservableCodeEditor } from '../../../../../../browser/observableCodeEditor.js'; import { EditorOption } from '../../../../../../common/config/editorOptions.js'; import { hideInlineCompletionId, inlineSuggestCommitId, jumpToNextInlineEditId, toggleShowCollapsedId } from '../../../controller/commandIds.js'; -import { IInlineEditsViewHost } from '../inlineEditsViewInterface.js'; -import { FirstFnArg, InlineEditTabAction } from '../utils/utils.js'; +import { IInlineEditModel, InlineEditTabAction } from '../inlineEditsViewInterface.js'; +import { FirstFnArg, } from '../utils/utils.js'; export class GutterIndicatorMenuContent { - private readonly _inlineEditsShowCollapsed = this._editorObs.getOption(EditorOption.inlineSuggest).map(s => s.edits.showCollapsed); + private readonly _inlineEditsShowCollapsed: IObservable; constructor( - private readonly _host: IInlineEditsViewHost, + private readonly _model: IInlineEditModel, private readonly _close: (focusEditor: boolean) => void, private readonly _editorObs: ObservableCodeEditor, @IContextKeyService private readonly _contextKeyService: IContextKeyService, @IKeybindingService private readonly _keybindingService: IKeybindingService, @ICommandService private readonly _commandService: ICommandService, ) { + this._inlineEditsShowCollapsed = this._editorObs.getOption(EditorOption.inlineSuggest).map(s => s.edits.showCollapsed); } public toDisposableLiveElement(): LiveElement { @@ -60,39 +61,74 @@ export class GutterIndicatorMenuContent { }; }; - // TODO make this menu contributable! - return hoverContent([ - header(this._host.displayName), + const title = header(this._model.displayName); + + const gotoAndAccept = option(createOptionArgs({ + id: 'gotoAndAccept', + title: `${localize('goto', "Go To")} / ${localize('accept', "Accept")}`, + icon: this._model.tabAction.map(action => action === InlineEditTabAction.Accept ? Codicon.check : Codicon.arrowRight), + commandId: this._model.tabAction.map(action => action === InlineEditTabAction.Accept ? inlineSuggestCommitId : jumpToNextInlineEditId) + })); + + const reject = option(createOptionArgs({ + id: 'reject', + title: localize('reject', "Reject"), + icon: Codicon.close, + commandId: hideInlineCompletionId + })); + + const extensionCommands = this._model.extensionCommands.map((c, idx) => option(createOptionArgs({ id: c.id + '_' + idx, title: c.title, icon: Codicon.symbolEvent, commandId: c.id, commandArgs: c.arguments }))); + + const toggleCollapsedMode = this._inlineEditsShowCollapsed.map(showCollapsed => showCollapsed ? option(createOptionArgs({ - id: 'gotoAndAccept', title: `${localize('goto', "Go To")} / ${localize('accept', "Accept")}`, - icon: this._host.tabAction.map(action => action === InlineEditTabAction.Accept ? Codicon.check : Codicon.arrowRight), - commandId: this._host.tabAction.map(action => action === InlineEditTabAction.Accept ? inlineSuggestCommitId : jumpToNextInlineEditId) + id: 'showExpanded', + title: localize('showExpanded', "Show Expanded"), + icon: Codicon.expandAll, + commandId: toggleShowCollapsedId + })) + : option(createOptionArgs({ + id: 'showCollapsed', + title: localize('showCollapsed', "Show Collapsed"), + icon: Codicon.collapseAll, + commandId: toggleShowCollapsedId + })) + ); + + const settings = option(createOptionArgs({ + id: 'settings', + title: localize('settings', "Settings"), + icon: Codicon.gear, + commandId: 'workbench.action.openSettings', + commandArgs: ['@tag:nextEditSuggestions'] + })); + + const actions = this._model.action ? [this._model.action] : []; + const actionBarFooter = actions.length > 0 ? actionBar( + actions.map(action => ({ + id: action.id, + label: action.title, + enabled: true, + run: () => this._commandService.executeCommand(action.id, ...(action.arguments ?? [])), + class: undefined, + tooltip: action.tooltip ?? action.title })), - option(createOptionArgs({ id: 'reject', title: localize('reject', "Reject"), icon: Codicon.close, commandId: hideInlineCompletionId })), + { hoverDelegate: nativeHoverDelegate /* unable to show hover inside another hover */ } + ) : undefined; + + return hoverContent([ + title, + gotoAndAccept, + reject, separator(), - this._inlineEditsShowCollapsed.map(showCollapsed => showCollapsed ? - option(createOptionArgs({ id: 'showExpanded', title: localize('showExpanded', "Show Expanded"), icon: Codicon.expandAll, commandId: toggleShowCollapsedId })) : - option(createOptionArgs({ id: 'showCollapsed', title: localize('showCollapsed', "Show Collapsed"), icon: Codicon.collapseAll, commandId: toggleShowCollapsedId })) - ), - this._host.extensionCommands?.map(c => c && c.length > 0 ? [ - ...c.map((c, idx) => option(createOptionArgs({ id: c.id + '_' + idx, title: c.title, icon: Codicon.symbolEvent, commandId: c.id, commandArgs: c.arguments }))), - separator() - ] : []), - option(createOptionArgs({ id: 'settings', title: localize('settings', "Settings"), icon: Codicon.gear, commandId: 'workbench.action.openSettings', commandArgs: ['@tag:nextEditSuggestions'] })), - this._host.action.map(action => action ? [ - separator(), - actionBar( - [{ - id: action.id, - label: action.title, - enabled: true, - run: () => this._commandService.executeCommand(action.id, ...(action.arguments ?? [])), - class: undefined, - tooltip: action.tooltip ?? action.title - }], - { hoverDelegate: nativeHoverDelegate /* unable to show hover inside another hover */ } - ) - ] : []) + + ...extensionCommands, + extensionCommands.length ? separator() : undefined, + + toggleCollapsedMode, + settings, + + actionBarFooter ? separator() : undefined, + actionBarFooter ]); } @@ -188,6 +224,7 @@ function actionBar(actions: IAction[], options: IActionBarOptions) { function separator() { return n.div({ + id: 'inline-edit-gutter-indicator-menu-separator', class: 'menu-separator', style: { color: asCssVariable(editorActionListForeground), diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/components/gutterIndicatorView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/components/gutterIndicatorView.ts index 170fa86dce8e..77e3b3fc1b40 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/components/gutterIndicatorView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/components/gutterIndicatorView.ts @@ -6,6 +6,7 @@ import { n, trackFocus } from '../../../../../../../base/browser/dom.js'; import { renderIcon } from '../../../../../../../base/browser/ui/iconLabel/iconLabels.js'; import { Codicon } from '../../../../../../../base/common/codicons.js'; +import { BugIndicatingError } from '../../../../../../../base/common/errors.js'; import { Disposable, DisposableStore, toDisposable } from '../../../../../../../base/common/lifecycle.js'; import { IObservable, ISettableObservable, constObservable, derived, observableFromEvent, observableValue, runOnChange } from '../../../../../../../base/common/observable.js'; import { debouncedObservable } from '../../../../../../../base/common/observableInternal/utils.js'; @@ -21,19 +22,28 @@ import { EditorOption } from '../../../../../../common/config/editorOptions.js'; import { LineRange } from '../../../../../../common/core/lineRange.js'; import { OffsetRange } from '../../../../../../common/core/offsetRange.js'; import { StickyScrollController } from '../../../../../stickyScroll/browser/stickyScrollController.js'; -import { InlineCompletionsModel } from '../../../model/inlineCompletionsModel.js'; -import { IInlineEditsViewHost } from '../inlineEditsViewInterface.js'; +import { IInlineEditModel, InlineEditTabAction } from '../inlineEditsViewInterface.js'; import { inlineEditIndicatorBackground, inlineEditIndicatorPrimaryBackground, inlineEditIndicatorPrimaryForeground, inlineEditIndicatorSecondaryBackground, inlineEditIndicatorSecondaryForeground, inlineEditIndicatorsuccessfulBackground, inlineEditIndicatorsuccessfulForeground } from '../theme.js'; -import { InlineEditTabAction, mapOutFalsy, rectToProps } from '../utils/utils.js'; +import { mapOutFalsy, rectToProps } from '../utils/utils.js'; import { GutterIndicatorMenuContent } from './gutterIndicatorMenu.js'; export class InlineEditsGutterIndicator extends Disposable { + + private get model() { + const model = this._model.get(); + if (!model) { throw new BugIndicatingError('Inline Edit Model not available'); } + return model; + } + + private readonly _gutterIndicatorBackgroundColor: IObservable; + private readonly _gutterIndicatorForegroundColor: IObservable; + private readonly _isHoveredOverInlineEditDebounced: IObservable; + constructor( private readonly _editorObs: ObservableCodeEditor, private readonly _originalRange: IObservable, private readonly _verticalOffset: IObservable, - private readonly _model: IObservable, - private readonly _host: IInlineEditsViewHost, + private readonly _model: IObservable, private readonly _isHoveringOverInlineEdit: IObservable, private readonly _focusIsInMenu: ISettableObservable, @IHoverService private readonly _hoverService: HoverService, @@ -42,6 +52,21 @@ export class InlineEditsGutterIndicator extends Disposable { ) { super(); + this._gutterIndicatorBackgroundColor = this._tabAction.map(v => { + switch (v) { + case InlineEditTabAction.Inactive: return asCssVariable(inlineEditIndicatorSecondaryBackground); + case InlineEditTabAction.Jump: return asCssVariable(inlineEditIndicatorPrimaryBackground); + case InlineEditTabAction.Accept: return asCssVariable(inlineEditIndicatorsuccessfulBackground); + } + }); + this._gutterIndicatorForegroundColor = this._tabAction.map(v => { + switch (v) { + case InlineEditTabAction.Inactive: return asCssVariable(inlineEditIndicatorSecondaryForeground); + case InlineEditTabAction.Jump: return asCssVariable(inlineEditIndicatorPrimaryForeground); + case InlineEditTabAction.Accept: return asCssVariable(inlineEditIndicatorsuccessfulForeground); + } + }); + this._register(this._editorObs.createOverlayWidget({ domNode: this._indicator.element, position: constObservable(null), @@ -49,20 +74,37 @@ export class InlineEditsGutterIndicator extends Disposable { minContentWidthInPx: constObservable(0), })); + this._isHoveredOverInlineEditDebounced = debouncedObservable(this._isHoveringOverInlineEdit, 100); + if (!accessibilityService.isMotionReduced()) { - const debouncedIsHovering = debouncedObservable(this._isHoveringOverInlineEdit, 100); - this._register(runOnChange(debouncedIsHovering, (isHovering) => { + this._register(runOnChange(this._isHoveredOverInlineEditDebounced, (isHovering) => { if (!isHovering) { return; } - this._iconRef.element.animate([ + + // WIGGLE ANIMATION: + /* this._iconRef.element.animate([ { transform: 'rotate(0) scale(1)', offset: 0 }, { transform: 'rotate(14.4deg) scale(1.1)', offset: 0.15 }, { transform: 'rotate(-14.4deg) scale(1.2)', offset: 0.3 }, { transform: 'rotate(14.4deg) scale(1.1)', offset: 0.45 }, { transform: 'rotate(-14.4deg) scale(1.2)', offset: 0.6 }, { transform: 'rotate(0) scale(1)', offset: 1 } - ], { duration: 800 }); + ], { duration: 800 }); */ + + // PULSE ANIMATION: + this._iconRef.element.animate([ + { + outline: `2px solid ${this._gutterIndicatorBackgroundColor.get()}`, + outlineOffset: '-1px', + offset: 0 + }, + { + outline: `2px solid transparent`, + outlineOffset: '10px', + offset: 1 + }, + ], { duration: 500 }); })); } } @@ -111,12 +153,32 @@ export class InlineEditsGutterIndicator extends Disposable { ? pillRectMoved : pillRectMoved.moveToBeContainedIn(fullViewPort.intersect(targetRect.union(fullViewPort.withHeight(lineHeight)))!); //viewPortWithStickyScroll.intersect(rect)!; + + const docked = rect.containsRect(iconRect) && viewPortWithStickyScroll.containsRect(iconRect); + let iconDirecion = (targetRect.containsRect(iconRect) ? 'right' as const + : iconRect.top > targetRect.top ? 'top' as const : 'bottom' as const); + + let icon; + if (docked && (this._isHoveredOverIconDebounced.read(reader) || this._isHoveredOverInlineEditDebounced.read(reader))) { + icon = renderIcon(Codicon.check); + iconDirecion = 'right'; + } else { + icon = this._tabAction.read(reader) === InlineEditTabAction.Accept ? renderIcon(Codicon.keyboardTab) : renderIcon(Codicon.arrowRight); + } + + let rotation = 0; + switch (iconDirecion) { + case 'right': rotation = 0; break; + case 'bottom': rotation = 90; break; + case 'top': rotation = -90; break; + } + return { rect, + icon, + rotation, + docked, iconRect, - arrowDirection: (targetRect.containsRect(iconRect) ? 'right' as const - : iconRect.top > targetRect.top ? 'top' as const : 'bottom' as const), - docked: rect.containsRect(iconRect) && viewPortWithStickyScroll.containsRect(iconRect), }; }); @@ -124,6 +186,7 @@ export class InlineEditsGutterIndicator extends Disposable { private readonly _hoverVisible = observableValue(this, false); public readonly isHoverVisible: IObservable = this._hoverVisible; private readonly _isHoveredOverIcon = observableValue(this, false); + private readonly _isHoveredOverIconDebounced: IObservable = debouncedObservable(this._isHoveredOverIcon, 100); private _showHover(): void { if (this._hoverVisible.get()) { @@ -133,7 +196,7 @@ export class InlineEditsGutterIndicator extends Disposable { const disposableStore = new DisposableStore(); const content = disposableStore.add(this._instantiationService.createInstance( GutterIndicatorMenuContent, - this._host, + this.model, (focusEditor) => { if (focusEditor) { this._editorObs.editor.focus(); @@ -148,7 +211,7 @@ export class InlineEditsGutterIndicator extends Disposable { disposableStore.add(focusTracker.onDidFocus(() => this._focusIsInMenu.set(true, undefined))); disposableStore.add(toDisposable(() => this._focusIsInMenu.set(false, undefined))); - const h = this._hoverService.showHover({ + const h = this._hoverService.showInstantHover({ target: this._iconRef.element, content: content.element, }) as HoverWidget | undefined; @@ -163,17 +226,21 @@ export class InlineEditsGutterIndicator extends Disposable { } } + private readonly _tabAction = derived(this, reader => { + const model = this._model.read(reader); + if (!model) { return InlineEditTabAction.Inactive; } + return model.tabAction.read(reader); + }); + private readonly _indicator = n.div({ class: 'inline-edits-view-gutter-indicator', onclick: () => { - const model = this._model.get(); - if (!model) { return; } const docked = this._layout.map(l => l && l.docked).get(); this._editorObs.editor.focus(); if (docked) { - model.accept(); + this.model.accept(); } else { - model.jump(); + this.model.jump(); } }, tabIndex: 0, @@ -203,20 +270,8 @@ export class InlineEditsGutterIndicator extends Disposable { cursor: 'pointer', zIndex: '1000', position: 'absolute', - backgroundColor: this._host.tabAction.map(v => { - switch (v) { - case InlineEditTabAction.Inactive: return asCssVariable(inlineEditIndicatorSecondaryBackground); - case InlineEditTabAction.Jump: return asCssVariable(inlineEditIndicatorPrimaryBackground); - case InlineEditTabAction.Accept: return asCssVariable(inlineEditIndicatorsuccessfulBackground); - } - }), - ['--vscodeIconForeground' as any]: this._host.tabAction.map(v => { - switch (v) { - case InlineEditTabAction.Inactive: return asCssVariable(inlineEditIndicatorSecondaryForeground); - case InlineEditTabAction.Jump: return asCssVariable(inlineEditIndicatorPrimaryForeground); - case InlineEditTabAction.Accept: return asCssVariable(inlineEditIndicatorsuccessfulForeground); - } - }), + backgroundColor: this._gutterIndicatorBackgroundColor, + ['--vscodeIconForeground' as any]: this._gutterIndicatorForegroundColor, borderRadius: '4px', display: 'flex', justifyContent: 'center', @@ -226,20 +281,14 @@ export class InlineEditsGutterIndicator extends Disposable { }, [ n.div({ style: { - rotate: layout.map(l => { - switch (l.arrowDirection) { - case 'right': return '0deg'; - case 'bottom': return '90deg'; - case 'top': return '-90deg'; - } - }), + rotate: layout.map(i => `${i.rotation}deg`), transition: 'rotate 0.2s ease-in-out', display: 'flex', alignItems: 'center', justifyContent: 'center', } }, [ - this._host.tabAction.map(v => v === InlineEditTabAction.Accept ? renderIcon(Codicon.keyboardTab) : renderIcon(Codicon.arrowRight)) + layout.map(i => i.icon), ]) ]), ])).keepUpdated(this._store); diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditWithChanges.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditWithChanges.ts index 7a31b034666a..0aac963340b8 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditWithChanges.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditWithChanges.ts @@ -19,7 +19,6 @@ export class InlineEditWithChanges { public readonly originalText: AbstractText, public readonly edit: TextEdit, public readonly cursorPosition: Position, - public readonly userJumpedToIt: boolean, public readonly commands: readonly Command[], public readonly inlineCompletion: InlineCompletionItem ) { @@ -29,7 +28,6 @@ export class InlineEditWithChanges { return this.originalText.getValue() === other.originalText.getValue() && this.edit.equals(other.edit) && this.cursorPosition.equals(other.cursorPosition) && - this.userJumpedToIt === other.userJumpedToIt && this.commands === other.commands && this.inlineCompletion === other.inlineCompletion; } diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsModel.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsModel.ts new file mode 100644 index 000000000000..9556dfbce7a2 --- /dev/null +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsModel.ts @@ -0,0 +1,93 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { derived, IObservable } from '../../../../../../base/common/observable.js'; +import { localize } from '../../../../../../nls.js'; +import { ICodeEditor } from '../../../../../browser/editorBrowser.js'; +import { observableCodeEditor } from '../../../../../browser/observableCodeEditor.js'; +import { LineRange } from '../../../../../common/core/lineRange.js'; +import { StringText, TextEdit } from '../../../../../common/core/textEdit.js'; +import { Command } from '../../../../../common/languages.js'; +import { InlineCompletionsModel } from '../../model/inlineCompletionsModel.js'; +import { InlineCompletionWithUpdatedRange } from '../../model/inlineCompletionsSource.js'; +import { IInlineEditModel, InlineEditTabAction } from './inlineEditsViewInterface.js'; +import { InlineEditWithChanges } from './inlineEditWithChanges.js'; + +export class InlineEditModel implements IInlineEditModel { + + readonly action: Command | undefined; + readonly displayName: string; + readonly extensionCommands: Command[]; + + readonly showCollapsed: IObservable; + readonly inAcceptFlow: IObservable; + readonly inPartialAcceptFlow: IObservable; + + constructor( + private readonly _model: InlineCompletionsModel, + readonly inlineEdit: InlineEditWithChanges, + readonly tabAction: IObservable, + ) { + this.action = this.inlineEdit.inlineCompletion.action; + this.displayName = this.inlineEdit.inlineCompletion.source.provider.displayName ?? localize('inlineEdit', "Inline Edit"); + this.extensionCommands = this.inlineEdit.inlineCompletion.source.inlineCompletions.commands ?? []; + + this.inAcceptFlow = this._model.inAcceptFlow; + this.inPartialAcceptFlow = this._model.inPartialAcceptFlow; + this.showCollapsed = this._model.showCollapsed; + } + + accept() { + this._model.accept(); + } + + jump() { + this._model.jump(); + } + + abort(reason: string) { + console.error(reason); // TODO: add logs/telemetry + this._model.stop(); + } + + handleInlineEditShown() { + this._model.handleInlineEditShown(this.inlineEdit.inlineCompletion); + } +} + + +export class GhostTextIndicator { + + readonly model: InlineEditModel; + + constructor( + editor: ICodeEditor, + model: InlineCompletionsModel, + readonly lineRange: LineRange, + inlineCompletion: InlineCompletionWithUpdatedRange, + ) { + const editorObs = observableCodeEditor(editor); + const tabAction = derived(this, reader => { + if (editorObs.isFocused.read(reader)) { + if (model.inlineCompletionState.read(reader)?.inlineCompletion?.sourceInlineCompletion.showInlineEditMenu) { + return InlineEditTabAction.Accept; + } + } + return InlineEditTabAction.Inactive; + }); + + this.model = new InlineEditModel( + model, + new InlineEditWithChanges( + new StringText(''), + new TextEdit([]), + model.primaryPosition.get(), + inlineCompletion.source.inlineCompletions.commands ?? [], + inlineCompletion.inlineCompletion + ), + tabAction, + ); + } +} diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsView.ts index a1aa61c76693..6670696ecdc3 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsView.ts @@ -3,12 +3,14 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { equalsIfDefined, itemEquals } from '../../../../../../base/common/equals.js'; +import { BugIndicatingError } from '../../../../../../base/common/errors.js'; +import { Event } from '../../../../../../base/common/event.js'; import { Disposable } from '../../../../../../base/common/lifecycle.js'; -import { derived, derivedObservableWithCache, derivedWithStore, IObservable, IReader, ISettableObservable, mapObservableArrayCached } from '../../../../../../base/common/observable.js'; -import { localize } from '../../../../../../nls.js'; +import { autorunWithStore, derived, derivedOpts, derivedWithStore, IObservable, IReader, ISettableObservable, mapObservableArrayCached, observableValue } from '../../../../../../base/common/observable.js'; import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; import { ICodeEditor } from '../../../../../browser/editorBrowser.js'; -import { observableCodeEditor } from '../../../../../browser/observableCodeEditor.js'; +import { ObservableCodeEditor, observableCodeEditor } from '../../../../../browser/observableCodeEditor.js'; import { EditorOption } from '../../../../../common/config/editorOptions.js'; import { LineRange } from '../../../../../common/core/lineRange.js'; import { Position } from '../../../../../common/core/position.js'; @@ -17,45 +19,76 @@ import { AbstractText, SingleTextEdit, StringText } from '../../../../../common/ import { TextLength } from '../../../../../common/core/textLength.js'; import { DetailedLineRangeMapping, lineRangeMappingFromRangeMappings, RangeMapping } from '../../../../../common/diff/rangeMapping.js'; import { TextModel } from '../../../../../common/model/textModel.js'; -import { InlineCompletionsModel } from '../../model/inlineCompletionsModel.js'; import { InlineEditsGutterIndicator } from './components/gutterIndicatorView.js'; -import { IInlineEditsIndicatorState, InlineEditsIndicator } from './components/indicatorView.js'; +import { InlineEditsIndicator } from './components/indicatorView.js'; import { InlineEditWithChanges } from './inlineEditWithChanges.js'; -import { IInlineEditsViewHost } from './inlineEditsViewInterface.js'; +import { GhostTextIndicator, InlineEditModel } from './inlineEditsModel.js'; +import { IInlineEditModel, InlineEditTabAction } from './inlineEditsViewInterface.js'; import { InlineEditsDeletionView } from './inlineEditsViews/inlineEditsDeletionView.js'; import { InlineEditsInsertionView } from './inlineEditsViews/inlineEditsInsertionView.js'; import { InlineEditsLineReplacementView } from './inlineEditsViews/inlineEditsLineReplacementView.js'; import { InlineEditsSideBySideView } from './inlineEditsViews/inlineEditsSideBySideView.js'; import { InlineEditsWordReplacementView } from './inlineEditsViews/inlineEditsWordReplacementView.js'; import { IOriginalEditorInlineDiffViewState, OriginalEditorInlineDiffView } from './inlineEditsViews/originalEditorInlineDiffView.js'; -import { applyEditToModifiedRangeMappings, createReindentEdit, InlineEditTabAction } from './utils/utils.js'; +import { applyEditToModifiedRangeMappings, createReindentEdit } from './utils/utils.js'; import './view.css'; export class InlineEditsView extends Disposable { - private readonly _editorObs = observableCodeEditor(this._editor); + private readonly _editorObs: ObservableCodeEditor = observableCodeEditor(this._editor); - private readonly _useMixedLinesDiff = this._editorObs.getOption(EditorOption.inlineSuggest).map(s => s.edits.useMixedLinesDiff); - private readonly _useInterleavedLinesDiff = this._editorObs.getOption(EditorOption.inlineSuggest).map(s => s.edits.useInterleavedLinesDiff); - private readonly _useCodeShifting = this._editorObs.getOption(EditorOption.inlineSuggest).map(s => s.edits.codeShifting); - private readonly _renderSideBySide = this._editorObs.getOption(EditorOption.inlineSuggest).map(s => s.edits.renderSideBySide); - private readonly _showCollapsed = this._editorObs.getOption(EditorOption.inlineSuggest).map(s => s.edits.showCollapsed); - private readonly _useMultiLineGhostText = this._editorObs.getOption(EditorOption.inlineSuggest).map(s => s.edits.useMultiLineGhostText); + private readonly _useCodeShifting; + private readonly _renderSideBySide; + private readonly _useMultiLineGhostText; + + private readonly _tabAction = derived(reader => this._model.read(reader)?.tabAction.read(reader) ?? InlineEditTabAction.Inactive); private _previousView: { id: string; view: ReturnType; - userJumpedToIt: boolean; editorWidth: number; + timestamp: number; } | undefined; constructor( private readonly _editor: ICodeEditor, - private readonly _edit: IObservable, - private readonly _model: IObservable, + private readonly _model: IObservable, + private readonly _ghostTextIndicator: IObservable, private readonly _focusIsInMenu: ISettableObservable, @IInstantiationService private readonly _instantiationService: IInstantiationService, ) { super(); + + this._useCodeShifting = this._editorObs.getOption(EditorOption.inlineSuggest).map(s => s.edits.allowCodeShifting); + this._renderSideBySide = this._editorObs.getOption(EditorOption.inlineSuggest).map(s => s.edits.renderSideBySide); + this._useMultiLineGhostText = this._editorObs.getOption(EditorOption.inlineSuggest).map(s => s.edits.useMultiLineGhostText); + + this._register(autorunWithStore((reader, store) => { + const model = this._model.read(reader); + if (!model) { + return; + } + + store.add( + Event.any( + this._sideBySide.onDidClick, + this._deletion.onDidClick, + this._lineReplacementView.onDidClick, + this._insertion.onDidClick, + ...this._wordReplacementViews.read(reader).map(w => w.onDidClick), + this._inlineDiffView.onDidClick, + )(e => { + if (this._viewHasBeenShownLongerThan(350)) { + e.preventDefault(); + model.accept(); + } + }) + ); + })); + + this._indicator.recomputeInitiallyAndOnChange(this._store); + this._wordReplacementViews.recomputeInitiallyAndOnChange(this._store); + + this._indicatorCyclicDependencyCircuitBreaker.set(true, undefined); } private readonly _uiState = derived<{ @@ -66,35 +99,36 @@ export class InlineEditsView extends Disposable { newTextLineCount: number; originalDisplayRange: LineRange; } | undefined>(this, reader => { - const edit = this._edit.read(reader); - if (!edit) { + const model = this._model.read(reader); + if (!model) { return undefined; } - this._model.get()?.handleInlineCompletionShown(edit.inlineCompletion); + model.handleInlineEditShown(); - let mappings = RangeMapping.fromEdit(edit.edit); - let newText = edit.edit.apply(edit.originalText); - let diff = lineRangeMappingFromRangeMappings(mappings, edit.originalText, new StringText(newText)); + const inlineEdit = model.inlineEdit; + let mappings = RangeMapping.fromEdit(inlineEdit.edit); + let newText = inlineEdit.edit.apply(inlineEdit.originalText); + let diff = lineRangeMappingFromRangeMappings(mappings, inlineEdit.originalText, new StringText(newText)); - const originalDisplayRange = edit.originalText.lineRange.intersect( - edit.originalLineRange.join( - LineRange.ofLength(edit.originalLineRange.startLineNumber, edit.lineEdit.newLines.length) + const originalDisplayRange = inlineEdit.originalText.lineRange.intersect( + inlineEdit.originalLineRange.join( + LineRange.ofLength(inlineEdit.originalLineRange.startLineNumber, inlineEdit.lineEdit.newLines.length) ) )!; - let state = this.determineRenderState(edit, reader, diff, new StringText(newText), originalDisplayRange); + let state = this.determineRenderState(model, reader, diff, new StringText(newText), originalDisplayRange); if (!state) { - this._model.get()?.stop(); + model.abort(`unable to determine view: tried to render ${this._previousView?.view}`); return undefined; } if (state.kind === 'sideBySide') { - const indentationAdjustmentEdit = createReindentEdit(newText, edit.modifiedLineRange); + const indentationAdjustmentEdit = createReindentEdit(newText, inlineEdit.modifiedLineRange); newText = indentationAdjustmentEdit.applyToString(newText); mappings = applyEditToModifiedRangeMappings(mappings, indentationAdjustmentEdit); - diff = lineRangeMappingFromRangeMappings(mappings, edit.originalText, new StringText(newText)); + diff = lineRangeMappingFromRangeMappings(mappings, inlineEdit.originalText, new StringText(newText)); } this._previewTextModel.setLanguage(this._editor.getModel()!.getLanguageId()); @@ -105,16 +139,16 @@ export class InlineEditsView extends Disposable { this._previewTextModel.setValue(newText); } - if (this._showCollapsed.read(reader) && this._host.tabAction.read(reader) !== InlineEditTabAction.Accept && !this._indicator.read(reader).isHoverVisible.read(reader) && !this._model.get()!.inAcceptFlow.read(reader)) { + if (model.showCollapsed.read(reader) && !this._indicator.read(reader)?.isHoverVisible.read(reader)) { state = { kind: 'hidden' }; } return { state, diff, - edit, + edit: inlineEdit, newText, - newTextLineCount: edit.modifiedLineRange.length, + newTextLineCount: inlineEdit.modifiedLineRange.length, originalDisplayRange: originalDisplayRange, }; }); @@ -127,50 +161,88 @@ export class InlineEditsView extends Disposable { null )); - // TODO: This has become messy, should it be passed in to the InlineEditsView? Maybe include in accept flow? - private readonly _host: IInlineEditsViewHost = { - displayName: derivedObservableWithCache(this, (reader, previousDisplayName) => { - const state = this._model.read(reader)?.inlineEditState; - const item = state?.read(reader); - const completionSource = item?.inlineCompletion?.source; - // TODO: expose the provider (typed) and expose the provider the edit belongs to typing and get correct edit - return (completionSource?.inlineCompletions as any)?.edits?.[0]?.provider?.displayName ?? previousDisplayName ?? localize('inlineEdit', "Inline Edit"); - }), - tabAction: derived(this, reader => { - const m = this._model.read(reader); - if (this._editorObs.isFocused.read(reader)) { - if (m && m.tabShouldJumpToInlineEdit.read(reader)) { return InlineEditTabAction.Jump; } - if (m && m.tabShouldAcceptInlineEdit.read(reader)) { return InlineEditTabAction.Accept; } + private readonly _indicatorCyclicDependencyCircuitBreaker = observableValue(this, false); + + protected readonly _indicator = derivedWithStore(this, (reader, store) => { + if (!this._indicatorCyclicDependencyCircuitBreaker.read(reader)) { + return undefined; + } + + const indicatorDisplayRange = derivedOpts({ owner: this, equalsFn: equalsIfDefined(itemEquals()) }, reader => { + const ghostTextIndicator = this._ghostTextIndicator.read(reader); + if (ghostTextIndicator) { + return ghostTextIndicator.lineRange; + } + + const state = this._uiState.read(reader); + if (state?.state?.kind === 'insertionMultiLine') { + return this._insertion.originalLines.read(reader); + } + return state?.originalDisplayRange; + }); + + const modelWithGhostTextSupport = derived(this, reader => { + const model = this._model.read(reader); + if (model) { + return model; + } + + const ghostTextIndicator = this._ghostTextIndicator.read(reader); + if (ghostTextIndicator) { + return ghostTextIndicator.model; } - return InlineEditTabAction.Inactive; - }), - action: this._model.map((m, r) => m?.state.read(r)?.inlineCompletion?.inlineCompletion.action), - extensionCommands: this._model.map((m, r) => m?.state.read(r)?.inlineCompletion?.source.inlineCompletions.commands ?? []), - accept: () => { - this._model.get()?.accept(); + + return model; + }); + + return store.add(this._instantiationService.createInstance( + InlineEditsGutterIndicator, + this._editorObs, + indicatorDisplayRange, + this._gutterIndicatorOffset, + modelWithGhostTextSupport, + this._inlineEditsIsHovered, + this._focusIsInMenu, + )); + }); + + private readonly _inlineEditsIsHovered = derived(this, reader => { + return this._sideBySide.isHovered.read(reader) + || this._wordReplacementViews.read(reader).some(v => v.isHovered.read(reader)) + || this._deletion.isHovered.read(reader) + || this._inlineDiffView.isHovered.read(reader) + || this._lineReplacementView.isHovered.read(reader) + || this._insertion.isHovered.read(reader); + }); + + private readonly _gutterIndicatorOffset = derived(this, reader => { + // TODO: have a better way to tell the gutter indicator view where the edit is inside a viewzone + if (this._uiState.read(reader)?.state?.kind === 'insertionMultiLine') { + return this._insertion.startLineOffset.read(reader); } - }; + return 0; + }); private readonly _sideBySide = this._register(this._instantiationService.createInstance(InlineEditsSideBySideView, this._editor, - this._edit, + this._model.map(m => m?.inlineEdit), this._previewTextModel, this._uiState.map(s => s && s.state?.kind === 'sideBySide' ? ({ edit: s.edit, newTextLineCount: s.newTextLineCount, originalDisplayRange: s.originalDisplayRange, }) : undefined), - this._host, + this._tabAction, )); protected readonly _deletion = this._register(this._instantiationService.createInstance(InlineEditsDeletionView, this._editor, - this._edit, + this._model.map(m => m?.inlineEdit), this._uiState.map(s => s && s.state?.kind === 'deletion' ? ({ originalRange: s.state.originalRange, deletions: s.state.deletions, }) : undefined), - this._host, + this._tabAction, )); protected readonly _insertion = this._register(this._instantiationService.createInstance(InlineEditsInsertionView, @@ -180,7 +252,7 @@ export class InlineEditsView extends Disposable { startColumn: s.state.column, text: s.state.text, }) : undefined), - this._host, + this._tabAction, )); private readonly _inlineDiffViewState = derived(this, reader => { @@ -197,11 +269,11 @@ export class InlineEditsView extends Disposable { }; }); - protected readonly _inlineDiffView = this._register(new OriginalEditorInlineDiffView(this._editor, this._inlineDiffViewState, this._previewTextModel, this._host)); + protected readonly _inlineDiffView = this._register(new OriginalEditorInlineDiffView(this._editor, this._inlineDiffViewState, this._previewTextModel)); protected readonly _wordReplacementViews = mapObservableArrayCached(this, this._uiState.map(s => s?.state?.kind === 'wordReplacements' ? s.state.replacements : []), (e, store) => { - return store.add(this._instantiationService.createInstance(InlineEditsWordReplacementView, this._editorObs, e, [e], this._host)); - }).recomputeInitiallyAndOnChange(this._store); + return store.add(this._instantiationService.createInstance(InlineEditsWordReplacementView, this._editorObs, e, [e], this._tabAction)); + }); protected readonly _lineReplacementView = this._register(this._instantiationService.createInstance(InlineEditsLineReplacementView, this._editorObs, @@ -211,78 +283,29 @@ export class InlineEditsView extends Disposable { modifiedLines: s.state.modifiedLines, replacements: s.state.replacements, }) : undefined), - this._host + this._tabAction, )); - private readonly _useGutterIndicator = observableCodeEditor(this._editor).getOption(EditorOption.inlineSuggest).map(s => s.edits.useGutterIndicator); - - private readonly _inlineEditsIsHovered = derived(this, reader => { - return this._sideBySide.isHovered.read(reader) - || this._wordReplacementViews.read(reader).some(v => v.isHovered.read(reader)) - || this._deletion.isHovered.read(reader) - || this._inlineDiffView.isHovered.read(reader) - || this._lineReplacementView.isHovered.read(reader); - }); - - private readonly _gutterIndicatorOffset = derived(this, reader => { - // TODO: have a better way to tell the gutter indicator view where the edit is inside a viewzone - if (this._uiState.read(reader)?.state?.kind === 'insertionMultiLine') { - return this._insertion.startLineOffset.read(reader); + private getCacheId(model: IInlineEditModel) { + const inlineEdit = model.inlineEdit; + if (model.inPartialAcceptFlow.get()) { + return `${inlineEdit.inlineCompletion.id}_${inlineEdit.edit.edits.map(innerEdit => innerEdit.range.toString() + innerEdit.text).join(',')}`; } - return 0; - }); - - protected readonly _indicator = derivedWithStore(this, (reader, store) => { - const indicatorDisplayRange = derived(this, reader => { - const state = this._uiState.read(reader); - if (state?.state?.kind === 'insertionMultiLine') { - return this._insertion.originalLines.read(reader); - } - return state?.originalDisplayRange; - }); - - if (this._useGutterIndicator.read(reader)) { - return store.add(this._instantiationService.createInstance( - InlineEditsGutterIndicator, - this._editorObs, - indicatorDisplayRange, - this._gutterIndicatorOffset, - this._model, - this._host, - this._inlineEditsIsHovered, - this._focusIsInMenu, - )); - } else { - return store.add(new InlineEditsIndicator( - this._editorObs, - derived(reader => { - const state = this._uiState.read(reader); - const range = indicatorDisplayRange.read(reader); - if (!state || !state.state || !range) { return undefined; } - const top = this._editor.getTopForLineNumber(range.startLineNumber) - this._editorObs.scrollTop.read(reader) + this._gutterIndicatorOffset.read(reader); - return { editTop: top, showAlways: state.state.kind !== 'sideBySide' }; - }), - this._model, - )); - } - }).recomputeInitiallyAndOnChange(this._store); + return inlineEdit.inlineCompletion.id; + } - private determineView(edit: InlineEditWithChanges, reader: IReader, diff: DetailedLineRangeMapping[], newText: StringText, originalDisplayRange: LineRange): string { + private determineView(model: IInlineEditModel, reader: IReader, diff: DetailedLineRangeMapping[], newText: StringText, originalDisplayRange: LineRange): string { // Check if we can use the previous view if it is the same InlineCompletion as previously shown - const canUseCache = this._previousView?.id === edit.inlineCompletion.id; - const reconsiderViewAfterJump = edit.userJumpedToIt !== this._previousView?.userJumpedToIt && - ( - (this._useMixedLinesDiff.read(reader) === 'afterJumpWhenPossible' && this._previousView?.view !== 'mixedLines') || - (this._useInterleavedLinesDiff.read(reader) === 'afterJump' && this._previousView?.view !== 'interleavedLines') - ); + const inlineEdit = model.inlineEdit; + const canUseCache = this._previousView?.id === this.getCacheId(model); const reconsiderViewEditorWidthChange = this._previousView?.editorWidth !== this._editorObs.layoutInfoWidth.read(reader) && ( this._previousView?.view === 'sideBySide' || this._previousView?.view === 'lineReplacement' ); - if (canUseCache && !reconsiderViewAfterJump && !reconsiderViewEditorWidthChange) { + if (canUseCache && !reconsiderViewEditorWidthChange) { return this._previousView!.view; } @@ -291,68 +314,54 @@ export class InlineEditsView extends Disposable { const inner = diff.flatMap(d => d.innerChanges ?? []); const isSingleInnerEdit = inner.length === 1; if ( - isSingleInnerEdit && ( - this._useMixedLinesDiff.read(reader) === 'forStableInsertions' - && this._useCodeShifting.read(reader) - && isSingleLineInsertionAfterPosition(diff, edit.cursorPosition) - ) + isSingleInnerEdit + && this._useCodeShifting.read(reader) !== 'never' + && isSingleLineInsertionAfterPosition(diff, inlineEdit.cursorPosition) ) { return 'insertionInline'; } - const innerValues = inner.map(m => ({ original: edit.originalText.getValueOfRange(m.originalRange), modified: newText.getValueOfRange(m.modifiedRange) })); + const innerValues = inner.map(m => ({ original: inlineEdit.originalText.getValueOfRange(m.originalRange), modified: newText.getValueOfRange(m.modifiedRange) })); if (innerValues.every(({ original, modified }) => modified.trim() === '' && original.length > 0 && (original.length > modified.length || original.trim() !== ''))) { return 'deletion'; } - if (isSingleMultiLineInsertion(diff) && this._useMultiLineGhostText.read(reader) && this._useCodeShifting.read(reader)) { + if (isSingleMultiLineInsertion(diff) && this._useMultiLineGhostText.read(reader) && this._useCodeShifting.read(reader) === 'always') { return 'insertionMultiLine'; } - const numOriginalLines = edit.originalLineRange.length; - const numModifiedLines = edit.modifiedLineRange.length; + const numOriginalLines = inlineEdit.originalLineRange.length; + const numModifiedLines = inlineEdit.modifiedLineRange.length; const allInnerChangesNotTooLong = inner.every(m => TextLength.ofRange(m.originalRange).columnCount < InlineEditsWordReplacementView.MAX_LENGTH && TextLength.ofRange(m.modifiedRange).columnCount < InlineEditsWordReplacementView.MAX_LENGTH); if (allInnerChangesNotTooLong && isSingleInnerEdit && numOriginalLines === 1 && numModifiedLines === 1) { // Make sure there is no insertion, even if we grow them if ( !inner.some(m => m.originalRange.isEmpty()) || - !growEditsUntilWhitespace(inner.map(m => new SingleTextEdit(m.originalRange, '')), edit.originalText).some(e => e.range.isEmpty() && TextLength.ofRange(e.range).columnCount < InlineEditsWordReplacementView.MAX_LENGTH) + !growEditsUntilWhitespace(inner.map(m => new SingleTextEdit(m.originalRange, '')), inlineEdit.originalText).some(e => e.range.isEmpty() && TextLength.ofRange(e.range).columnCount < InlineEditsWordReplacementView.MAX_LENGTH) ) { return 'wordReplacements'; } } if (numOriginalLines > 0 && numModifiedLines > 0) { - if (this._renderSideBySide.read(reader) !== 'never' && InlineEditsSideBySideView.fitsInsideViewport(this._editor, edit, originalDisplayRange, reader)) { + if (this._renderSideBySide.read(reader) !== 'never' && InlineEditsSideBySideView.fitsInsideViewport(this._editor, inlineEdit, originalDisplayRange, reader)) { return 'sideBySide'; } return 'lineReplacement'; } - if ( - (this._useMixedLinesDiff.read(reader) === 'whenPossible' || (edit.userJumpedToIt && this._useMixedLinesDiff.read(reader) === 'afterJumpWhenPossible')) - && diff.every(m => OriginalEditorInlineDiffView.supportsInlineDiffRendering(m)) - ) { - return 'mixedLines'; - } - - if (this._useInterleavedLinesDiff.read(reader) === 'always' || (edit.userJumpedToIt && this._useInterleavedLinesDiff.read(reader) === 'afterJump')) { - return 'interleavedLines'; - } - return 'sideBySide'; } - private determineRenderState(edit: InlineEditWithChanges, reader: IReader, diff: DetailedLineRangeMapping[], newText: StringText, originalDisplayRange: LineRange) { + private determineRenderState(model: IInlineEditModel, reader: IReader, diff: DetailedLineRangeMapping[], newText: StringText, originalDisplayRange: LineRange) { + const inlineEdit = model.inlineEdit; - const view = this.determineView(edit, reader, diff, newText, originalDisplayRange); + const view = this.determineView(model, reader, diff, newText, originalDisplayRange); - this._previousView = { id: edit.inlineCompletion.id, view, userJumpedToIt: edit.userJumpedToIt, editorWidth: this._editor.getLayoutInfo().width }; + this._previousView = { id: this.getCacheId(model), view, editorWidth: this._editor.getLayoutInfo().width, timestamp: Date.now() }; switch (view) { case 'insertionInline': return { kind: 'insertionInline' as const }; - case 'mixedLines': return { kind: 'mixedLines' as const }; - case 'interleavedLines': return { kind: 'interleavedLines' as const }; case 'sideBySide': return { kind: 'sideBySide' as const }; case 'hidden': return { kind: 'hidden' as const }; } @@ -362,7 +371,7 @@ export class InlineEditsView extends Disposable { if (view === 'deletion') { return { kind: 'deletion' as const, - originalRange: edit.originalLineRange, + originalRange: inlineEdit.originalLineRange, deletions: inner.map(m => m.originalRange), }; } @@ -383,10 +392,10 @@ export class InlineEditsView extends Disposable { } if (view === 'wordReplacements') { - let grownEdits = growEditsToEntireWord(replacements, edit.originalText); + let grownEdits = growEditsToEntireWord(replacements, inlineEdit.originalText); if (grownEdits.some(e => e.range.isEmpty())) { - grownEdits = growEditsUntilWhitespace(replacements, edit.originalText); + grownEdits = growEditsUntilWhitespace(replacements, inlineEdit.originalText); } return { @@ -398,15 +407,25 @@ export class InlineEditsView extends Disposable { if (view === 'lineReplacement') { return { kind: 'lineReplacement' as const, - originalRange: edit.originalLineRange, - modifiedRange: edit.modifiedLineRange, - modifiedLines: edit.modifiedLineRange.mapToLineArray(line => newText.getLineAt(line)), + originalRange: inlineEdit.originalLineRange, + modifiedRange: inlineEdit.modifiedLineRange, + modifiedLines: inlineEdit.modifiedLineRange.mapToLineArray(line => newText.getLineAt(line)), replacements: inner.map(m => ({ originalRange: m.originalRange, modifiedRange: m.modifiedRange })), }; } return undefined; } + + private _viewHasBeenShownLongerThan(durationMs: number): boolean { + const viewCreationTime = this._previousView?.timestamp; + if (!viewCreationTime) { + throw new BugIndicatingError('viewHasBeenShownLongThan called before a view has been shown'); + } + + const currentTime = Date.now(); + return (currentTime - viewCreationTime) >= durationMs; + } } function isSingleLineInsertionAfterPosition(diff: DetailedLineRangeMapping[], position: Position | null) { diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViewInterface.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViewInterface.ts index 7ff746f47f22..a00fc94d390b 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViewInterface.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViewInterface.ts @@ -3,18 +3,35 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { IMouseEvent } from '../../../../../../base/browser/mouseEvent.js'; +import { Event } from '../../../../../../base/common/event.js'; import { IObservable } from '../../../../../../base/common/observable.js'; import { Command } from '../../../../../common/languages.js'; -import { InlineEditTabAction } from './utils/utils.js'; +import { InlineEditWithChanges } from './inlineEditWithChanges.js'; + +export enum InlineEditTabAction { + Jump = 'jump', + Accept = 'accept', + Inactive = 'inactive' +} export interface IInlineEditsView { isHovered: IObservable; + onDidClick: Event; } -export interface IInlineEditsViewHost { - displayName: IObservable; - action: IObservable; +export interface IInlineEditModel { + displayName: string; + action: Command | undefined; + extensionCommands: Command[]; + inlineEdit: InlineEditWithChanges; + tabAction: IObservable; - extensionCommands: IObservable; + inAcceptFlow: IObservable; + inPartialAcceptFlow: IObservable; + + handleInlineEditShown(): void; accept(): void; + jump(): void; + abort(reason: string): void; } diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/viewAndDiffProducer.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViewProducer.ts similarity index 54% rename from src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/viewAndDiffProducer.ts rename to src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViewProducer.ts index 232fda504581..c20a1bcf546f 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/viewAndDiffProducer.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViewProducer.ts @@ -8,17 +8,23 @@ import { Disposable } from '../../../../../../base/common/lifecycle.js'; import { derived, IObservable, ISettableObservable } from '../../../../../../base/common/observable.js'; import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; import { ICodeEditor } from '../../../../../browser/editorBrowser.js'; +import { ObservableCodeEditor, observableCodeEditor } from '../../../../../browser/observableCodeEditor.js'; +import { LineRange } from '../../../../../common/core/lineRange.js'; import { Range } from '../../../../../common/core/range.js'; import { SingleTextEdit, TextEdit } from '../../../../../common/core/textEdit.js'; import { TextModelText } from '../../../../../common/model/textModelText.js'; import { InlineCompletionsModel } from '../../model/inlineCompletionsModel.js'; import { InlineEdit } from '../../model/inlineEdit.js'; import { InlineEditWithChanges } from './inlineEditWithChanges.js'; +import { GhostTextIndicator, InlineEditModel } from './inlineEditsModel.js'; import { InlineEditsView } from './inlineEditsView.js'; +import { InlineEditTabAction } from './inlineEditsViewInterface.js'; export class InlineEditsViewAndDiffProducer extends Disposable { // TODO: This class is no longer a diff producer. Rename it or get rid of it public static readonly hot = createHotClass(InlineEditsViewAndDiffProducer); + private readonly _editorObs: ObservableCodeEditor; + private readonly _inlineEdit = derived(this, (reader) => { const model = this._model.read(reader); if (!model) { return undefined; } @@ -30,7 +36,8 @@ export class InlineEditsViewAndDiffProducer extends Disposable { // TODO: This c const editOffset = model.inlineEditState.get()?.inlineCompletion.updatedEdit.read(reader); if (!editOffset) { return undefined; } - const edits = editOffset.edits.map(e => { + const offsetEdits = model.inPartialAcceptFlow.read(reader) ? [editOffset.edits[0]] : editOffset.edits; + const edits = offsetEdits.map(e => { const innerEditRange = Range.fromPositions( textModel.getPositionAt(e.replaceRange.start), textModel.getPositionAt(e.replaceRange.endExclusive) @@ -41,7 +48,41 @@ export class InlineEditsViewAndDiffProducer extends Disposable { // TODO: This c const diffEdits = new TextEdit(edits); const text = new TextModelText(textModel); - return new InlineEditWithChanges(text, diffEdits, model.primaryPosition.get(), inlineEdit.renderExplicitly, inlineEdit.commands, inlineEdit.inlineCompletion); + return new InlineEditWithChanges(text, diffEdits, model.primaryPosition.get(), inlineEdit.commands, inlineEdit.inlineCompletion); + }); + + private readonly _inlineEditModel = derived(this, reader => { + const model = this._model.read(reader); + if (!model) { return undefined; } + const edit = this._inlineEdit.read(reader); + if (!edit) { return undefined; } + + const tabAction = derived(this, reader => { + if (this._editorObs.isFocused.read(reader)) { + if (model.tabShouldJumpToInlineEdit.read(reader)) { return InlineEditTabAction.Jump; } + if (model.tabShouldAcceptInlineEdit.read(reader)) { return InlineEditTabAction.Accept; } + } + return InlineEditTabAction.Inactive; + }); + + return new InlineEditModel(model, edit, tabAction); + }); + + private readonly _ghostTextIndicator = derived(this, reader => { + const model = this._model.read(reader); + if (!model) { return undefined; } + const state = model.inlineCompletionState.read(reader); + if (!state) { return undefined; } + const inlineCompletion = state.inlineCompletion; + if (!inlineCompletion) { return undefined; } + + if (!inlineCompletion.sourceInlineCompletion.showInlineEditMenu) { + return undefined; + } + + const lineRange = LineRange.ofLength(state.primaryGhostText.lineNumber, 1); + + return new GhostTextIndicator(this._editor, model, lineRange, inlineCompletion); }); constructor( @@ -49,10 +90,12 @@ export class InlineEditsViewAndDiffProducer extends Disposable { // TODO: This c private readonly _edit: IObservable, private readonly _model: IObservable, private readonly _focusIsInMenu: ISettableObservable, - @IInstantiationService private readonly _instantiationService: IInstantiationService, + @IInstantiationService instantiationService: IInstantiationService, ) { super(); - this._register(this._instantiationService.createInstance(InlineEditsView, this._editor, this._inlineEdit, this._model, this._focusIsInMenu)); + this._editorObs = observableCodeEditor(this._editor); + + this._register(instantiationService.createInstance(InlineEditsView, this._editor, this._inlineEditModel, this._ghostTextIndicator, this._focusIsInMenu)); } } diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsDeletionView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsDeletionView.ts index a0d6b6caff08..9feed6f4a6bd 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsDeletionView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsDeletionView.ts @@ -3,22 +3,33 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { n } from '../../../../../../../base/browser/dom.js'; +import { IMouseEvent } from '../../../../../../../base/browser/mouseEvent.js'; +import { Emitter } from '../../../../../../../base/common/event.js'; import { Disposable } from '../../../../../../../base/common/lifecycle.js'; import { constObservable, derived, derivedObservableWithCache, IObservable } from '../../../../../../../base/common/observable.js'; import { asCssVariable } from '../../../../../../../platform/theme/common/colorUtils.js'; import { ICodeEditor } from '../../../../../../browser/editorBrowser.js'; -import { observableCodeEditor } from '../../../../../../browser/observableCodeEditor.js'; +import { ObservableCodeEditor, observableCodeEditor } from '../../../../../../browser/observableCodeEditor.js'; import { Point } from '../../../../../../browser/point.js'; import { LineRange } from '../../../../../../common/core/lineRange.js'; import { Position } from '../../../../../../common/core/position.js'; import { Range } from '../../../../../../common/core/range.js'; -import { IInlineEditsView, IInlineEditsViewHost } from '../inlineEditsViewInterface.js'; +import { IInlineEditsView, InlineEditTabAction } from '../inlineEditsViewInterface.js'; import { InlineEditWithChanges } from '../inlineEditWithChanges.js'; import { getOriginalBorderColor, originalBackgroundColor } from '../theme.js'; import { createRectangle, getPrefixTrim, mapOutFalsy, maxContentWidthInRange } from '../utils/utils.js'; export class InlineEditsDeletionView extends Disposable implements IInlineEditsView { - private readonly _editorObs = observableCodeEditor(this._editor); + + private readonly _onDidClick = this._register(new Emitter()); + readonly onDidClick = this._onDidClick.event; + + private readonly _editorObs: ObservableCodeEditor; + + private readonly _originalVerticalStartPosition: IObservable; + private readonly _originalVerticalEndPosition: IObservable; + + private readonly _originalDisplayRange: IObservable; constructor( private readonly _editor: ICodeEditor, @@ -27,10 +38,26 @@ export class InlineEditsDeletionView extends Disposable implements IInlineEditsV originalRange: LineRange; deletions: Range[]; } | undefined>, - private readonly _host: IInlineEditsViewHost, + private readonly _tabAction: IObservable, ) { super(); + this._editorObs = observableCodeEditor(this._editor); + + const originalStartPosition = derived(this, (reader) => { + const inlineEdit = this._edit.read(reader); + return inlineEdit ? new Position(inlineEdit.originalLineRange.startLineNumber, 1) : null; + }); + + const originalEndPosition = derived(this, (reader) => { + const inlineEdit = this._edit.read(reader); + return inlineEdit ? new Position(inlineEdit.originalLineRange.endLineNumberExclusive, 1) : null; + }); + + this._originalDisplayRange = this._uiState.map(s => s?.originalRange); + this._originalVerticalStartPosition = this._editorObs.observePosition(originalStartPosition, this._store).map(p => p?.y); + this._originalVerticalEndPosition = this._editorObs.observePosition(originalEndPosition, this._store).map(p => p?.y); + this._register(this._editorObs.createOverlayWidget({ domNode: this._nonOverflowView.element, position: constObservable(null), @@ -45,20 +72,6 @@ export class InlineEditsDeletionView extends Disposable implements IInlineEditsV private readonly _display = derived(this, reader => !!this._uiState.read(reader) ? 'block' : 'none'); - private readonly _originalStartPosition = derived(this, (reader) => { - const inlineEdit = this._edit.read(reader); - return inlineEdit ? new Position(inlineEdit.originalLineRange.startLineNumber, 1) : null; - }); - - private readonly _originalEndPosition = derived(this, (reader) => { - const inlineEdit = this._edit.read(reader); - return inlineEdit ? new Position(inlineEdit.originalLineRange.endLineNumberExclusive, 1) : null; - }); - - private readonly _originalVerticalStartPosition = this._editorObs.observePosition(this._originalStartPosition, this._store).map(p => p?.y); - private readonly _originalVerticalEndPosition = this._editorObs.observePosition(this._originalEndPosition, this._store).map(p => p?.y); - - private readonly _originalDisplayRange = this._uiState.map(s => s?.originalRange); private readonly _editorMaxContentWidthInRange = derived(this, reader => { const originalDisplayRange = this._originalDisplayRange.read(reader); if (!originalDisplayRange) { @@ -146,7 +159,7 @@ export class InlineEditsDeletionView extends Disposable implements IInlineEditsV { hideLeft: layoutInfo.horizontalScrollOffset !== 0 } ); - const originalBorderColor = getOriginalBorderColor(this._host.tabAction).read(reader); + const originalBorderColor = getOriginalBorderColor(this._tabAction).read(reader); return [ n.svgElem('path', { diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsInsertionView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsInsertionView.ts index 0815f76a3ae2..bb20b7d863ed 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsInsertionView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsInsertionView.ts @@ -3,13 +3,15 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { $, n } from '../../../../../../../base/browser/dom.js'; +import { IMouseEvent } from '../../../../../../../base/browser/mouseEvent.js'; +import { Emitter } from '../../../../../../../base/common/event.js'; import { Disposable } from '../../../../../../../base/common/lifecycle.js'; import { constObservable, derived, derivedWithStore, IObservable, observableValue } from '../../../../../../../base/common/observable.js'; import { IInstantiationService } from '../../../../../../../platform/instantiation/common/instantiation.js'; import { asCssVariable } from '../../../../../../../platform/theme/common/colorUtils.js'; import { ICodeEditor } from '../../../../../../browser/editorBrowser.js'; -import { observableCodeEditor } from '../../../../../../browser/observableCodeEditor.js'; -import { Point } from '../../../../../../browser/point.js'; +import { ObservableCodeEditor, observableCodeEditor } from '../../../../../../browser/observableCodeEditor.js'; +import { Rect } from '../../../../../../browser/rect.js'; import { LineSource, renderLines, RenderOptions } from '../../../../../../browser/widget/diffEditor/components/diffEditorViewZones/renderLines.js'; import { EditorOption } from '../../../../../../common/config/editorOptions.js'; import { LineRange } from '../../../../../../common/core/lineRange.js'; @@ -21,12 +23,15 @@ import { TokenArray } from '../../../../../../common/tokens/tokenArray.js'; import { InlineDecoration, InlineDecorationType } from '../../../../../../common/viewModel.js'; import { GhostText, GhostTextPart } from '../../../model/ghostText.js'; import { GhostTextView } from '../../ghostText/ghostTextView.js'; -import { IInlineEditsView, IInlineEditsViewHost } from '../inlineEditsViewInterface.js'; +import { IInlineEditsView, InlineEditTabAction } from '../inlineEditsViewInterface.js'; import { getModifiedBorderColor, modifiedChangedLineBackgroundColor } from '../theme.js'; import { createRectangle, getPrefixTrim, mapOutFalsy } from '../utils/utils.js'; export class InlineEditsInsertionView extends Disposable implements IInlineEditsView { - private readonly _editorObs = observableCodeEditor(this._editor); + private readonly _editorObs: ObservableCodeEditor; + + private readonly _onDidClick = this._register(new Emitter()); + readonly onDidClick = this._onDidClick.event; private readonly _state = derived(this, reader => { const state = this._input.read(reader); @@ -53,7 +58,12 @@ export class InlineEditsInsertionView extends Disposable implements IInlineEdits const eol = textModel.getEOL(); const startsWithEol = state.text.startsWith(eol); const originalRange = new LineRange(state.lineNumber, state.lineNumber + (startsWithEol ? 0 : 1)); - const modifiedLines = state.text.split(eol).splice(startsWithEol ? 0 : 1); + let modifiedLines = state.text.split(eol); + if (startsWithEol) { + modifiedLines = modifiedLines.splice(1); + } else { + modifiedLines[0] = textModel.getLineContent(state.lineNumber) + modifiedLines[0]; + } return getPrefixTrim([], originalRange, modifiedLines, this._editor); }); @@ -76,17 +86,8 @@ export class InlineEditsInsertionView extends Disposable implements IInlineEdits return new GhostText(state.lineNumber, [new GhostTextPart(state.column, state.text, false, inlineDecorations)]); }); - protected readonly _ghostTextView = this._register(this._instantiationService.createInstance(GhostTextView, - this._editor, - { - ghostText: this._ghostText, - minReservedLineCount: constObservable(0), - targetTextModel: this._editorObs.model.map(model => model ?? undefined), - warning: constObservable(undefined), - }, - observableValue(this, { syntaxHighlightingEnabled: true, extraClasses: ['inline-edit'] }), - true, - )); + protected readonly _ghostTextView: GhostTextView; + readonly isHovered: IObservable; constructor( private readonly _editor: ICodeEditor, @@ -95,12 +96,33 @@ export class InlineEditsInsertionView extends Disposable implements IInlineEdits startColumn: number; text: string; } | undefined>, - private readonly _host: IInlineEditsViewHost, - @IInstantiationService private readonly _instantiationService: IInstantiationService, + private readonly _tabAction: IObservable, + @IInstantiationService instantiationService: IInstantiationService, @ILanguageService private readonly _languageService: ILanguageService, ) { super(); + this._editorObs = observableCodeEditor(this._editor); + + this._ghostTextView = this._register(instantiationService.createInstance(GhostTextView, + this._editor, + { + ghostText: this._ghostText, + minReservedLineCount: constObservable(0), + targetTextModel: this._editorObs.model.map(model => model ?? undefined), + warning: constObservable(undefined), + }, + observableValue(this, { syntaxHighlightingEnabled: true, extraClasses: ['inline-edit'] }), + true, + true + )); + + this.isHovered = this._ghostTextView.isHovered; + + this._register(this._ghostTextView.onDidClick((e) => { + this._onDidClick.fire(e); + })); + this._register(this._editorObs.createOverlayWidget({ domNode: this._nonOverflowView.element, position: constObservable(null), @@ -108,7 +130,7 @@ export class InlineEditsInsertionView extends Disposable implements IInlineEdits minContentWidthInPx: derived(reader => { const info = this._overlayLayout.read(reader); if (info === null) { return 0; } - return info.code1.x - info.codeStart1.x; + return info.minContentWidthRequired; }), })); } @@ -191,11 +213,12 @@ export class InlineEditsInsertionView extends Disposable implements IInlineEdits const editorLayout = this._editorObs.layoutInfo.read(reader); const horizontalScrollOffset = this._editorObs.scrollLeft.read(reader); + const verticalScrollbarWidth = this._editorObs.layoutInfoVerticalScrollbarWidth.read(reader); - const left = editorLayout.contentLeft + this._editorMaxContentWidthInRange.read(reader) - horizontalScrollOffset; - const prefixTrim = this._maxPrefixTrim.read(reader); - const codeLeft = editorLayout.contentLeft + (prefixTrim?.prefixLeftOffset ?? 0 /* fix due to observable bug? */); - if (left <= codeLeft) { + const right = editorLayout.contentLeft + this._editorMaxContentWidthInRange.read(reader) - horizontalScrollOffset; + const prefixLeftOffset = this._maxPrefixTrim.read(reader).prefixLeftOffset ?? 0 /* fix due to observable bug? */; + const left = editorLayout.contentLeft + prefixLeftOffset - horizontalScrollOffset; + if (right <= left) { return null; } @@ -206,19 +229,14 @@ export class InlineEditsInsertionView extends Disposable implements IInlineEdits const top = this._editor.getTopForLineNumber(state.lineNumber) - scrollTop + topTrim; const bottom = top + height; - const code1 = new Point(left, top); - const codeStart1 = new Point(codeLeft, top); - const code2 = new Point(left, bottom); - const codeStart2 = new Point(codeLeft, bottom); + const overlay = new Rect(left, top, right, bottom); return { - code1, - codeStart1, - code2, - codeStart2, - horizontalScrollOffset, - padding: 3, + overlay, + contentLeft: editorLayout.contentLeft, + minContentWidthRequired: prefixLeftOffset + overlay.width + verticalScrollbarWidth, borderRadius: 4, + padding: 3 }; }).recomputeInitiallyAndOnChange(this._store); @@ -230,19 +248,21 @@ export class InlineEditsInsertionView extends Disposable implements IInlineEdits if (!overlayLayoutObs) { return undefined; } const layoutInfo = overlayLayoutObs.read(reader); + const overlay = layoutInfo.overlay; + const croppedOverlay = new Rect(Math.max(overlay.left, layoutInfo.contentLeft), overlay.top, overlay.right, overlay.bottom); const rectangleOverlay = createRectangle( { - topLeft: layoutInfo.codeStart1, - width: layoutInfo.code1.x - layoutInfo.codeStart1.x + 1, - height: layoutInfo.code2.y - layoutInfo.code1.y + 1, + topLeft: croppedOverlay.getLeftTop(), + width: croppedOverlay.width + 1, + height: croppedOverlay.height + 1, }, layoutInfo.padding, layoutInfo.borderRadius, - { hideLeft: layoutInfo.horizontalScrollOffset !== 0 } + { hideLeft: croppedOverlay.left !== overlay.left } ); - const modifiedBorderColor = getModifiedBorderColor(this._host.tabAction).read(reader); + const modifiedBorderColor = getModifiedBorderColor(this._tabAction).read(reader); return [ n.svgElem('path', { @@ -270,6 +290,4 @@ export class InlineEditsInsertionView extends Disposable implements IInlineEdits }, [ [this._foregroundSvg], ]).keepUpdated(this._store); - - readonly isHovered = constObservable(false); } diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsLineReplacementView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsLineReplacementView.ts index 6b6bcd8d65c9..eb7460eeed27 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsLineReplacementView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsLineReplacementView.ts @@ -3,13 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { $, n } from '../../../../../../../base/browser/dom.js'; +import { $, getWindow, n } from '../../../../../../../base/browser/dom.js'; +import { IMouseEvent, StandardMouseEvent } from '../../../../../../../base/browser/mouseEvent.js'; +import { Emitter } from '../../../../../../../base/common/event.js'; import { Disposable, toDisposable } from '../../../../../../../base/common/lifecycle.js'; import { autorun, autorunDelta, constObservable, derived, IObservable } from '../../../../../../../base/common/observable.js'; import { editorBackground, scrollbarShadow } from '../../../../../../../platform/theme/common/colorRegistry.js'; import { asCssVariable } from '../../../../../../../platform/theme/common/colorUtils.js'; -import { IViewZoneChangeAccessor } from '../../../../../../browser/editorBrowser.js'; +import { IEditorMouseEvent, IViewZoneChangeAccessor } from '../../../../../../browser/editorBrowser.js'; +import { EditorMouseEvent } from '../../../../../../browser/editorDom.js'; import { ObservableCodeEditor } from '../../../../../../browser/observableCodeEditor.js'; +import { Point } from '../../../../../../browser/point.js'; import { Rect } from '../../../../../../browser/rect.js'; import { LineSource, renderLines, RenderOptions } from '../../../../../../browser/widget/diffEditor/components/diffEditorViewZones/renderLines.js'; import { EditorOption } from '../../../../../../common/config/editorOptions.js'; @@ -21,13 +25,16 @@ import { IModelDecorationOptions, TrackedRangeStickiness } from '../../../../../ import { LineTokens } from '../../../../../../common/tokens/lineTokens.js'; import { TokenArray } from '../../../../../../common/tokens/tokenArray.js'; import { InlineDecoration, InlineDecorationType } from '../../../../../../common/viewModel.js'; -import { IInlineEditsView, IInlineEditsViewHost } from '../inlineEditsViewInterface.js'; +import { IInlineEditsView, InlineEditTabAction } from '../inlineEditsViewInterface.js'; import { getModifiedBorderColor, modifiedChangedLineBackgroundColor } from '../theme.js'; import { getPrefixTrim, mapOutFalsy, rectToProps } from '../utils/utils.js'; import { rangesToBubbleRanges, Replacement } from './inlineEditsWordReplacementView.js'; export class InlineEditsLineReplacementView extends Disposable implements IInlineEditsView { + private readonly _onDidClick = this._register(new Emitter()); + readonly onDidClick = this._onDidClick.event; + private readonly _originalBubblesDecorationCollection = this._editor.editor.createDecorationsCollection(); private readonly _originalBubblesDecorationOptions: IModelDecorationOptions = { description: 'inlineCompletions-original-bubble', @@ -100,6 +107,7 @@ export class InlineEditsLineReplacementView extends Disposable implements IInlin const lineHeight = this._editor.getOption(EditorOption.lineHeight).read(reader); const contentLeft = this._editor.layoutInfoContentLeft.read(reader); + const verticalScrollbarWidth = this._editor.layoutInfoVerticalScrollbarWidth.read(reader); const scrollLeft = this._editor.scrollLeft.read(reader); const scrollTop = this._editor.scrollTop.read(reader); const editorLeftOffset = contentLeft - scrollLeft; @@ -140,12 +148,12 @@ export class InlineEditsLineReplacementView extends Disposable implements IInlin lowerBackground, lowerText, padding: PADDING, - minContentWidthRequired: maxLineWidth + PADDING * 2, + minContentWidthRequired: prefixLeftOffset + maxLineWidth + PADDING * 2 + verticalScrollbarWidth, }; }); private readonly _viewZoneInfo = derived<{ height: number; lineNumber: number } | undefined>(reader => { - const shouldShowViewZone = this._editor.getOption(EditorOption.inlineSuggest).map(o => o.edits.codeShifting).read(reader); + const shouldShowViewZone = this._editor.getOption(EditorOption.inlineSuggest).map(o => o.edits.allowCodeShifting === 'always').read(reader); if (!shouldShowViewZone) { return undefined; } @@ -189,7 +197,7 @@ export class InlineEditsLineReplacementView extends Disposable implements IInlin l.style.position = 'relative'; }); - const modifiedBorderColor = getModifiedBorderColor(this._host.tabAction).read(reader); + const modifiedBorderColor = getModifiedBorderColor(this._tabAction).read(reader); return [ n.div({ @@ -222,8 +230,13 @@ export class InlineEditsLineReplacementView extends Disposable implements IInlin boxShadow: `${asCssVariable(scrollbarShadow)} 0 6px 6px -6px`, borderTop: `1px solid ${modifiedBorderColor}`, overflow: 'hidden', + cursor: 'pointer', + pointerEvents: 'auto', + }, + onmousedown: e => { + e.preventDefault(); // This prevents that the editor loses focus }, - //onmouseup: () => this._host.accept(), + onclick: (e) => this._onDidClick.fire(new StandardMouseEvent(getWindow(e), e)), }, [ n.div({ style: { @@ -264,7 +277,7 @@ export class InlineEditsLineReplacementView extends Disposable implements IInlin }) ]).keepUpdated(this._store); - readonly isHovered = this._div.didMouseMoveDuringHover; + readonly isHovered = this._editor.isTargetHovered((e) => this._isMouseOverWidget(e), this._store); constructor( private readonly _editor: ObservableCodeEditor, @@ -274,7 +287,7 @@ export class InlineEditsLineReplacementView extends Disposable implements IInlin modifiedLines: string[]; replacements: Replacement[]; } | undefined>, - private readonly _host: IInlineEditsViewHost, + private readonly _tabAction: IObservable, @ILanguageService private readonly _languageService: ILanguageService ) { super(); @@ -312,6 +325,15 @@ export class InlineEditsLineReplacementView extends Disposable implements IInlin })); } + private _isMouseOverWidget(e: IEditorMouseEvent): boolean { + const layout = this._layout.get(); + if (!layout || !(e.event instanceof EditorMouseEvent)) { + return false; + } + + return layout.lowerBackground.containsPoint(new Point(e.event.relativePos.x, e.event.relativePos.y)); + } + // View Zones private _previousViewZoneInfo: { height: number; lineNumber: number; id: string } | undefined = undefined; diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsSideBySideView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsSideBySideView.ts index dcd987f2cd01..41ff1f50b230 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsSideBySideView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsSideBySideView.ts @@ -3,7 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ import { $, getWindow, n } from '../../../../../../../base/browser/dom.js'; +import { IMouseEvent, StandardMouseEvent } from '../../../../../../../base/browser/mouseEvent.js'; import { Color } from '../../../../../../../base/common/color.js'; +import { Emitter } from '../../../../../../../base/common/event.js'; import { Disposable } from '../../../../../../../base/common/lifecycle.js'; import { IObservable, IReader, autorun, constObservable, derived, derivedObservableWithCache, observableFromEvent } from '../../../../../../../base/common/observable.js'; import { IInstantiationService } from '../../../../../../../platform/instantiation/common/instantiation.js'; @@ -21,7 +23,7 @@ import { Range } from '../../../../../../common/core/range.js'; import { ITextModel } from '../../../../../../common/model.js'; import { StickyScrollController } from '../../../../../stickyScroll/browser/stickyScrollController.js'; import { InlineCompletionContextKeys } from '../../../controller/inlineCompletionContextKeys.js'; -import { IInlineEditsView, IInlineEditsViewHost } from '../inlineEditsViewInterface.js'; +import { IInlineEditsView, InlineEditTabAction } from '../inlineEditsViewInterface.js'; import { InlineEditWithChanges } from '../inlineEditWithChanges.js'; import { getModifiedBorderColor, getOriginalBorderColor, modifiedBackgroundColor, originalBackgroundColor } from '../theme.js'; import { PathBuilder, createRectangle, getOffsetForPos, mapOutFalsy, maxContentWidthInRange } from '../utils/utils.js'; @@ -50,6 +52,9 @@ export class InlineEditsSideBySideView extends Disposable implements IInlineEdit private readonly _editorObs = observableCodeEditor(this._editor); + private readonly _onDidClick = this._register(new Emitter()); + readonly onDidClick = this._onDidClick.event; + constructor( private readonly _editor: ICodeEditor, private readonly _edit: IObservable, @@ -59,7 +64,7 @@ export class InlineEditsSideBySideView extends Disposable implements IInlineEdit newTextLineCount: number; originalDisplayRange: LineRange; } | undefined>, - private readonly _host: IInlineEditsViewHost, + private readonly _tabAction: IObservable, @IInstantiationService private readonly _instantiationService: IInstantiationService, @IThemeService private readonly _themeService: IThemeService, ) { @@ -118,10 +123,13 @@ export class InlineEditsSideBySideView extends Disposable implements IInlineEdit private readonly previewRef = n.ref(); private readonly _editorContainer = n.div({ - class: ['editorContainer', this._editorObs.getOption(EditorOption.inlineSuggest).map(v => !v.edits.useGutterIndicator && 'showHover')], + class: ['editorContainer'], style: { position: 'absolute', overflow: 'hidden', cursor: 'pointer' }, - onclick: () => { - this._host.accept(); + onmousedown: e => { + e.preventDefault(); // This prevents that the editor loses focus + }, + onclick: (e) => { + this._onDidClick.fire(new StandardMouseEvent(getWindow(e), e)); } }, [ n.div({ class: 'preview', style: { pointerEvents: 'none' }, ref: this.previewRef }), @@ -500,8 +508,8 @@ export class InlineEditsSideBySideView extends Disposable implements IInlineEdit const layoutInfoObs = mapOutFalsy(this._previewEditorLayoutInfo).read(reader); if (!layoutInfoObs) { return undefined; } - const modifiedBorderColor = getModifiedBorderColor(this._host.tabAction).read(reader); - const originalBorderColor = getOriginalBorderColor(this._host.tabAction).read(reader); + const modifiedBorderColor = getModifiedBorderColor(this._tabAction).read(reader); + const originalBorderColor = getOriginalBorderColor(this._tabAction).read(reader); return [ n.svgElem('path', { diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsWordInsertView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsWordInsertView.ts index dcd1747fd577..d18dad373c37 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsWordInsertView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsWordInsertView.ts @@ -4,6 +4,8 @@ *--------------------------------------------------------------------------------------------*/ import { n } from '../../../../../../../base/browser/dom.js'; +import { IMouseEvent } from '../../../../../../../base/browser/mouseEvent.js'; +import { Emitter } from '../../../../../../../base/common/event.js'; import { Disposable } from '../../../../../../../base/common/lifecycle.js'; import { constObservable, derived, IObservable } from '../../../../../../../base/common/observable.js'; import { ObservableCodeEditor } from '../../../../../../browser/observableCodeEditor.js'; @@ -12,11 +14,15 @@ import { Rect } from '../../../../../../browser/rect.js'; import { EditorOption } from '../../../../../../common/config/editorOptions.js'; import { OffsetRange } from '../../../../../../common/core/offsetRange.js'; import { SingleTextEdit } from '../../../../../../common/core/textEdit.js'; -import { IInlineEditsView } from '../inlineEditsViewInterface.js'; +import { IInlineEditsView, InlineEditTabAction } from '../inlineEditsViewInterface.js'; import { getModifiedBorderColor } from '../theme.js'; -import { InlineEditTabAction, mapOutFalsy, rectToProps } from '../utils/utils.js'; +import { mapOutFalsy, rectToProps } from '../utils/utils.js'; export class InlineEditsWordInsertView extends Disposable implements IInlineEditsView { + + private readonly _onDidClick = this._register(new Emitter()); + readonly onDidClick = this._onDidClick.event; + private readonly _start = this._editor.observePosition(constObservable(this._edit.range.getStartPosition()), this._store); private readonly _layout = derived(this, reader => { diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsWordReplacementView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsWordReplacementView.ts index c948e0097ffe..f7272f15052c 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsWordReplacementView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/inlineEditsWordReplacementView.ts @@ -3,9 +3,11 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { n, ObserverNodeWithElement } from '../../../../../../../base/browser/dom.js'; +import { getWindow, n, ObserverNodeWithElement } from '../../../../../../../base/browser/dom.js'; +import { IMouseEvent, StandardMouseEvent } from '../../../../../../../base/browser/mouseEvent.js'; +import { Emitter } from '../../../../../../../base/common/event.js'; import { Disposable } from '../../../../../../../base/common/lifecycle.js'; -import { constObservable, derived, observableValue } from '../../../../../../../base/common/observable.js'; +import { constObservable, derived, IObservable, observableValue } from '../../../../../../../base/common/observable.js'; import { editorBackground, editorHoverForeground, scrollbarShadow } from '../../../../../../../platform/theme/common/colorRegistry.js'; import { asCssVariable } from '../../../../../../../platform/theme/common/colorUtils.js'; import { ObservableCodeEditor } from '../../../../../../browser/observableCodeEditor.js'; @@ -20,7 +22,7 @@ import { SingleTextEdit } from '../../../../../../common/core/textEdit.js'; import { ILanguageService } from '../../../../../../common/languages/language.js'; import { LineTokens } from '../../../../../../common/tokens/lineTokens.js'; import { TokenArray } from '../../../../../../common/tokens/tokenArray.js'; -import { IInlineEditsView, IInlineEditsViewHost } from '../inlineEditsViewInterface.js'; +import { IInlineEditsView, InlineEditTabAction } from '../inlineEditsViewInterface.js'; import { getModifiedBorderColor, modifiedChangedTextOverlayColor, originalChangedTextOverlayColor, replacementViewBackground } from '../theme.js'; import { mapOutFalsy, rectToProps } from '../utils/utils.js'; @@ -28,6 +30,9 @@ export class InlineEditsWordReplacementView extends Disposable implements IInlin public static MAX_LENGTH = 100; + private readonly _onDidClick = this._register(new Emitter()); + readonly onDidClick = this._onDidClick.event; + private readonly _start = this._editor.observePosition(constObservable(this._edit.range.getStartPosition()), this._store); private readonly _end = this._editor.observePosition(constObservable(this._edit.range.getEndPosition()), this._store); @@ -42,7 +47,7 @@ export class InlineEditsWordReplacementView extends Disposable implements IInlin /** Must be single-line in both sides */ private readonly _edit: SingleTextEdit, private readonly _innerEdits: SingleTextEdit[], - private readonly _host: IInlineEditsViewHost, + private readonly _tabAction: IObservable, @ILanguageService private readonly _languageService: ILanguageService, ) { super(); @@ -157,7 +162,7 @@ export class InlineEditsWordReplacementView extends Disposable implements IInlin const edits = layoutProps.innerEdits.map(edit => ({ modified: edit.modified.translateX(-contentLeft), original: edit.original.translateX(-contentLeft) })); - const modifiedBorderColor = getModifiedBorderColor(this._host.tabAction).read(reader); + const modifiedBorderColor = getModifiedBorderColor(this._tabAction).read(reader); return [ n.div({ @@ -181,7 +186,10 @@ export class InlineEditsWordReplacementView extends Disposable implements IInlin cursor: 'pointer', pointerEvents: 'auto', }, - onmouseup: () => this._host.accept(), + onmousedown: e => { + e.preventDefault(); // This prevents that the editor loses focus + }, + onmouseup: (e) => this._onDidClick.fire(new StandardMouseEvent(getWindow(e), e)), obsRef: (elem) => { this._hoverableElement.set(elem, undefined); } diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/originalEditorInlineDiffView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/originalEditorInlineDiffView.ts index 903ae1338791..6af87b7dff85 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/originalEditorInlineDiffView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViews/originalEditorInlineDiffView.ts @@ -3,29 +3,26 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { IMouseEvent } from '../../../../../../../base/browser/mouseEvent.js'; +import { Emitter } from '../../../../../../../base/common/event.js'; import { Disposable } from '../../../../../../../base/common/lifecycle.js'; import { autorunWithStore, derived, IObservable, observableFromEvent } from '../../../../../../../base/common/observable.js'; import { ICodeEditor, MouseTargetType } from '../../../../../../browser/editorBrowser.js'; import { observableCodeEditor } from '../../../../../../browser/observableCodeEditor.js'; import { rangeIsSingleLine } from '../../../../../../browser/widget/diffEditor/components/diffEditorViewZones/diffEditorViewZones.js'; -import { LineSource, renderLines, RenderOptions } from '../../../../../../browser/widget/diffEditor/components/diffEditorViewZones/renderLines.js'; -import { diffAddDecoration } from '../../../../../../browser/widget/diffEditor/registrations.contribution.js'; -import { applyViewZones, IObservableViewZone } from '../../../../../../browser/widget/diffEditor/utils.js'; -import { EditorOption } from '../../../../../../common/config/editorOptions.js'; import { OffsetRange } from '../../../../../../common/core/offsetRange.js'; import { Range } from '../../../../../../common/core/range.js'; import { AbstractText } from '../../../../../../common/core/textEdit.js'; import { DetailedLineRangeMapping } from '../../../../../../common/diff/rangeMapping.js'; import { EndOfLinePreference, IModelDeltaDecoration, InjectedTextCursorStops, ITextModel } from '../../../../../../common/model.js'; import { ModelDecorationOptions } from '../../../../../../common/model/textModel.js'; -import { InlineDecoration, InlineDecorationType } from '../../../../../../common/viewModel.js'; -import { IInlineEditsView, IInlineEditsViewHost } from '../inlineEditsViewInterface.js'; +import { IInlineEditsView } from '../inlineEditsViewInterface.js'; import { classNames } from '../utils/utils.js'; export interface IOriginalEditorInlineDiffViewState { diff: DetailedLineRangeMapping[]; modifiedText: AbstractText; - mode: 'mixedLines' | 'insertionInline' | 'interleavedLines' | 'sideBySide' | 'deletion'; + mode: 'insertionInline' | 'sideBySide' | 'deletion'; modifiedCodeEditor: ICodeEditor; } @@ -35,8 +32,13 @@ export class OriginalEditorInlineDiffView extends Disposable implements IInlineE return allowsTrueInlineDiffRendering(mapping); } + private readonly _onDidClick = this._register(new Emitter()); + readonly onDidClick = this._onDidClick.event; + readonly isHovered = observableCodeEditor(this._originalEditor).isTargetHovered( - p => p.type === MouseTargetType.CONTENT_TEXT && p.detail.injectedText?.options.attachedData instanceof InlineEditAttachedData, + p => p.target.type === MouseTargetType.CONTENT_TEXT && + p.target.detail.injectedText?.options.attachedData instanceof InlineEditAttachedData && + p.target.detail.injectedText.options.attachedData.owner === this, this._store ); @@ -46,7 +48,6 @@ export class OriginalEditorInlineDiffView extends Disposable implements IInlineE private readonly _originalEditor: ICodeEditor, private readonly _state: IObservable, private readonly _modifiedTextModel: ITextModel, - private readonly _host: IInlineEditsViewHost, ) { super(); @@ -60,69 +61,15 @@ export class OriginalEditorInlineDiffView extends Disposable implements IInlineE } })); - const editor = observableCodeEditor(this._originalEditor); - this._register(this._originalEditor.onMouseUp(e => { if (e.target.type !== MouseTargetType.CONTENT_TEXT) { return; } const a = e.target.detail.injectedText?.options.attachedData; - if (a instanceof InlineEditAttachedData) { - this._host.accept(); - e.event.preventDefault(); + if (a instanceof InlineEditAttachedData && a.owner === this) { + this._onDidClick.fire(e.event); } })); - - const originalViewZones = derived(this, (reader) => { - const originalModel = editor.model.read(reader); - if (!originalModel) { return []; } - - const origViewZones: IObservableViewZone[] = []; - const renderOptions = RenderOptions.fromEditor(this._originalEditor); - const modLineHeight = editor.getOption(EditorOption.lineHeight).read(reader); - - const s = this._state.read(reader); - if (!s) { return origViewZones; } - - for (const diff of s.diff) { - if (s.mode !== 'interleavedLines') { - continue; - } - - this._tokenizationFinished.read(reader); // Update view-zones once tokenization completes - - const source = new LineSource(diff.modified.mapToLineArray(l => this._modifiedTextModel.tokenization.getLineTokens(l))); - - const decorations: InlineDecoration[] = []; - for (const i of diff.innerChanges || []) { - decorations.push(new InlineDecoration( - i.modifiedRange.delta(-(diff.original.startLineNumber - 1)), - diffAddDecoration.className!, - InlineDecorationType.Regular, - )); - } - - const deletedCodeDomNode = document.createElement('div'); - deletedCodeDomNode.classList.add('view-lines', 'line-insert', 'monaco-mouse-cursor-text'); - // .inline-deleted-margin-view-zone - - const result = renderLines(source, renderOptions, decorations, deletedCodeDomNode); - - origViewZones.push({ - afterLineNumber: diff.original.endLineNumberExclusive - 1, - domNode: deletedCodeDomNode, - heightInPx: result.heightInLines * modLineHeight, - minWidthInPx: result.minWidthInPx, - - showInHiddenAreas: true, - suppressMouseDown: true, - }); - } - - return origViewZones; - }); - - this._register(applyViewZones(this._originalEditor, originalViewZones)); } private readonly _decorations = derived(this, reader => { @@ -130,7 +77,7 @@ export class OriginalEditorInlineDiffView extends Disposable implements IInlineE if (!diff) { return undefined; } const modified = diff.modifiedText; - const showInline = diff.mode === 'mixedLines' || diff.mode === 'insertionInline'; + const showInline = diff.mode === 'insertionInline'; const showEmptyDecorations = true; @@ -260,7 +207,7 @@ export class OriginalEditorInlineDiffView extends Disposable implements IInlineE ...extraClasses // include extraClasses for additional styling if provided ), cursorStops: InjectedTextCursorStops.None, - attachedData: new InlineEditAttachedData(), + attachedData: new InlineEditAttachedData(this), }, zIndex: 2, showIfCollapsed: true, @@ -277,6 +224,7 @@ export class OriginalEditorInlineDiffView extends Disposable implements IInlineE } class InlineEditAttachedData { + constructor(public readonly owner: OriginalEditorInlineDiffView) { } } function allowsTrueInlineDiffRendering(mapping: DetailedLineRangeMapping): boolean { diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/theme.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/theme.ts index 85cc06a8a271..056fbf695d18 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/theme.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/theme.ts @@ -8,7 +8,7 @@ import { IObservable } from '../../../../../../base/common/observable.js'; import { localize } from '../../../../../../nls.js'; import { diffRemoved, diffInsertedLine, diffInserted, editorHoverBorder, editorHoverStatusBarBackground, buttonBackground, buttonForeground, buttonSecondaryBackground, buttonSecondaryForeground } from '../../../../../../platform/theme/common/colorRegistry.js'; import { registerColor, transparent, asCssVariable, lighten, darken } from '../../../../../../platform/theme/common/colorUtils.js'; -import { InlineEditTabAction } from './utils/utils.js'; +import { InlineEditTabAction } from './inlineEditsViewInterface.js'; export const originalBackgroundColor = registerColor( 'inlineEdit.originalBackground', diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils/utils.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils/utils.ts index 52b182c12828..a393e04e241b 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils/utils.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/utils/utils.ts @@ -26,12 +26,6 @@ import { SingleTextEdit, TextEdit } from '../../../../../../common/core/textEdit import { RangeMapping } from '../../../../../../common/diff/rangeMapping.js'; import { indentOfLine } from '../../../../../../common/model/textModel.js'; -export enum InlineEditTabAction { - Jump = 'jump', - Accept = 'accept', - Inactive = 'inactive' -} - export function maxContentWidthInRange(editor: ObservableCodeEditor, range: LineRange, reader: IReader | undefined): number { editor.layoutInfo.read(reader); editor.value.read(reader); @@ -81,10 +75,22 @@ export function getPrefixTrim(diffRanges: Range[], originalLinesRange: LineRange const replacementStart = diffRanges.map(r => r.isSingleLine() ? r.startColumn - 1 : 0); const originalIndents = originalLinesRange.mapToLineArray(line => indentOfLine(textModel.getLineContent(line))); - const modifiedIndents = modifiedLines.map(line => indentOfLine(line)); + const modifiedIndents = modifiedLines.filter(line => line !== '').map(line => indentOfLine(line)); const prefixTrim = Math.min(...replacementStart, ...originalIndents, ...modifiedIndents); - const prefixLeftOffset = editor.getOffsetForColumn(originalLinesRange.startLineNumber, prefixTrim + 1); + let prefixLeftOffset; + const startLineIndent = textModel.getLineIndentColumn(originalLinesRange.startLineNumber); + if (startLineIndent >= prefixTrim + 1) { + // We can use the editor to get the offset + prefixLeftOffset = editor.getOffsetForColumn(originalLinesRange.startLineNumber, prefixTrim + 1); + } else if (startLineIndent !== 1) { + // We need to approximate the offset as the editor does not contain the modified lines yet + const startLineIndentOffset = editor.getOffsetForColumn(originalLinesRange.startLineNumber, startLineIndent); + prefixLeftOffset = startLineIndentOffset / (startLineIndent - 1) * prefixTrim; + } else { + // unable to approximate the offset + return { prefixTrim: 0, prefixLeftOffset: 0 }; + } return { prefixTrim, prefixLeftOffset }; } diff --git a/src/vs/editor/contrib/multicursor/browser/multicursor.ts b/src/vs/editor/contrib/multicursor/browser/multicursor.ts index c3bcf55dd7ce..abc43c1f7d7e 100644 --- a/src/vs/editor/contrib/multicursor/browser/multicursor.ts +++ b/src/vs/editor/contrib/multicursor/browser/multicursor.ts @@ -201,7 +201,7 @@ class InsertCursorAtEndOfLineSelected extends EditorAction { constructor() { super({ id: 'editor.action.addCursorsToBottom', - label: nls.localize2('mutlicursor.addCursorsToBottom', "Add Cursors To Bottom"), + label: nls.localize2('mutlicursor.addCursorsToBottom', "Add Cursors to Bottom"), precondition: undefined }); } @@ -233,7 +233,7 @@ class InsertCursorAtTopOfLineSelected extends EditorAction { constructor() { super({ id: 'editor.action.addCursorsToTop', - label: nls.localize2('mutlicursor.addCursorsToTop', "Add Cursors To Top"), + label: nls.localize2('mutlicursor.addCursorsToTop', "Add Cursors to Top"), precondition: undefined }); } @@ -686,7 +686,7 @@ export class AddSelectionToNextFindMatchAction extends MultiCursorSelectionContr constructor() { super({ id: 'editor.action.addSelectionToNextFindMatch', - label: nls.localize2('addSelectionToNextFindMatch', "Add Selection To Next Find Match"), + label: nls.localize2('addSelectionToNextFindMatch', "Add Selection to Next Find Match"), precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.focus, @@ -710,7 +710,7 @@ export class AddSelectionToPreviousFindMatchAction extends MultiCursorSelectionC constructor() { super({ id: 'editor.action.addSelectionToPreviousFindMatch', - label: nls.localize2('addSelectionToPreviousFindMatch', "Add Selection To Previous Find Match"), + label: nls.localize2('addSelectionToPreviousFindMatch', "Add Selection to Previous Find Match"), precondition: undefined, menuOpts: { menuId: MenuId.MenubarSelectionMenu, @@ -729,7 +729,7 @@ export class MoveSelectionToNextFindMatchAction extends MultiCursorSelectionCont constructor() { super({ id: 'editor.action.moveSelectionToNextFindMatch', - label: nls.localize2('moveSelectionToNextFindMatch', "Move Last Selection To Next Find Match"), + label: nls.localize2('moveSelectionToNextFindMatch', "Move Last Selection to Next Find Match"), precondition: undefined, kbOpts: { kbExpr: EditorContextKeys.focus, @@ -747,7 +747,7 @@ export class MoveSelectionToPreviousFindMatchAction extends MultiCursorSelection constructor() { super({ id: 'editor.action.moveSelectionToPreviousFindMatch', - label: nls.localize2('moveSelectionToPreviousFindMatch', "Move Last Selection To Previous Find Match"), + label: nls.localize2('moveSelectionToPreviousFindMatch', "Move Last Selection to Previous Find Match"), precondition: undefined }); } @@ -803,7 +803,7 @@ export class CompatChangeAll extends MultiCursorSelectionControllerAction { } class SelectionHighlighterState { - private readonly _modelVersionId: number = this._model.getVersionId(); + private readonly _modelVersionId: number; private _cachedFindMatches: Range[] | null = null; constructor( @@ -813,6 +813,7 @@ class SelectionHighlighterState { private readonly _wordSeparators: string | null, prevState: SelectionHighlighterState | null ) { + this._modelVersionId = this._model.getVersionId(); if (prevState && this._model === prevState._model && this._searchText === prevState._searchText diff --git a/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts b/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts index 67334742dad8..ef3cd7d78ccf 100644 --- a/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts +++ b/src/vs/editor/contrib/parameterHints/browser/parameterHintsWidget.ts @@ -26,8 +26,6 @@ import { IOpenerService } from '../../../../platform/opener/common/opener.js'; import { listHighlightForeground, registerColor } from '../../../../platform/theme/common/colorRegistry.js'; import { registerIcon } from '../../../../platform/theme/common/iconRegistry.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; -import { StopWatch } from '../../../../base/common/stopwatch.js'; -import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; const $ = dom.$; @@ -62,8 +60,7 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget { private readonly model: ParameterHintsModel, @IContextKeyService contextKeyService: IContextKeyService, @IOpenerService openerService: IOpenerService, - @ILanguageService languageService: ILanguageService, - @ITelemetryService private readonly telemetryService: ITelemetryService, + @ILanguageService languageService: ILanguageService ) { super(); @@ -275,30 +272,12 @@ export class ParameterHintsWidget extends Disposable implements IContentWidget { } private renderMarkdownDocs(markdown: IMarkdownString | undefined): IMarkdownRenderResult { - const stopWatch = new StopWatch(); const renderedContents = this.renderDisposeables.add(this.markdownRenderer.render(markdown, { asyncRenderCallback: () => { this.domNodes?.scrollbar.scanDomNode(); } })); renderedContents.element.classList.add('markdown-docs'); - - type RenderMarkdownPerformanceClassification = { - owner: 'donjayamanne'; - comment: 'Measure the time taken to render markdown for parameter hints'; - renderDuration: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Time in ms to render the markdown' }; - }; - - type RenderMarkdownPerformanceEvent = { - renderDuration: number; - }; - const renderDuration = stopWatch.elapsed(); - if (renderDuration > 300) { - this.telemetryService.publicLog2('parameterHints.parseMarkdown', { - renderDuration - }); - } - return renderedContents; } diff --git a/src/vs/editor/contrib/quickAccess/browser/gotoSymbolQuickAccess.ts b/src/vs/editor/contrib/quickAccess/browser/gotoSymbolQuickAccess.ts index a6e7a7434589..b4f95f7158ef 100644 --- a/src/vs/editor/contrib/quickAccess/browser/gotoSymbolQuickAccess.ts +++ b/src/vs/editor/contrib/quickAccess/browser/gotoSymbolQuickAccess.ts @@ -9,7 +9,7 @@ import { Codicon } from '../../../../base/common/codicons.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; import { IMatch } from '../../../../base/common/filters.js'; import { IPreparedQuery, pieceToQuery, prepareQuery, scoreFuzzy2 } from '../../../../base/common/fuzzyScorer.js'; -import { Disposable, DisposableStore, IDisposable, toDisposable } from '../../../../base/common/lifecycle.js'; +import { Disposable, DisposableStore, IDisposable, MutableDisposable, toDisposable } from '../../../../base/common/lifecycle.js'; import { format, trim } from '../../../../base/common/strings.js'; import { IRange, Range } from '../../../common/core/range.js'; import { ScrollType } from '../../../common/editorCommon.js'; @@ -169,21 +169,21 @@ export abstract class AbstractGotoSymbolQuickAccessProvider extends AbstractEdit const symbolsPromise = this.getDocumentSymbols(model, token); // Set initial picks and update on type - let picksCts: CancellationTokenSource | undefined = undefined; + const picksCts = disposables.add(new MutableDisposable()); const updatePickerItems = async (positionToEnclose: Position | undefined) => { // Cancel any previous ask for picks and busy - picksCts?.dispose(true); + picksCts?.value?.cancel(); picker.busy = false; // Create new cancellation source for this run - picksCts = new CancellationTokenSource(token); + picksCts.value = new CancellationTokenSource(); // Collect symbol picks picker.busy = true; try { const query = prepareQuery(picker.value.substr(AbstractGotoSymbolQuickAccessProvider.PREFIX.length).trim()); - const items = await this.doGetSymbolPicks(symbolsPromise, query, undefined, picksCts.token, model); + const items = await this.doGetSymbolPicks(symbolsPromise, query, undefined, picksCts.value.token, model); if (token.isCancellationRequested) { return; } diff --git a/src/vs/editor/contrib/rename/browser/renameWidget.ts b/src/vs/editor/contrib/rename/browser/renameWidget.ts index e5a7bc714975..baf0f0b88c49 100644 --- a/src/vs/editor/contrib/rename/browser/renameWidget.ts +++ b/src/vs/editor/contrib/rename/browser/renameWidget.ts @@ -181,7 +181,7 @@ export class RenameWidget implements IRenameWidget, IContentWidget, IDisposable } })); - this._disposables.add(_themeService.onDidColorThemeChange(this._updateStyles, this)); + this._disposables.add(_themeService.onDidColorThemeChange(e => this._updateStyles(e.theme))); } dispose(): void { diff --git a/src/vs/editor/contrib/sectionHeaders/browser/sectionHeaders.ts b/src/vs/editor/contrib/sectionHeaders/browser/sectionHeaders.ts index 01b574130290..8fda7ce5b661 100644 --- a/src/vs/editor/contrib/sectionHeaders/browser/sectionHeaders.ts +++ b/src/vs/editor/contrib/sectionHeaders/browser/sectionHeaders.ts @@ -8,7 +8,7 @@ import { Disposable } from '../../../../base/common/lifecycle.js'; import { ICodeEditor } from '../../../browser/editorBrowser.js'; import { EditorContributionInstantiation, registerEditorContribution } from '../../../browser/editorExtensions.js'; import { EditorOption, IEditorMinimapOptions } from '../../../common/config/editorOptions.js'; -import { IEditorContribution } from '../../../common/editorCommon.js'; +import { IEditorContribution, IEditorDecorationsCollection } from '../../../common/editorCommon.js'; import { StandardTokenType } from '../../../common/encodedTokenAttributes.js'; import { ILanguageConfigurationService } from '../../../common/languages/languageConfigurationRegistry.js'; import { IModelDeltaDecoration, MinimapPosition, MinimapSectionHeaderStyle, TrackedRangeStickiness } from '../../../common/model.js'; @@ -21,7 +21,7 @@ export class SectionHeaderDetector extends Disposable implements IEditorContribu public static readonly ID: string = 'editor.sectionHeaderDetector'; private options: FindSectionHeaderOptions | undefined; - private decorations = this.editor.createDecorationsCollection(); + private decorations: IEditorDecorationsCollection; private computeSectionHeaders: RunOnceScheduler; private computePromise: CancelablePromise | null; private currentOccurrences: { [decorationId: string]: SectionHeaderOccurrence }; @@ -32,6 +32,7 @@ export class SectionHeaderDetector extends Disposable implements IEditorContribu @IEditorWorkerService private readonly editorWorkerService: IEditorWorkerService, ) { super(); + this.decorations = this.editor.createDecorationsCollection(); this.options = this.createOptions(editor.getOption(EditorOption.minimap)); this.computePromise = null; diff --git a/src/vs/editor/contrib/smartSelect/test/browser/smartSelect.test.ts b/src/vs/editor/contrib/smartSelect/test/browser/smartSelect.test.ts index 3e774fc932f4..ff60c2733b20 100644 --- a/src/vs/editor/contrib/smartSelect/test/browser/smartSelect.test.ts +++ b/src/vs/editor/contrib/smartSelect/test/browser/smartSelect.test.ts @@ -214,7 +214,7 @@ suite('SmartSelect', () => { async function assertRanges(provider: SelectionRangeProvider, value: string, ...expected: IRange[]): Promise { const index = value.indexOf('|'); - value = value.replace('|', ''); // CodeQL [SM02383] js/incomplete-sanitization this is purpose only the first | character + value = value.replace(/\|/g, ''); // Remove all '|' characters, not just the first const model = modelService.createModel(value, new StaticLanguageSelector(languageId), URI.parse('fake:lang')); const pos = model.getPositionAt(index); diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css b/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css index 12480664119e..aa580483cdad 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScroll.css @@ -50,6 +50,9 @@ .monaco-editor .sticky-line-number-inner { display: inline-block; text-align: right; + line-height: normal; + position: absolute; + bottom: 0; } .monaco-editor .sticky-widget { diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts index 38e4419456bc..c407471b340f 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollController.ts @@ -584,7 +584,6 @@ export class StickyScrollController extends Disposable implements IEditorContrib } findScrollWidgetState(): StickyScrollWidgetState { - const lineHeight: number = this._editor.getOption(EditorOption.lineHeight); const maxNumberStickyLines = Math.min(this._maxStickyLines, this._editor.getOption(EditorOption.stickyScroll).maxLineCount); const scrollTop: number = this._editor.getScrollTop(); let lastLineRelativePosition: number = 0; @@ -597,26 +596,18 @@ export class StickyScrollController extends Disposable implements IEditorContrib for (const range of candidateRanges) { const start = range.startLineNumber; const end = range.endLineNumber; - const depth = range.nestingDepth; if (end - start > 0) { - const topOfElementAtDepth = (depth - 1) * lineHeight; - const bottomOfElementAtDepth = depth * lineHeight; + const topOfElement = range.top; + const bottomOfElement = topOfElement + range.height; - const bottomOfBeginningLine = this._editor.getBottomForLineNumber(start) - scrollTop; - const topOfEndLine = this._editor.getTopForLineNumber(end) - scrollTop; + const topOfBeginningLine = this._editor.getTopForLineNumber(start) - scrollTop; const bottomOfEndLine = this._editor.getBottomForLineNumber(end) - scrollTop; - - if (topOfElementAtDepth > topOfEndLine && topOfElementAtDepth <= bottomOfEndLine) { + if (topOfElement > topOfBeginningLine && topOfElement <= bottomOfEndLine) { startLineNumbers.push(start); endLineNumbers.push(end + 1); - if (topOfElementAtDepth > bottomOfEndLine - lineHeight) { - lastLineRelativePosition = bottomOfEndLine - bottomOfElementAtDepth; + if (bottomOfElement > bottomOfEndLine) { + lastLineRelativePosition = bottomOfEndLine - bottomOfElement; } - break; - } - else if (bottomOfElementAtDepth > bottomOfBeginningLine && bottomOfElementAtDepth <= bottomOfEndLine) { - startLineNumbers.push(start); - endLineNumbers.push(end + 1); } if (startLineNumbers.length === maxNumberStickyLines) { break; diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollProvider.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollProvider.ts index 77cd911f9f46..ff98493b58ee 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollProvider.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollProvider.ts @@ -20,7 +20,8 @@ export class StickyLineCandidate { constructor( public readonly startLineNumber: number, public readonly endLineNumber: number, - public readonly nestingDepth: number, + public readonly top: number, + public readonly height: number, ) { } } @@ -150,6 +151,7 @@ export class StickyLineCandidateProvider extends Disposable implements IStickyLi outlineModel: StickyElement, result: StickyLineCandidate[], depth: number, + top: number, lastStartLineNumber: number ): void { if (outlineModel.children.length === 0) { @@ -177,11 +179,12 @@ export class StickyLineCandidateProvider extends Disposable implements IStickyLi const childEndLine = child.range.endLineNumber; if (range.startLineNumber <= childEndLine + 1 && childStartLine - 1 <= range.endLineNumber && childStartLine !== lastLine) { lastLine = childStartLine; - result.push(new StickyLineCandidate(childStartLine, childEndLine - 1, depth + 1)); - this.getCandidateStickyLinesIntersectingFromStickyModel(range, child, result, depth + 1, childStartLine); + const lineHeight = this._editor.getOption(EditorOption.lineHeight); + result.push(new StickyLineCandidate(childStartLine, childEndLine - 1, top, lineHeight)); + this.getCandidateStickyLinesIntersectingFromStickyModel(range, child, result, depth + 1, top + lineHeight, childStartLine); } } else { - this.getCandidateStickyLinesIntersectingFromStickyModel(range, child, result, depth, lastStartLineNumber); + this.getCandidateStickyLinesIntersectingFromStickyModel(range, child, result, depth, top, lastStartLineNumber); } } } @@ -191,7 +194,7 @@ export class StickyLineCandidateProvider extends Disposable implements IStickyLi return []; } let stickyLineCandidates: StickyLineCandidate[] = []; - this.getCandidateStickyLinesIntersectingFromStickyModel(range, this._model.element, stickyLineCandidates, 0, -1); + this.getCandidateStickyLinesIntersectingFromStickyModel(range, this._model.element, stickyLineCandidates, 0, 0, -1); const hiddenRanges: Range[] | undefined = this._editor._getViewModel()?.getHiddenAreas(); if (hiddenRanges) { diff --git a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts index fada2e0c7cff..69ebc1f12841 100644 --- a/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts +++ b/src/vs/editor/contrib/stickyScroll/browser/stickyScrollWidget.ts @@ -56,8 +56,10 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { private readonly _linesDomNodeScrollable: HTMLElement = document.createElement('div'); private readonly _linesDomNode: HTMLElement = document.createElement('div'); + private readonly _editor: ICodeEditor; + private _previousState: StickyScrollWidgetState | undefined; - private _lineHeight: number = this._editor.getOption(EditorOption.lineHeight); + private _lineHeight: number; private _renderedStickyLines: RenderedStickyLine[] = []; private _lineNumbers: number[] = []; private _lastLineRelativePosition: number = 0; @@ -71,10 +73,12 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { public readonly onDidChangeStickyScrollHeight = this._onDidChangeStickyScrollHeight.event; constructor( - private readonly _editor: ICodeEditor + editor: ICodeEditor ) { super(); + this._editor = editor; + this._lineHeight = editor.getOption(EditorOption.lineHeight); this._lineNumbersDomNode.className = 'sticky-widget-line-numbers'; this._lineNumbersDomNode.setAttribute('role', 'none'); @@ -85,7 +89,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { this._linesDomNodeScrollable.appendChild(this._linesDomNode); this._rootDomNode.className = 'sticky-widget'; - this._rootDomNode.classList.toggle('peek', _editor instanceof EmbeddedCodeEditorWidget); + this._rootDomNode.classList.toggle('peek', editor instanceof EmbeddedCodeEditorWidget); this._rootDomNode.appendChild(this._lineNumbersDomNode); this._rootDomNode.appendChild(this._linesDomNodeScrollable); this._setHeight(0); @@ -303,6 +307,7 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { actualInlineDecorations = []; } + const lineHeight = this._lineHeight; const renderLineInput: RenderLineInput = new RenderLineInput(true, true, lineRenderingData.content, lineRenderingData.continuesWithWrappedLine, lineRenderingData.isBasicASCII, lineRenderingData.containsRTL, 0, @@ -328,14 +333,14 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { lineHTMLNode.tabIndex = 0; lineHTMLNode.className = 'sticky-line-content'; lineHTMLNode.classList.add(`stickyLine${line}`); - lineHTMLNode.style.lineHeight = `${this._lineHeight}px`; + lineHTMLNode.style.lineHeight = `${lineHeight}px`; lineHTMLNode.innerHTML = newLine as string; const lineNumberHTMLNode = document.createElement('span'); lineNumberHTMLNode.setAttribute(STICKY_INDEX_ATTR, String(index)); lineNumberHTMLNode.setAttribute(STICKY_IS_LINE_NUMBER_ATTR, ''); lineNumberHTMLNode.className = 'sticky-line-number'; - lineNumberHTMLNode.style.lineHeight = `${this._lineHeight}px`; + lineNumberHTMLNode.style.lineHeight = `${lineHeight}px`; const lineNumbersWidth = layoutInfo.contentLeft; lineNumberHTMLNode.style.width = `${lineNumbersWidth}px`; @@ -346,7 +351,6 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { innerLineNumberHTML.innerText = Math.abs(line - this._editor.getPosition()!.lineNumber).toString(); } innerLineNumberHTML.className = 'sticky-line-number-inner'; - innerLineNumberHTML.style.lineHeight = `${this._lineHeight}px`; innerLineNumberHTML.style.width = `${layoutInfo.lineNumbersWidth}px`; innerLineNumberHTML.style.paddingLeft = `${layoutInfo.lineNumbersLeft}px`; @@ -361,12 +365,12 @@ export class StickyScrollWidget extends Disposable implements IOverlayWidget { this._editor.applyFontInfo(lineNumberHTMLNode); - lineNumberHTMLNode.style.lineHeight = `${this._lineHeight}px`; - lineHTMLNode.style.lineHeight = `${this._lineHeight}px`; - lineNumberHTMLNode.style.height = `${this._lineHeight}px`; - lineHTMLNode.style.height = `${this._lineHeight}px`; + lineNumberHTMLNode.style.lineHeight = `${lineHeight}px`; + lineHTMLNode.style.lineHeight = `${lineHeight}px`; + lineNumberHTMLNode.style.height = `${lineHeight}px`; + lineHTMLNode.style.height = `${lineHeight}px`; - const renderedLine = new RenderedStickyLine(index, line, lineHTMLNode, lineNumberHTMLNode, foldingIcon, renderOutput.characterMapping, lineHTMLNode.scrollWidth); + const renderedLine = new RenderedStickyLine(index, line, lineHTMLNode, lineNumberHTMLNode, foldingIcon, renderOutput.characterMapping, lineHTMLNode.scrollWidth, lineHeight); return this._updateTopAndZIndexOfStickyLine(renderedLine); } @@ -512,6 +516,7 @@ class RenderedStickyLine { public readonly foldingIcon: StickyFoldingIcon | undefined, public readonly characterMapping: CharacterMapping, public readonly scrollWidth: number, + public readonly height: number ) { } } diff --git a/src/vs/editor/contrib/stickyScroll/test/browser/stickyScroll.test.ts b/src/vs/editor/contrib/stickyScroll/test/browser/stickyScroll.test.ts index 7ac055817329..07493f907149 100644 --- a/src/vs/editor/contrib/stickyScroll/test/browser/stickyScroll.test.ts +++ b/src/vs/editor/contrib/stickyScroll/test/browser/stickyScroll.test.ts @@ -148,9 +148,9 @@ suite('Sticky Scroll Tests', () => { disposables.add(languageService.documentSymbolProvider.register('*', documentSymbolProviderForTestModel())); const provider: StickyLineCandidateProvider = new StickyLineCandidateProvider(editor, languageService, languageConfigurationService); await provider.update(); - assert.deepStrictEqual(provider.getCandidateStickyLinesIntersecting({ startLineNumber: 1, endLineNumber: 4 }), [new StickyLineCandidate(1, 2, 1)]); - assert.deepStrictEqual(provider.getCandidateStickyLinesIntersecting({ startLineNumber: 8, endLineNumber: 10 }), [new StickyLineCandidate(7, 11, 1), new StickyLineCandidate(9, 11, 2), new StickyLineCandidate(10, 10, 3)]); - assert.deepStrictEqual(provider.getCandidateStickyLinesIntersecting({ startLineNumber: 10, endLineNumber: 13 }), [new StickyLineCandidate(7, 11, 1), new StickyLineCandidate(9, 11, 2), new StickyLineCandidate(10, 10, 3)]); + assert.deepStrictEqual(provider.getCandidateStickyLinesIntersecting({ startLineNumber: 1, endLineNumber: 4 }), [new StickyLineCandidate(1, 2, 0, 19)]); + assert.deepStrictEqual(provider.getCandidateStickyLinesIntersecting({ startLineNumber: 8, endLineNumber: 10 }), [new StickyLineCandidate(7, 11, 0, 19), new StickyLineCandidate(9, 11, 19, 19), new StickyLineCandidate(10, 10, 38, 19)]); + assert.deepStrictEqual(provider.getCandidateStickyLinesIntersecting({ startLineNumber: 10, endLineNumber: 13 }), [new StickyLineCandidate(7, 11, 0, 19), new StickyLineCandidate(9, 11, 19, 19), new StickyLineCandidate(10, 10, 38, 19)]); provider.dispose(); model.dispose(); diff --git a/src/vs/editor/contrib/suggest/browser/suggestController.ts b/src/vs/editor/contrib/suggest/browser/suggestController.ts index 0ede4624f230..7f871915d76c 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestController.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestController.ts @@ -517,8 +517,7 @@ export class SuggestController implements IEditorContribution { } private _reportSuggestionAcceptedTelemetry(item: CompletionItem, model: ITextModel, itemResolved: boolean, commandExectionDuration: number, additionalEditsAppliedAsync: number, index: number, completionItems: CompletionItem[]): void { - if (Math.floor(Math.random() * 100) === 0) { - // throttle telemetry event because accepting completions happens a lot + if (Math.random() > 0.0001) { // 0.01% return; } diff --git a/src/vs/editor/contrib/suggest/browser/suggestModel.ts b/src/vs/editor/contrib/suggest/browser/suggestModel.ts index 7a7019032cab..fcf97af35a62 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestModel.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestModel.ts @@ -505,6 +505,7 @@ export class SuggestModel implements IDisposable { this._requestToken?.dispose(); if (!this._editor.hasModel()) { + completions.disposable.dispose(); return; } @@ -514,6 +515,7 @@ export class SuggestModel implements IDisposable { } if (this._triggerState === undefined) { + completions.disposable.dispose(); return; } @@ -561,11 +563,12 @@ export class SuggestModel implements IDisposable { }).catch(onUnexpectedError); } - private _telemetryGate: number = 0; - + /** + * Report durations telemetry with a 1% sampling rate. + * The telemetry is reported only if a random number between 0 and 100 is less than or equal to 1. + */ private _reportDurationsTelemetry(durations: CompletionDurations): void { - - if (this._telemetryGate++ % 230 !== 0) { + if (Math.random() > 0.0001) { // 0.01% return; } diff --git a/src/vs/editor/contrib/suggest/browser/suggestWidget.ts b/src/vs/editor/contrib/suggest/browser/suggestWidget.ts index 0ad604159e1a..8c77c920b400 100644 --- a/src/vs/editor/contrib/suggest/browser/suggestWidget.ts +++ b/src/vs/editor/contrib/suggest/browser/suggestWidget.ts @@ -35,6 +35,7 @@ import { canExpandCompletionItem, SuggestDetailsOverlay, SuggestDetailsWidget } import { ItemRenderer } from './suggestWidgetRenderer.js'; import { getListStyles } from '../../../../platform/theme/browser/defaultStyles.js'; import { status } from '../../../../base/browser/ui/aria/aria.js'; +import { CompletionItemKinds } from '../../../common/languages.js'; /** * Suggest widget colors @@ -236,17 +237,19 @@ export class SuggestWidget implements IDisposable { getAriaLabel: (item: CompletionItem) => { let label = item.textLabel; + const kindLabel = CompletionItemKinds.toLabel(item.completion.kind); 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}, {3}', label, detail, description, kindLabel); } else if (detail) { - label = nls.localize('label.detail', '{0} {1}', label, detail); + label = nls.localize('label.detail', '{0} {1}, {2}', label, detail, kindLabel); } else if (description) { - label = nls.localize('label.desc', '{0}, {1}', label, description); + label = nls.localize('label.desc', '{0}, {1}, {2}', label, description, kindLabel); } + } else { + label = nls.localize('label', '{0}, {1}', label, kindLabel); } - if (!item.isResolved || !this._isDetailsVisible()) { return label; } @@ -270,7 +273,7 @@ export class SuggestWidget implements IDisposable { const applyStatusBarStyle = () => this.element.domNode.classList.toggle('with-status-bar', this.editor.getOption(EditorOption.suggest).showStatusBar); applyStatusBarStyle(); - this._disposables.add(_themeService.onDidColorThemeChange(t => this._onThemeChange(t))); + this._disposables.add(_themeService.onDidColorThemeChange(t => this._onThemeChange(t.theme))); this._onThemeChange(_themeService.getColorTheme()); this._disposables.add(this._list.onMouseDown(e => this._onListMouseDownOrTap(e))); @@ -1041,3 +1044,4 @@ export class SuggestContentWidget implements IContentWidget { this._position = position; } } + diff --git a/src/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.ts b/src/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.ts index de82ea26de3d..118a9e66da40 100644 --- a/src/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.ts +++ b/src/vs/editor/contrib/wordHighlighter/browser/wordHighlighter.ts @@ -548,7 +548,7 @@ class WordHighlighter { // ignore typing & other // need to check if the model is a notebook cell, should not stop if nb - if (e.source !== 'api' && e.reason !== CursorChangeReason.Explicit && this.editor.getModel()?.uri.scheme !== Schemas.vscodeNotebookCell) { + if (e.source !== 'api' && e.reason !== CursorChangeReason.Explicit) { this._stopAll(); return; } diff --git a/src/vs/editor/contrib/zoneWidget/browser/zoneWidget.ts b/src/vs/editor/contrib/zoneWidget/browser/zoneWidget.ts index 611fcb7e21c1..2a65960d9831 100644 --- a/src/vs/editor/contrib/zoneWidget/browser/zoneWidget.ts +++ b/src/vs/editor/contrib/zoneWidget/browser/zoneWidget.ts @@ -117,13 +117,15 @@ class Arrow { private static readonly _IdGenerator = new IdGenerator('.arrow-decoration-'); private readonly _ruleName = Arrow._IdGenerator.nextId(); - private readonly _decorations = this._editor.createDecorationsCollection(); + private readonly _decorations: IEditorDecorationsCollection; private _color: string | null = null; private _height: number = -1; constructor( private readonly _editor: ICodeEditor - ) { } + ) { + this._decorations = this._editor.createDecorationsCollection(); + } dispose(): void { this.hide(); diff --git a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts index 2b7abea38c13..dffab646d2b4 100644 --- a/src/vs/editor/standalone/browser/standaloneCodeEditor.ts +++ b/src/vs/editor/standalone/browser/standaloneCodeEditor.ts @@ -293,7 +293,7 @@ export class StandaloneCodeEditor extends CodeEditorWidget implements IStandalon createAriaDomNode(options.ariaContainerElement); - setHoverDelegateFactory((placement, enableInstantHover) => instantiationService.createInstance(WorkbenchHoverDelegate, placement, enableInstantHover, {})); + setHoverDelegateFactory((placement, enableInstantHover) => instantiationService.createInstance(WorkbenchHoverDelegate, placement, { instantHover: enableInstantHover }, {})); setBaseLayerHoverDelegate(hoverService); } diff --git a/src/vs/editor/standalone/browser/standaloneThemeService.ts b/src/vs/editor/standalone/browser/standaloneThemeService.ts index 8a697f653ff5..4446b6932906 100644 --- a/src/vs/editor/standalone/browser/standaloneThemeService.ts +++ b/src/vs/editor/standalone/browser/standaloneThemeService.ts @@ -16,7 +16,7 @@ import { hc_black, hc_light, vs, vs_dark } from '../common/themes.js'; import { IEnvironmentService } from '../../../platform/environment/common/environment.js'; import { Registry } from '../../../platform/registry/common/platform.js'; import { asCssVariableName, ColorIdentifier, Extensions, IColorRegistry } from '../../../platform/theme/common/colorRegistry.js'; -import { Extensions as ThemingExtensions, ICssStyleCollector, IFileIconTheme, IProductIconTheme, IThemingRegistry, ITokenStyle } from '../../../platform/theme/common/themeService.js'; +import { Extensions as ThemingExtensions, ICssStyleCollector, IFileIconTheme, IProductIconTheme, IThemingRegistry, ITokenStyle, IThemeChangeEvent } from '../../../platform/theme/common/themeService.js'; import { IDisposable, Disposable } from '../../../base/common/lifecycle.js'; import { ColorScheme, isDark, isHighContrast } from '../../../platform/theme/common/theme.js'; import { getIconsStyleSheet, UnthemedProductIconTheme } from '../../../platform/theme/browser/iconsStyleSheet.js'; @@ -213,7 +213,7 @@ export class StandaloneThemeService extends Disposable implements IStandaloneThe declare readonly _serviceBrand: undefined; - private readonly _onColorThemeChange = this._register(new Emitter()); + private readonly _onColorThemeChange = this._register(new Emitter()); public readonly onDidColorThemeChange = this._onColorThemeChange.event; private readonly _onFileIconThemeChange = this._register(new Emitter()); @@ -403,7 +403,7 @@ export class StandaloneThemeService extends Disposable implements IStandaloneThe this._updateCSS(); TokenizationRegistry.setColorMap(colorMap); - this._onColorThemeChange.fire(this._theme); + this._onColorThemeChange.fire({ theme: this._theme }); } private _updateCSS(): void { diff --git a/src/vs/editor/standalone/test/browser/standaloneLanguages.test.ts b/src/vs/editor/standalone/test/browser/standaloneLanguages.test.ts index 0fa82cf782b2..def6b624a189 100644 --- a/src/vs/editor/standalone/test/browser/standaloneLanguages.test.ts +++ b/src/vs/editor/standalone/test/browser/standaloneLanguages.test.ts @@ -17,7 +17,7 @@ import { IStandaloneTheme, IStandaloneThemeData, IStandaloneThemeService } from import { UnthemedProductIconTheme } from '../../../../platform/theme/browser/iconsStyleSheet.js'; import { ColorIdentifier } from '../../../../platform/theme/common/colorRegistry.js'; import { ColorScheme } from '../../../../platform/theme/common/theme.js'; -import { IColorTheme, IFileIconTheme, IProductIconTheme, ITokenStyle } from '../../../../platform/theme/common/themeService.js'; +import { IFileIconTheme, IProductIconTheme, IThemeChangeEvent, ITokenStyle } from '../../../../platform/theme/common/themeService.js'; suite('TokenizationSupport2Adapter', () => { @@ -92,7 +92,7 @@ suite('TokenizationSupport2Adapter', () => { public getProductIconTheme(): IProductIconTheme { return this._builtInProductIconTheme; } - public readonly onDidColorThemeChange = new Emitter().event; + public readonly onDidColorThemeChange = new Emitter().event; public readonly onDidFileIconThemeChange = new Emitter().event; public readonly onDidProductIconThemeChange = new Emitter().event; } diff --git a/src/vs/editor/test/browser/widget/diffEditorWidget.test.ts b/src/vs/editor/test/browser/widget/diffEditorWidget.test.ts index eeac9f526e54..ad354a957408 100644 --- a/src/vs/editor/test/browser/widget/diffEditorWidget.test.ts +++ b/src/vs/editor/test/browser/widget/diffEditorWidget.test.ts @@ -3,16 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import assert from 'assert'; -import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { UnchangedRegion } from '../../../browser/widget/diffEditor/diffEditorViewModel.js'; -import { LineRange } from '../../../common/core/lineRange.js'; -import { DetailedLineRangeMapping } from '../../../common/diff/rangeMapping.js'; +import * as assert from 'assert'; +import { UnchangedRegion } from 'vs/editor/browser/widget/diffEditor/diffEditorViewModel'; +import { LineRange } from 'vs/editor/common/core/lineRange'; +import { DetailedLineRangeMapping } from 'vs/editor/common/diff/rangeMapping'; suite('DiffEditorWidget2', () => { - - ensureNoDisposablesAreLeakedInTestSuite(); - suite('UnchangedRegion', () => { function serialize(regions: UnchangedRegion[]): unknown { return regions.map(r => `${r.originalUnchangedRange} - ${r.modifiedUnchangedRange}`); diff --git a/src/vs/editor/test/common/codecs/markdownDecoder.test.ts b/src/vs/editor/test/common/codecs/markdownDecoder.test.ts index bff4b428ae1d..d0ea73d7fa23 100644 --- a/src/vs/editor/test/common/codecs/markdownDecoder.test.ts +++ b/src/vs/editor/test/common/codecs/markdownDecoder.test.ts @@ -3,23 +3,28 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import assert from 'assert'; import { TestDecoder } from '../utils/testDecoder.js'; import { Range } from '../../../common/core/range.js'; import { VSBuffer } from '../../../../base/common/buffer.js'; import { newWriteableStream } from '../../../../base/common/stream.js'; import { Tab } from '../../../common/codecs/simpleCodec/tokens/tab.js'; import { Word } from '../../../common/codecs/simpleCodec/tokens/word.js'; +import { Dash } from '../../../common/codecs/simpleCodec/tokens/dash.js'; import { Space } from '../../../common/codecs/simpleCodec/tokens/space.js'; import { NewLine } from '../../../common/codecs/linesCodec/tokens/newLine.js'; +import { FormFeed } from '../../../common/codecs/simpleCodec/tokens/formFeed.js'; import { VerticalTab } from '../../../common/codecs/simpleCodec/tokens/verticalTab.js'; import { MarkdownLink } from '../../../common/codecs/markdownCodec/tokens/markdownLink.js'; +import { CarriageReturn } from '../../../common/codecs/linesCodec/tokens/carriageReturn.js'; +import { MarkdownImage } from '../../../common/codecs/markdownCodec/tokens/markdownImage.js'; +import { ExclamationMark } from '../../../common/codecs/simpleCodec/tokens/exclamationMark.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; +import { MarkdownComment } from '../../../common/codecs/markdownCodec/tokens/markdownComment.js'; +import { LeftBracket, RightBracket } from '../../../common/codecs/simpleCodec/tokens/brackets.js'; import { MarkdownDecoder, TMarkdownToken } from '../../../common/codecs/markdownCodec/markdownDecoder.js'; -import { FormFeed } from '../../../common/codecs/simpleCodec/tokens/formFeed.js'; import { LeftParenthesis, RightParenthesis } from '../../../common/codecs/simpleCodec/tokens/parentheses.js'; -import { LeftBracket, RightBracket } from '../../../common/codecs/simpleCodec/tokens/brackets.js'; -import { CarriageReturn } from '../../../common/codecs/linesCodec/tokens/carriageReturn.js'; -import assert from 'assert'; +import { LeftAngleBracket, RightAngleBracket } from '../../../common/codecs/simpleCodec/tokens/angleBrackets.js'; /** * A reusable test utility that asserts that a `TestMarkdownDecoder` instance @@ -55,278 +60,785 @@ export class TestMarkdownDecoder extends TestDecoder { const testDisposables = ensureNoDisposablesAreLeakedInTestSuite(); - test('produces expected tokens', async () => { - const test = testDisposables.add( - new TestMarkdownDecoder(), - ); - - await test.run( - ' hello world\nhow are\t you [caption text](./some/file/path/refer🎨nce.md)?\v\n\n[(example)](another/path/with[-and-]-chars/folder)\t \n\t[#file:something.txt](/absolute/path/to/something.txt)', - [ - // first line - new Space(new Range(1, 1, 1, 2)), - new Word(new Range(1, 2, 1, 7), 'hello'), - new Space(new Range(1, 7, 1, 8)), - new Word(new Range(1, 8, 1, 13), 'world'), - new NewLine(new Range(1, 13, 1, 14)), - // second line - new Word(new Range(2, 1, 2, 4), 'how'), - new Space(new Range(2, 4, 2, 5)), - new Word(new Range(2, 5, 2, 8), 'are'), - new Tab(new Range(2, 8, 2, 9)), - new Space(new Range(2, 9, 2, 10)), - new Word(new Range(2, 10, 2, 13), 'you'), - new Space(new Range(2, 13, 2, 14)), - new MarkdownLink(2, 14, '[caption text]', '(./some/file/path/refer🎨nce.md)'), - new Word(new Range(2, 60, 2, 61), '?'), - new VerticalTab(new Range(2, 61, 2, 62)), - new NewLine(new Range(2, 62, 2, 63)), - // third line - new NewLine(new Range(3, 1, 3, 2)), - // fourth line - new MarkdownLink(4, 1, '[(example)]', '(another/path/with[-and-]-chars/folder)'), - new Tab(new Range(4, 51, 4, 52)), - new Space(new Range(4, 52, 4, 53)), - new NewLine(new Range(4, 53, 4, 54)), - // fifth line - new Tab(new Range(5, 1, 5, 2)), - new MarkdownLink(5, 2, '[#file:something.txt]', '(/absolute/path/to/something.txt)'), - ], - ); - }); + suite('• general', () => { + test('• base cases', async () => { + const test = testDisposables.add( + new TestMarkdownDecoder(), + ); - test('handles complex cases', async () => { - const test = testDisposables.add( - new TestMarkdownDecoder(), - ); - - const inputLines = [ - // tests that the link caption contain a chat prompt `#file:` reference, while - // the file path can contain other `graphical characters` - '\v\t[#file:./another/path/to/file.txt](./real/filepath/file◆name.md)', - // tests that the link file path contain a chat prompt `#file:` reference, - // `spaces`, `emojies`, and other `graphical characters` - ' [reference ∘ label](/absolute/pa th/to-#file:file.txt/f🥸⚡️le.md)', - // tests that link caption and file path can contain `parentheses`, `spaces`, and - // `emojies` - '\f[!(hello)!](./w(())rld/nice-🦚-filen(a)me.git))\n\t', - // tests that the link caption can be empty, while the file path can contain `square brackets` - '[](./s[]me/pa[h!) ', - ]; - - await test.run( - inputLines, - [ - // `1st` line - new VerticalTab(new Range(1, 1, 1, 2)), - new Tab(new Range(1, 2, 1, 3)), - new MarkdownLink(1, 3, '[#file:./another/path/to/file.txt]', '(./real/filepath/file◆name.md)'), - new NewLine(new Range(1, 67, 1, 68)), - // `2nd` line - new Space(new Range(2, 1, 2, 2)), - new MarkdownLink(2, 2, '[reference ∘ label]', '(/absolute/pa th/to-#file:file.txt/f🥸⚡️le.md)'), - new NewLine(new Range(2, 67, 2, 68)), - // `3rd` line - new FormFeed(new Range(3, 1, 3, 2)), - new MarkdownLink(3, 2, '[!(hello)!]', '(./w(())rld/nice-🦚-filen(a)me.git)'), - new RightParenthesis(new Range(3, 48, 3, 49)), - new NewLine(new Range(3, 49, 3, 50)), - // `4th` line - new Tab(new Range(4, 1, 4, 2)), - new NewLine(new Range(4, 2, 4, 3)), - // `5th` line - new MarkdownLink(5, 1, '[]', '(./s[]me/pa[h!)'), - new Space(new Range(5, 18, 5, 19)), - ], - ); - }); + await test.run( + [ + // basic text + ' hello world', + // text with markdown link and special characters in the filename + 'how are\t you [caption text](./some/file/path/refer🎨nce.md)?\v', + // empty line + '', + // markdown link with special characters in the link caption and path + '[(example!)](another/path/with[-and-]-chars/folder)\t ', + // markdown link `#file` variable in the caption and with absolute path + '\t[#file:something.txt](/absolute/path/to/something.txt)', + // text with a commented out markdown link + '\v\f machines must suffer', + ], + [ + // first line + new Space(new Range(1, 1, 1, 2)), + new Word(new Range(1, 2, 1, 7), 'hello'), + new Space(new Range(1, 7, 1, 8)), + new Word(new Range(1, 8, 1, 13), 'world'), + new NewLine(new Range(1, 13, 1, 14)), + // second line + new Word(new Range(2, 1, 2, 4), 'how'), + new Space(new Range(2, 4, 2, 5)), + new Word(new Range(2, 5, 2, 8), 'are'), + new Tab(new Range(2, 8, 2, 9)), + new Space(new Range(2, 9, 2, 10)), + new Word(new Range(2, 10, 2, 13), 'you'), + new Space(new Range(2, 13, 2, 14)), + new MarkdownLink(2, 14, '[caption text]', '(./some/file/path/refer🎨nce.md)'), + new Word(new Range(2, 60, 2, 61), '?'), + new VerticalTab(new Range(2, 61, 2, 62)), + new NewLine(new Range(2, 62, 2, 63)), + // third line + new NewLine(new Range(3, 1, 3, 2)), + // fourth line + new MarkdownLink(4, 1, '[(example!)]', '(another/path/with[-and-]-chars/folder)'), + new Tab(new Range(4, 52, 4, 53)), + new Space(new Range(4, 53, 4, 54)), + new NewLine(new Range(4, 54, 4, 55)), + // fifth line + new Tab(new Range(5, 1, 5, 2)), + new MarkdownLink(5, 2, '[#file:something.txt]', '(/absolute/path/to/something.txt)'), + new NewLine(new Range(5, 56, 5, 57)), + // sixth line + new VerticalTab(new Range(6, 1, 6, 2)), + new FormFeed(new Range(6, 2, 6, 3)), + new Space(new Range(6, 3, 6, 4)), + new Word(new Range(6, 4, 6, 12), 'machines'), + new Space(new Range(6, 12, 6, 13)), + new Word(new Range(6, 13, 6, 17), 'must'), + new Space(new Range(6, 17, 6, 18)), + new MarkdownComment(new Range(6, 18, 6, 18 + 41), ''), + new Space(new Range(6, 59, 6, 60)), + new Word(new Range(6, 60, 6, 66), 'suffer'), + ], + ); + }); - suite('broken links', () => { - test('incomplete/invalid links', async () => { + test('• nuanced', async () => { const test = testDisposables.add( new TestMarkdownDecoder(), ); const inputLines = [ - // incomplete link reference with empty caption - '[ ](./real/file path/file⇧name.md', - // space between caption and reference is disallowed - '[link text] (./file path/name.txt)', + // tests that the link caption contain a chat prompt `#file:` reference, while + // the file path can contain other `graphical characters` + '\v\t[#file:./another/path/to/file.txt](./real/file!path/file◆name.md)', + // tests that the link file path contain a chat prompt `#file:` reference, + // `spaces`, `emojies`, and other `graphical characters` + ' [reference ∘ label](/absolute/pa th/to-#file:file.txt/f🥸⚡️le.md)', + // tests that link caption and file path can contain `parentheses`, `spaces`, and + // `emojies` + '\f[!(hello)!](./w(())rld/nice-🦚-filen(a).git))\n\t', + // tests that the link caption can be empty, while the file path can contain `square brackets` + '[](./s[]me/pa[h!) ', ]; await test.run( inputLines, [ // `1st` line - new LeftBracket(new Range(1, 1, 1, 2)), - new Space(new Range(1, 2, 1, 3)), - new RightBracket(new Range(1, 3, 1, 4)), - new LeftParenthesis(new Range(1, 4, 1, 5)), - new Word(new Range(1, 5, 1, 5 + 11), './real/file'), - new Space(new Range(1, 16, 1, 17)), - new Word(new Range(1, 17, 1, 17 + 17), 'path/file⇧name.md'), - new NewLine(new Range(1, 34, 1, 35)), + new VerticalTab(new Range(1, 1, 1, 2)), + new Tab(new Range(1, 2, 1, 3)), + new MarkdownLink(1, 3, '[#file:./another/path/to/file.txt]', '(./real/file!path/file◆name.md)'), + new NewLine(new Range(1, 68, 1, 69)), // `2nd` line - new LeftBracket(new Range(2, 1, 2, 2)), - new Word(new Range(2, 2, 2, 2 + 4), 'link'), - new Space(new Range(2, 6, 2, 7)), - new Word(new Range(2, 7, 2, 7 + 4), 'text'), - new RightBracket(new Range(2, 11, 2, 12)), - new Space(new Range(2, 12, 2, 13)), - new LeftParenthesis(new Range(2, 13, 2, 14)), - new Word(new Range(2, 14, 2, 14 + 6), './file'), - new Space(new Range(2, 20, 2, 21)), - new Word(new Range(2, 21, 2, 21 + 13), 'path/name.txt'), - new RightParenthesis(new Range(2, 34, 2, 35)), + new Space(new Range(2, 1, 2, 2)), + new MarkdownLink(2, 2, '[reference ∘ label]', '(/absolute/pa th/to-#file:file.txt/f🥸⚡️le.md)'), + new NewLine(new Range(2, 67, 2, 68)), + // `3rd` line + new FormFeed(new Range(3, 1, 3, 2)), + new MarkdownLink(3, 2, '[!(hello)!]', '(./w(())rld/nice-🦚-filen(a).git)'), + new RightParenthesis(new Range(3, 50, 3, 51)), + new NewLine(new Range(3, 51, 3, 52)), + // `4th` line + new Tab(new Range(4, 1, 4, 2)), + new NewLine(new Range(4, 2, 4, 3)), + // `5th` line + new MarkdownLink(5, 1, '[]', '(./s[]me/pa[h!)'), + new Space(new Range(5, 24, 5, 25)), ], ); }); + }); - suite('stop characters inside caption/reference (new lines)', () => { - for (const stopCharacter of [CarriageReturn, NewLine]) { - let characterName = ''; + suite('• links', () => { + suite('• broken', () => { + test('• invalid', async () => { + const test = testDisposables.add( + new TestMarkdownDecoder(), + ); - if (stopCharacter === CarriageReturn) { - characterName = '\\r'; - } - if (stopCharacter === NewLine) { - characterName = '\\n'; - } + const inputLines = [ + // incomplete link reference with empty caption + '[ ](./real/file path/file⇧name.md', + // space between caption and reference is disallowed + '[link text] (./file path/name.txt)', + ]; - assert( - characterName !== '', - 'The "characterName" must be set, got "empty line".', + await test.run( + inputLines, + [ + // `1st` line + new LeftBracket(new Range(1, 1, 1, 2)), + new Space(new Range(1, 2, 1, 3)), + new RightBracket(new Range(1, 3, 1, 4)), + new LeftParenthesis(new Range(1, 4, 1, 5)), + new Word(new Range(1, 5, 1, 5 + 11), './real/file'), + new Space(new Range(1, 16, 1, 17)), + new Word(new Range(1, 17, 1, 17 + 17), 'path/file⇧name.md'), + new NewLine(new Range(1, 34, 1, 35)), + // `2nd` line + new LeftBracket(new Range(2, 1, 2, 2)), + new Word(new Range(2, 2, 2, 2 + 4), 'link'), + new Space(new Range(2, 6, 2, 7)), + new Word(new Range(2, 7, 2, 7 + 4), 'text'), + new RightBracket(new Range(2, 11, 2, 12)), + new Space(new Range(2, 12, 2, 13)), + new LeftParenthesis(new Range(2, 13, 2, 14)), + new Word(new Range(2, 14, 2, 14 + 6), './file'), + new Space(new Range(2, 20, 2, 21)), + new Word(new Range(2, 21, 2, 21 + 13), 'path/name.txt'), + new RightParenthesis(new Range(2, 34, 2, 35)), + ], ); + }); - test(`stop character - "${characterName}"`, async () => { - const test = testDisposables.add( - new TestMarkdownDecoder(), - ); + suite('• stop characters inside caption/reference (new lines)', () => { + for (const stopCharacter of [CarriageReturn, NewLine]) { + let characterName = ''; + + if (stopCharacter === CarriageReturn) { + characterName = '\\r'; + } + if (stopCharacter === NewLine) { + characterName = '\\n'; + } - const inputLines = [ - // stop character inside link caption - `[haa${stopCharacter.symbol}loů](./real/💁/name.txt)`, - // stop character inside link reference - `[ref text](/etc/pat${stopCharacter.symbol}h/to/file.md)`, - // stop character between line caption and link reference is disallowed - `[text]${stopCharacter.symbol}(/etc/ path/file.md)`, - ]; - - - await test.run( - inputLines, - [ - // `1st` input line - new LeftBracket(new Range(1, 1, 1, 2)), - new Word(new Range(1, 2, 1, 2 + 3), 'haa'), - new stopCharacter(new Range(1, 5, 1, 6)), // <- stop character - new Word(new Range(2, 1, 2, 1 + 3), 'loů'), - new RightBracket(new Range(2, 4, 2, 5)), - new LeftParenthesis(new Range(2, 5, 2, 6)), - new Word(new Range(2, 6, 2, 6 + 18), './real/💁/name.txt'), - new RightParenthesis(new Range(2, 24, 2, 25)), - new NewLine(new Range(2, 25, 2, 26)), - // `2nd` input line - new LeftBracket(new Range(3, 1, 3, 2)), - new Word(new Range(3, 2, 3, 2 + 3), 'ref'), - new Space(new Range(3, 5, 3, 6)), - new Word(new Range(3, 6, 3, 6 + 4), 'text'), - new RightBracket(new Range(3, 10, 3, 11)), - new LeftParenthesis(new Range(3, 11, 3, 12)), - new Word(new Range(3, 12, 3, 12 + 8), '/etc/pat'), - new stopCharacter(new Range(3, 20, 3, 21)), // <- stop character - new Word(new Range(4, 1, 4, 1 + 12), 'h/to/file.md'), - new RightParenthesis(new Range(4, 13, 4, 14)), - new NewLine(new Range(4, 14, 4, 15)), - // `3nd` input line - new LeftBracket(new Range(5, 1, 5, 2)), - new Word(new Range(5, 2, 5, 2 + 4), 'text'), - new RightBracket(new Range(5, 6, 5, 7)), - new stopCharacter(new Range(5, 7, 5, 8)), // <- stop character - new LeftParenthesis(new Range(6, 1, 6, 2)), - new Word(new Range(6, 2, 6, 2 + 5), '/etc/'), - new Space(new Range(6, 7, 6, 8)), - new Word(new Range(6, 8, 6, 8 + 12), 'path/file.md'), - new RightParenthesis(new Range(6, 20, 6, 21)), - ], + assert( + characterName !== '', + 'The "characterName" must be set, got "empty line".', ); - }); - } - }); - /** - * Same as above but these stop characters do not move the caret to the next line. - */ - suite('stop characters inside caption/reference (same line)', () => { - for (const stopCharacter of [VerticalTab, FormFeed]) { - let characterName = ''; + test(`• stop character - "${characterName}"`, async () => { + const test = testDisposables.add( + new TestMarkdownDecoder(), + ); + + const inputLines = [ + // stop character inside link caption + `[haa${stopCharacter.symbol}loů](./real/💁/name.txt)`, + // stop character inside link reference + `[ref text](/etc/pat${stopCharacter.symbol}h/to/file.md)`, + // stop character between line caption and link reference is disallowed + `[text]${stopCharacter.symbol}(/etc/ path/file.md)`, + ]; + - if (stopCharacter === VerticalTab) { - characterName = '\\v'; + await test.run( + inputLines, + [ + // `1st` input line + new LeftBracket(new Range(1, 1, 1, 2)), + new Word(new Range(1, 2, 1, 2 + 3), 'haa'), + new stopCharacter(new Range(1, 5, 1, 6)), // <- stop character + new Word(new Range(2, 1, 2, 1 + 3), 'loů'), + new RightBracket(new Range(2, 4, 2, 5)), + new LeftParenthesis(new Range(2, 5, 2, 6)), + new Word(new Range(2, 6, 2, 6 + 18), './real/💁/name.txt'), + new RightParenthesis(new Range(2, 24, 2, 25)), + new NewLine(new Range(2, 25, 2, 26)), + // `2nd` input line + new LeftBracket(new Range(3, 1, 3, 2)), + new Word(new Range(3, 2, 3, 2 + 3), 'ref'), + new Space(new Range(3, 5, 3, 6)), + new Word(new Range(3, 6, 3, 6 + 4), 'text'), + new RightBracket(new Range(3, 10, 3, 11)), + new LeftParenthesis(new Range(3, 11, 3, 12)), + new Word(new Range(3, 12, 3, 12 + 8), '/etc/pat'), + new stopCharacter(new Range(3, 20, 3, 21)), // <- stop character + new Word(new Range(4, 1, 4, 1 + 12), 'h/to/file.md'), + new RightParenthesis(new Range(4, 13, 4, 14)), + new NewLine(new Range(4, 14, 4, 15)), + // `3nd` input line + new LeftBracket(new Range(5, 1, 5, 2)), + new Word(new Range(5, 2, 5, 2 + 4), 'text'), + new RightBracket(new Range(5, 6, 5, 7)), + new stopCharacter(new Range(5, 7, 5, 8)), // <- stop character + new LeftParenthesis(new Range(6, 1, 6, 2)), + new Word(new Range(6, 2, 6, 2 + 5), '/etc/'), + new Space(new Range(6, 7, 6, 8)), + new Word(new Range(6, 8, 6, 8 + 12), 'path/file.md'), + new RightParenthesis(new Range(6, 20, 6, 21)), + ], + ); + }); } - if (stopCharacter === FormFeed) { - characterName = '\\f'; + }); + + /** + * Same as above but these stop characters do not move the caret to the next line. + */ + suite('• stop characters inside caption/reference (same line)', () => { + for (const stopCharacter of [VerticalTab, FormFeed]) { + let characterName = ''; + + if (stopCharacter === VerticalTab) { + characterName = '\\v'; + } + if (stopCharacter === FormFeed) { + characterName = '\\f'; + } + + assert( + characterName !== '', + 'The "characterName" must be set, got "empty line".', + ); + + test(`• stop character - "${characterName}"`, async () => { + const test = testDisposables.add( + new TestMarkdownDecoder(), + ); + + const inputLines = [ + // stop character inside link caption + `[haa${stopCharacter.symbol}loů](./real/💁/name.txt)`, + // stop character inside link reference + `[ref text](/etc/pat${stopCharacter.symbol}h/to/file.md)`, + // stop character between line caption and link reference is disallowed + `[text]${stopCharacter.symbol}(/etc/ path/file.md)`, + ]; + + + await test.run( + inputLines, + [ + // `1st` input line + new LeftBracket(new Range(1, 1, 1, 2)), + new Word(new Range(1, 2, 1, 2 + 3), 'haa'), + new stopCharacter(new Range(1, 5, 1, 6)), // <- stop character + new Word(new Range(1, 6, 1, 6 + 3), 'loů'), + new RightBracket(new Range(1, 9, 1, 10)), + new LeftParenthesis(new Range(1, 10, 1, 11)), + new Word(new Range(1, 11, 1, 11 + 18), './real/💁/name.txt'), + new RightParenthesis(new Range(1, 29, 1, 30)), + new NewLine(new Range(1, 30, 1, 31)), + // `2nd` input line + new LeftBracket(new Range(2, 1, 2, 2)), + new Word(new Range(2, 2, 2, 2 + 3), 'ref'), + new Space(new Range(2, 5, 2, 6)), + new Word(new Range(2, 6, 2, 6 + 4), 'text'), + new RightBracket(new Range(2, 10, 2, 11)), + new LeftParenthesis(new Range(2, 11, 2, 12)), + new Word(new Range(2, 12, 2, 12 + 8), '/etc/pat'), + new stopCharacter(new Range(2, 20, 2, 21)), // <- stop character + new Word(new Range(2, 21, 2, 21 + 12), 'h/to/file.md'), + new RightParenthesis(new Range(2, 33, 2, 34)), + new NewLine(new Range(2, 34, 2, 35)), + // `3nd` input line + new LeftBracket(new Range(3, 1, 3, 2)), + new Word(new Range(3, 2, 3, 2 + 4), 'text'), + new RightBracket(new Range(3, 6, 3, 7)), + new stopCharacter(new Range(3, 7, 3, 8)), // <- stop character + new LeftParenthesis(new Range(3, 8, 3, 9)), + new Word(new Range(3, 9, 3, 9 + 5), '/etc/'), + new Space(new Range(3, 14, 3, 15)), + new Word(new Range(3, 15, 3, 15 + 12), 'path/file.md'), + new RightParenthesis(new Range(3, 27, 3, 28)), + ], + ); + }); } + }); + }); + }); + + + suite('• images', () => { + suite('• general', () => { + test('• base cases', async () => { + const test = testDisposables.add( + new TestMarkdownDecoder(), + ); + + const inputData = [ + '\t![alt text](./some/path/to/file.jpg) ', + 'plain text \f![label](./image.png)\v and more text', + '![](/var/images/default) following text', + ]; + + await test.run( + inputData, + [ + // `1st` + new Tab(new Range(1, 1, 1, 2)), + new MarkdownImage(1, 2, '![alt text]', '(./some/path/to/file.jpg)'), + new Space(new Range(1, 38, 1, 39)), + new NewLine(new Range(1, 39, 1, 40)), + // `2nd` + new Word(new Range(2, 1, 2, 6), 'plain'), + new Space(new Range(2, 6, 2, 7)), + new Word(new Range(2, 7, 2, 11), 'text'), + new Space(new Range(2, 11, 2, 12)), + new FormFeed(new Range(2, 12, 2, 13)), + new MarkdownImage(2, 13, '![label]', '(./image.png)'), + new VerticalTab(new Range(2, 34, 2, 35)), + new Space(new Range(2, 35, 2, 36)), + new Word(new Range(2, 36, 2, 39), 'and'), + new Space(new Range(2, 39, 2, 40)), + new Word(new Range(2, 40, 2, 44), 'more'), + new Space(new Range(2, 44, 2, 45)), + new Word(new Range(2, 45, 2, 49), 'text'), + new NewLine(new Range(2, 49, 2, 50)), + // `3rd` + new MarkdownImage(3, 1, '![]', '(/var/images/default)'), + new Space(new Range(3, 25, 3, 26)), + new Word(new Range(3, 26, 3, 35), 'following'), + new Space(new Range(3, 35, 3, 36)), + new Word(new Range(3, 36, 3, 40), 'text'), + ], + ); + }); + + test('• nuanced', async () => { + const test = testDisposables.add( + new TestMarkdownDecoder(), + ); + + const inputData = [ + '\t![](./s☻me/path/to/file.jpeg) ', + 'raw text \f![(/1.png)](./image-🥸.png)\v and more text', + // '![](/var/images/default) following text', + ]; + + await test.run( + inputData, + [ + // `1st` + new Tab(new Range(1, 1, 1, 2)), + new MarkdownImage(1, 2, '![]', '(./s☻me/path/to/file.jpeg)'), + new Space(new Range(1, 47, 1, 48)), + new NewLine(new Range(1, 48, 1, 49)), + // `2nd` + new Word(new Range(2, 1, 2, 4), 'raw'), + new Space(new Range(2, 4, 2, 5)), + new Word(new Range(2, 5, 2, 9), 'text'), + new Space(new Range(2, 9, 2, 10)), + new FormFeed(new Range(2, 10, 2, 11)), + new MarkdownImage(2, 11, '![(/1.png)]', '(./image-🥸.png)'), + new VerticalTab(new Range(2, 38, 2, 39)), + new Space(new Range(2, 39, 2, 40)), + new Word(new Range(2, 40, 2, 43), 'and'), + new Space(new Range(2, 43, 2, 44)), + new Word(new Range(2, 44, 2, 48), 'more'), + new Space(new Range(2, 48, 2, 49)), + new Word(new Range(2, 49, 2, 53), 'text'), + ], + ); + }); + }); + + suite('• broken', () => { + test('• invalid', async () => { + const test = testDisposables.add( + new TestMarkdownDecoder(), + ); + + const inputLines = [ + // incomplete link reference with empty caption + '![ ](./real/file path/file★name.webp', + // space between caption and reference is disallowed + '\f![link text] (./file path/name.jpg)', + // new line inside the link reference + '\v![ ](./file\npath/name.jpg )', + ]; - assert( - characterName !== '', - 'The "characterName" must be set, got "empty line".', + await test.run( + inputLines, + [ + // `1st` line + new ExclamationMark(new Range(1, 1, 1, 2)), + new LeftBracket(new Range(1, 2, 1, 3)), + new Space(new Range(1, 3, 1, 4)), + new RightBracket(new Range(1, 4, 1, 5)), + new LeftParenthesis(new Range(1, 5, 1, 6)), + new Word(new Range(1, 6, 1, 6 + 11), './real/file'), + new Space(new Range(1, 17, 1, 18)), + new Word(new Range(1, 18, 1, 18 + 19), 'path/file★name.webp'), + new NewLine(new Range(1, 37, 1, 38)), + // `2nd` line + new FormFeed(new Range(2, 1, 2, 2)), + new ExclamationMark(new Range(2, 2, 2, 3)), + new LeftBracket(new Range(2, 3, 2, 4)), + new Word(new Range(2, 4, 2, 4 + 4), 'link'), + new Space(new Range(2, 8, 2, 9)), + new Word(new Range(2, 9, 2, 9 + 4), 'text'), + new RightBracket(new Range(2, 13, 2, 14)), + new Space(new Range(2, 14, 2, 15)), + new LeftParenthesis(new Range(2, 15, 2, 16)), + new Word(new Range(2, 16, 2, 16 + 6), './file'), + new Space(new Range(2, 22, 2, 23)), + new Word(new Range(2, 23, 2, 23 + 13), 'path/name.jpg'), + new RightParenthesis(new Range(2, 36, 2, 37)), + new NewLine(new Range(2, 37, 2, 38)), + // `3rd` line + new VerticalTab(new Range(3, 1, 3, 2)), + new ExclamationMark(new Range(3, 2, 3, 3)), + new LeftBracket(new Range(3, 3, 3, 4)), + new Space(new Range(3, 4, 3, 5)), + new RightBracket(new Range(3, 5, 3, 6)), + new LeftParenthesis(new Range(3, 6, 3, 7)), + new Word(new Range(3, 7, 3, 7 + 6), './file'), + new NewLine(new Range(3, 13, 3, 14)), + new Word(new Range(4, 1, 4, 1 + 13), 'path/name.jpg'), + new Space(new Range(4, 14, 4, 15)), + new RightParenthesis(new Range(4, 15, 4, 16)), + ], ); + }); + + suite('• stop characters inside caption/reference (new lines)', () => { + for (const stopCharacter of [CarriageReturn, NewLine]) { + let characterName = ''; - test(`stop character - "${characterName}"`, async () => { - const test = testDisposables.add( - new TestMarkdownDecoder(), + if (stopCharacter === CarriageReturn) { + characterName = '\\r'; + } + if (stopCharacter === NewLine) { + characterName = '\\n'; + } + + assert( + characterName !== '', + 'The "characterName" must be set, got "empty line".', ); - const inputLines = [ - // stop character inside link caption - `[haa${stopCharacter.symbol}loů](./real/💁/name.txt)`, - // stop character inside link reference - `[ref text](/etc/pat${stopCharacter.symbol}h/to/file.md)`, - // stop character between line caption and link reference is disallowed - `[text]${stopCharacter.symbol}(/etc/ path/file.md)`, - ]; - - - await test.run( - inputLines, - [ - // `1st` input line - new LeftBracket(new Range(1, 1, 1, 2)), - new Word(new Range(1, 2, 1, 2 + 3), 'haa'), - new stopCharacter(new Range(1, 5, 1, 6)), // <- stop character - new Word(new Range(1, 6, 1, 6 + 3), 'loů'), - new RightBracket(new Range(1, 9, 1, 10)), - new LeftParenthesis(new Range(1, 10, 1, 11)), - new Word(new Range(1, 11, 1, 11 + 18), './real/💁/name.txt'), - new RightParenthesis(new Range(1, 29, 1, 30)), - new NewLine(new Range(1, 30, 1, 31)), - // `2nd` input line - new LeftBracket(new Range(2, 1, 2, 2)), - new Word(new Range(2, 2, 2, 2 + 3), 'ref'), - new Space(new Range(2, 5, 2, 6)), - new Word(new Range(2, 6, 2, 6 + 4), 'text'), - new RightBracket(new Range(2, 10, 2, 11)), - new LeftParenthesis(new Range(2, 11, 2, 12)), - new Word(new Range(2, 12, 2, 12 + 8), '/etc/pat'), - new stopCharacter(new Range(2, 20, 2, 21)), // <- stop character - new Word(new Range(2, 21, 2, 21 + 12), 'h/to/file.md'), - new RightParenthesis(new Range(2, 33, 2, 34)), - new NewLine(new Range(2, 34, 2, 35)), - // `3nd` input line - new LeftBracket(new Range(3, 1, 3, 2)), - new Word(new Range(3, 2, 3, 2 + 4), 'text'), - new RightBracket(new Range(3, 6, 3, 7)), - new stopCharacter(new Range(3, 7, 3, 8)), // <- stop character - new LeftParenthesis(new Range(3, 8, 3, 9)), - new Word(new Range(3, 9, 3, 9 + 5), '/etc/'), - new Space(new Range(3, 14, 3, 15)), - new Word(new Range(3, 15, 3, 15 + 12), 'path/file.md'), - new RightParenthesis(new Range(3, 27, 3, 28)), - ], + test(`• stop character - "${characterName}"`, async () => { + const test = testDisposables.add( + new TestMarkdownDecoder(), + ); + + const inputLines = [ + // stop character inside link caption + `![haa${stopCharacter.symbol}loů](./real/💁/name.png)`, + // stop character inside link reference + `![ref text](/etc/pat${stopCharacter.symbol}h/to/file.webp)`, + // stop character between line caption and link reference is disallowed + `![text]${stopCharacter.symbol}(/etc/ path/file.jpeg)`, + ]; + + + await test.run( + inputLines, + [ + // `1st` input line + new ExclamationMark(new Range(1, 1, 1, 2)), + new LeftBracket(new Range(1, 2, 1, 3)), + new Word(new Range(1, 3, 1, 3 + 3), 'haa'), + new stopCharacter(new Range(1, 6, 1, 7)), // <- stop character + new Word(new Range(2, 1, 2, 1 + 3), 'loů'), + new RightBracket(new Range(2, 4, 2, 5)), + new LeftParenthesis(new Range(2, 5, 2, 6)), + new Word(new Range(2, 6, 2, 6 + 18), './real/💁/name.png'), + new RightParenthesis(new Range(2, 24, 2, 25)), + new NewLine(new Range(2, 25, 2, 26)), + // `2nd` input line + new ExclamationMark(new Range(3, 1, 3, 2)), + new LeftBracket(new Range(3, 2, 3, 3)), + new Word(new Range(3, 3, 3, 3 + 3), 'ref'), + new Space(new Range(3, 6, 3, 7)), + new Word(new Range(3, 7, 3, 7 + 4), 'text'), + new RightBracket(new Range(3, 11, 3, 12)), + new LeftParenthesis(new Range(3, 12, 3, 13)), + new Word(new Range(3, 13, 3, 13 + 8), '/etc/pat'), + new stopCharacter(new Range(3, 21, 3, 22)), // <- stop character + new Word(new Range(4, 1, 4, 1 + 14), 'h/to/file.webp'), + new RightParenthesis(new Range(4, 15, 4, 16)), + new NewLine(new Range(4, 16, 4, 17)), + // `3nd` input line + new ExclamationMark(new Range(5, 1, 5, 2)), + new LeftBracket(new Range(5, 2, 5, 3)), + new Word(new Range(5, 3, 5, 3 + 4), 'text'), + new RightBracket(new Range(5, 7, 5, 8)), + new stopCharacter(new Range(5, 8, 5, 9)), // <- stop character + new LeftParenthesis(new Range(6, 1, 6, 2)), + new Word(new Range(6, 2, 6, 2 + 5), '/etc/'), + new Space(new Range(6, 7, 6, 8)), + new Word(new Range(6, 8, 6, 8 + 14), 'path/file.jpeg'), + new RightParenthesis(new Range(6, 22, 6, 23)), + ], + ); + }); + } + }); + + /** + * Same as above but these stop characters do not move the caret to the next line. + */ + suite('• stop characters inside caption/reference (same line)', () => { + for (const stopCharacter of [VerticalTab, FormFeed]) { + let characterName = ''; + + if (stopCharacter === VerticalTab) { + characterName = '\\v'; + } + if (stopCharacter === FormFeed) { + characterName = '\\f'; + } + + assert( + characterName !== '', + 'The "characterName" must be set, got "empty line".', ); - }); - } + + test(`• stop character - "${characterName}"`, async () => { + const test = testDisposables.add( + new TestMarkdownDecoder(), + ); + + const inputLines = [ + // stop character inside link caption + `![haa${stopCharacter.symbol}loů](./real/💁/name)`, + // stop character inside link reference + `![ref text](/etc/pat${stopCharacter.symbol}h/to/file.webp)`, + // stop character between line caption and link reference is disallowed + `![text]${stopCharacter.symbol}(/etc/ path/image.gif)`, + ]; + + + await test.run( + inputLines, + [ + // `1st` input line + new ExclamationMark(new Range(1, 1, 1, 2)), + new LeftBracket(new Range(1, 2, 1, 3)), + new Word(new Range(1, 3, 1, 3 + 3), 'haa'), + new stopCharacter(new Range(1, 6, 1, 7)), // <- stop character + new Word(new Range(1, 7, 1, 7 + 3), 'loů'), + new RightBracket(new Range(1, 10, 1, 11)), + new LeftParenthesis(new Range(1, 11, 1, 12)), + new Word(new Range(1, 12, 1, 12 + 14), './real/💁/name'), + new RightParenthesis(new Range(1, 26, 1, 27)), + new NewLine(new Range(1, 27, 1, 28)), + // `2nd` input line + new ExclamationMark(new Range(2, 1, 2, 2)), + new LeftBracket(new Range(2, 2, 2, 3)), + new Word(new Range(2, 3, 2, 3 + 3), 'ref'), + new Space(new Range(2, 6, 2, 7)), + new Word(new Range(2, 7, 2, 7 + 4), 'text'), + new RightBracket(new Range(2, 11, 2, 12)), + new LeftParenthesis(new Range(2, 12, 2, 13)), + new Word(new Range(2, 13, 2, 13 + 8), '/etc/pat'), + new stopCharacter(new Range(2, 21, 2, 22)), // <- stop character + new Word(new Range(2, 22, 2, 22 + 14), 'h/to/file.webp'), + new RightParenthesis(new Range(2, 36, 2, 37)), + new NewLine(new Range(2, 37, 2, 38)), + // `3nd` input line + new ExclamationMark(new Range(3, 1, 3, 2)), + new LeftBracket(new Range(3, 2, 3, 3)), + new Word(new Range(3, 3, 3, 3 + 4), 'text'), + new RightBracket(new Range(3, 7, 3, 8)), + new stopCharacter(new Range(3, 8, 3, 9)), // <- stop character + new LeftParenthesis(new Range(3, 9, 3, 10)), + new Word(new Range(3, 10, 3, 10 + 5), '/etc/'), + new Space(new Range(3, 15, 3, 16)), + new Word(new Range(3, 16, 3, 16 + 14), 'path/image.gif'), + new RightParenthesis(new Range(3, 30, 3, 31)), + ], + ); + }); + } + }); + }); + }); + + suite('• comments', () => { + suite('• general', () => { + test('• base cases', async () => { + const test = testDisposables.add( + new TestMarkdownDecoder(), + ); + + const inputData = [ + // comment with text inside it + '\t', + // comment with a link inside + 'some text and more text ', + // comment new lines inside it + ' usual text follows', + // an empty comment + '\t\t', + // comment that was not closed properly + 'haalo\t'), + new NewLine(new Range(1, 22, 1, 23)), + // `2nd` + new Word(new Range(2, 1, 2, 5), 'some'), + new Space(new Range(2, 5, 2, 6)), + new Word(new Range(2, 6, 2, 10), 'text'), + new MarkdownComment(new Range(2, 10, 2, 10 + 46), ''), + new Space(new Range(2, 56, 2, 57)), + new Word(new Range(2, 57, 2, 60), 'and'), + new Space(new Range(2, 60, 2, 61)), + new Word(new Range(2, 61, 2, 65), 'more'), + new Space(new Range(2, 65, 2, 66)), + new Word(new Range(2, 66, 2, 70), 'text'), + new Space(new Range(2, 70, 2, 71)), + new NewLine(new Range(2, 71, 2, 72)), + // `3rd` + new MarkdownComment(new Range(3, 1, 3 + 3, 1 + 13), ''), + new Space(new Range(6, 14, 6, 15)), + new Word(new Range(6, 15, 6, 15 + 5), 'usual'), + new Space(new Range(6, 20, 6, 21)), + new Word(new Range(6, 21, 6, 21 + 4), 'text'), + new Space(new Range(6, 25, 6, 26)), + new Word(new Range(6, 26, 6, 26 + 7), 'follows'), + new NewLine(new Range(6, 33, 6, 34)), + // `4rd` + new Tab(new Range(7, 1, 7, 2)), + new MarkdownComment(new Range(7, 2, 7, 2 + 7), ''), + new Tab(new Range(7, 9, 7, 10)), + new NewLine(new Range(7, 10, 7, 11)), + // `5th` + new Word(new Range(8, 1, 8, 6), 'haalo'), + new Tab(new Range(8, 6, 8, 7)), + new MarkdownComment(new Range(8, 7, 8, 7 + 40), '>', + // comment contains `<[]>` brackets and `!` + '\t\t', + // comment contains `\t\t', + // comment contains `'), + new RightAngleBracket(new Range(1, 19, 1, 20)), + new NewLine(new Range(1, 20, 1, 21)), + // `2nd` + new MarkdownComment(new Range(2, 1, 2, 1 + 21), ''), + new Tab(new Range(2, 22, 2, 23)), + new Tab(new Range(2, 23, 2, 24)), + new NewLine(new Range(2, 24, 2, 25)), + // `3rd` + new VerticalTab(new Range(3, 1, 3, 2)), + new MarkdownComment(new Range(3, 2, 3 + 3, 1 + 7), ''), + new Tab(new Range(6, 8, 6, 9)), + new Tab(new Range(6, 9, 6, 10)), + new NewLine(new Range(6, 10, 6, 11)), + // `4rd` + new Space(new Range(7, 1, 7, 2)), + // note! comment does not have correct closing `-->`, hence the comment extends + // to the end of the text, and therefore includes the \t\v\f and space at the end + new MarkdownComment(new Range(7, 2, 8, 1 + 12), ' ', + ' < !-- світ -->\t', + '\v\f', + '`, hence the comment extends + // to the end of the text, and therefore includes the `space` at the end + new MarkdownComment(new Range(4, 1, 4, 1 + 15), '|\})/, + decreaseIndentPattern: /^\s*(<\/(?!html)[-_\.A-Za-z0-9]+\b[^>]*>|--!?>|\})/, increaseIndentPattern: /<(?!\?|(?:area|base|br|col|frame|hr|html|img|input|keygen|link|menuitem|meta|param|source|track|wbr)\b|[^>]*\/>)([-_\.A-Za-z0-9]+)(?=\s|>)\b[^>]*>(?!.*<\/\1>)|)|\{[^}"']*$/, }; diff --git a/src/vs/editor/test/common/utils/testDecoder.ts b/src/vs/editor/test/common/utils/testDecoder.ts index a998a64e2cc1..78ce39a310b2 100644 --- a/src/vs/editor/test/common/utils/testDecoder.ts +++ b/src/vs/editor/test/common/utils/testDecoder.ts @@ -129,6 +129,8 @@ export class TestDecoder> extends receivedTokens.push(token); }); + this.decoder.start(); + // in this case we also test the `settled` promise of the decoder await this.decoder.settled; @@ -158,6 +160,8 @@ export class TestDecoder> extends // add the tokens consume method to the error message so we // would know which method of consuming the tokens failed exactly error.message = `[${tokensConsumeMethod}] ${error.message}`; + + throw error; } } @@ -196,23 +200,37 @@ export class TestDecoder> extends ) { for (let i = 0; i < expectedTokens.length; i++) { const expectedToken = expectedTokens[i]; - const receivedtoken = receivedTokens[i]; + const receivedToken = receivedTokens[i]; assertDefined( - receivedtoken, + receivedToken, `Expected token '${i}' to be '${expectedToken}', got 'undefined'.`, ); assert( - receivedtoken.equals(expectedToken), - `Expected token '${i}' to be '${expectedToken}', got '${receivedtoken}'.`, + receivedToken.equals(expectedToken), + `Expected token '${i}' to be '${expectedToken}', got '${receivedToken}'.`, ); } - assert.strictEqual( - receivedTokens.length, - expectedTokens.length, - 'Must produce correct number of tokens.', + if (receivedTokens.length === expectedTokens.length) { + return; + } + + // sanity check - if received/expected list lengths are not equal, the received + // list must be longer than the expected one, because the other way around case + // must have been caught by the comparison loop above + assert( + receivedTokens.length > expectedTokens.length, + 'Must have received more tokens than expected.', + ); + + const index = expectedTokens.length; + throw new Error( + [ + `Expected no '${index}' token present, got '${receivedTokens[index]}'.`, + `(received ${receivedTokens.length} tokens in total)`, + ].join(' '), ); } } diff --git a/src/vs/editor/test/node/diffing/defaultLinesDiffComputer.test.ts b/src/vs/editor/test/node/diffing/defaultLinesDiffComputer.test.ts index 7e93197efec4..664ef33cf4f3 100644 --- a/src/vs/editor/test/node/diffing/defaultLinesDiffComputer.test.ts +++ b/src/vs/editor/test/node/diffing/defaultLinesDiffComputer.test.ts @@ -3,22 +3,19 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import assert from 'assert'; -import { Range } from '../../../common/core/range.js'; -import { getLineRangeMapping, RangeMapping } from '../../../common/diff/rangeMapping.js'; -import { OffsetRange } from '../../../common/core/offsetRange.js'; -import { LinesSliceCharSequence } from '../../../common/diff/defaultLinesDiffComputer/linesSliceCharSequence.js'; -import { MyersDiffAlgorithm } from '../../../common/diff/defaultLinesDiffComputer/algorithms/myersDiffAlgorithm.js'; -import { DynamicProgrammingDiffing } from '../../../common/diff/defaultLinesDiffComputer/algorithms/dynamicProgrammingDiffing.js'; -import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { ArrayText } from '../../../common/core/textEdit.js'; +import * as assert from 'assert'; +import { Range } from 'vs/editor/common/core/range'; +import { RangeMapping } from 'vs/editor/common/diff/rangeMapping'; +import { OffsetRange } from 'vs/editor/common/core/offsetRange'; +import { getLineRangeMapping } from 'vs/editor/common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer'; +import { LinesSliceCharSequence } from 'vs/editor/common/diff/defaultLinesDiffComputer/linesSliceCharSequence'; +import { MyersDiffAlgorithm } from 'vs/editor/common/diff/defaultLinesDiffComputer/algorithms/myersDiffAlgorithm'; +import { DynamicProgrammingDiffing } from 'vs/editor/common/diff/defaultLinesDiffComputer/algorithms/dynamicProgrammingDiffing'; suite('myers', () => { - ensureNoDisposablesAreLeakedInTestSuite(); - test('1', () => { - const s1 = new LinesSliceCharSequence(['hello world'], new Range(1, 1, 1, Number.MAX_SAFE_INTEGER), true); - const s2 = new LinesSliceCharSequence(['hallo welt'], new Range(1, 1, 1, Number.MAX_SAFE_INTEGER), true); + const s1 = new LinesSliceCharSequence(['hello world'], new OffsetRange(0, 1), true); + const s2 = new LinesSliceCharSequence(['hallo welt'], new OffsetRange(0, 1), true); const a = true ? new MyersDiffAlgorithm() : new DynamicProgrammingDiffing(); a.compute(s1, s2); @@ -26,8 +23,6 @@ suite('myers', () => { }); suite('lineRangeMapping', () => { - ensureNoDisposablesAreLeakedInTestSuite(); - test('Simple', () => { assert.deepStrictEqual( getLineRangeMapping( @@ -35,15 +30,15 @@ suite('lineRangeMapping', () => { new Range(2, 1, 3, 1), new Range(2, 1, 2, 1) ), - new ArrayText([ + [ 'const abc = "helloworld".split("");', '', '' - ]), - new ArrayText([ + ], + [ 'const asciiLower = "helloworld".split("");', '' - ]) + ] ).toString(), "{[2,3)->[2,2)}" ); @@ -56,16 +51,16 @@ suite('lineRangeMapping', () => { new Range(2, 1, 2, 1), new Range(2, 1, 4, 1), ), - new ArrayText([ + [ '', '', - ]), - new ArrayText([ + ], + [ '', '', '', '', - ]) + ] ).toString(), "{[2,2)->[2,4)}" ); @@ -73,8 +68,6 @@ suite('lineRangeMapping', () => { }); suite('LinesSliceCharSequence', () => { - ensureNoDisposablesAreLeakedInTestSuite(); - const sequence = new LinesSliceCharSequence( [ 'line1: foo', @@ -83,7 +76,7 @@ suite('LinesSliceCharSequence', () => { 'line4: hello world', 'line5: bazz', ], - new Range(2, 1, 5, 1), true + new OffsetRange(1, 4), true ); test('translateOffset', () => { diff --git a/src/vs/editor/test/node/diffing/fixtures.test.ts b/src/vs/editor/test/node/diffing/fixtures.test.ts index e85d69f1260c..a7ff5dbe5a11 100644 --- a/src/vs/editor/test/node/diffing/fixtures.test.ts +++ b/src/vs/editor/test/node/diffing/fixtures.test.ts @@ -3,22 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import assert from 'assert'; +import * as assert from 'assert'; import { existsSync, readFileSync, readdirSync, rmSync, writeFileSync } from 'fs'; import { join, resolve } from 'path'; -import { setUnexpectedErrorHandler } from '../../../../base/common/errors.js'; -import { FileAccess } from '../../../../base/common/network.js'; -import { DetailedLineRangeMapping, RangeMapping } from '../../../common/diff/rangeMapping.js'; -import { LegacyLinesDiffComputer } from '../../../common/diff/legacyLinesDiffComputer.js'; -import { DefaultLinesDiffComputer } from '../../../common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer.js'; -import { Range } from '../../../common/core/range.js'; -import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; -import { AbstractText, ArrayText, SingleTextEdit, TextEdit } from '../../../common/core/textEdit.js'; -import { LinesDiff } from '../../../common/diff/linesDiffComputer.js'; +import { setUnexpectedErrorHandler } from 'vs/base/common/errors'; +import { FileAccess } from 'vs/base/common/network'; +import { DetailedLineRangeMapping } from 'vs/editor/common/diff/rangeMapping'; +import { LegacyLinesDiffComputer } from 'vs/editor/common/diff/legacyLinesDiffComputer'; +import { DefaultLinesDiffComputer } from 'vs/editor/common/diff/defaultLinesDiffComputer/defaultLinesDiffComputer'; suite('diffing fixtures', () => { - ensureNoDisposablesAreLeakedInTestSuite(); - setup(() => { setUnexpectedErrorHandler(e => { throw e; @@ -47,33 +41,19 @@ suite('diffing fixtures', () => { const diffingAlgo = diffingAlgoName === 'legacy' ? new LegacyLinesDiffComputer() : new DefaultLinesDiffComputer(); const ignoreTrimWhitespace = folder.indexOf('trimws') >= 0; - const diff = diffingAlgo.computeDiff(firstContentLines, secondContentLines, { ignoreTrimWhitespace, maxComputationTimeMs: Number.MAX_SAFE_INTEGER, computeMoves: true }); - - if (diffingAlgoName === 'advanced' && !ignoreTrimWhitespace) { - assertDiffCorrectness(diff, firstContentLines, secondContentLines); - } + const diff = diffingAlgo.computeDiff(firstContentLines, secondContentLines, { ignoreTrimWhitespace, maxComputationTimeMs: Number.MAX_SAFE_INTEGER, computeMoves: false }); function getDiffs(changes: readonly DetailedLineRangeMapping[]): IDetailedDiff[] { - for (const c of changes) { - RangeMapping.assertSorted(c.innerChanges ?? []); - } - return changes.map(c => ({ originalRange: c.original.toString(), modifiedRange: c.modified.toString(), innerChanges: c.innerChanges?.map(c => ({ - originalRange: formatRange(c.originalRange, firstContentLines), - modifiedRange: formatRange(c.modifiedRange, secondContentLines), + originalRange: c.originalRange.toString(), + modifiedRange: c.modifiedRange.toString(), })) || null })); } - function formatRange(range: Range, lines: string[]): string { - const toLastChar = range.endColumn === lines[range.endLineNumber - 1].length + 1; - - return '[' + range.startLineNumber + ',' + range.startColumn + ' -> ' + range.endLineNumber + ',' + range.endColumn + (toLastChar ? ' EOL' : '') + ']'; - } - const actualDiffingResult: DiffingResult = { original: { content: firstContent, fileName: `./${firstFileName}` }, modified: { content: secondContent, fileName: `./${secondFileName}` }, @@ -168,22 +148,5 @@ interface IMoveInfo { originalRange: string; // [startLineNumber, endLineNumberExclusive) modifiedRange: string; // [startLineNumber, endLineNumberExclusive) - changes: IDetailedDiff[]; -} - -function assertDiffCorrectness(diff: LinesDiff, original: string[], modified: string[]) { - const allInnerChanges = diff.changes.flatMap(c => c.innerChanges!); - const edit = rangeMappingsToTextEdit(allInnerChanges, new ArrayText(modified)); - const result = edit.normalize().apply(new ArrayText(original)); - - assert.deepStrictEqual(result, modified.join('\n')); -} - -function rangeMappingsToTextEdit(rangeMappings: readonly RangeMapping[], modified: AbstractText): TextEdit { - return new TextEdit(rangeMappings.map(m => { - return new SingleTextEdit( - m.originalRange, - modified.getValueOfRange(m.modifiedRange) - ); - })); + changes?: IDetailedDiff[]; } diff --git a/src/vs/monaco.d.ts b/src/vs/monaco.d.ts index 12d9e3de28c8..8fcb54449116 100644 --- a/src/vs/monaco.d.ts +++ b/src/vs/monaco.d.ts @@ -7301,6 +7301,7 @@ declare namespace monaco.languages { readonly action?: Command; /** * Is called the first time an inline completion is shown. + * @deprecated. Use `onDidShow` of the provider instead. */ readonly shownCommand?: Command; /** @@ -7309,6 +7310,7 @@ declare namespace monaco.languages { */ readonly completeBracketPairs?: boolean; readonly isInlineEdit?: boolean; + readonly showInlineEditMenu?: boolean; readonly showRange?: IRange; readonly warning?: InlineCompletionWarning; } diff --git a/src/vs/platform/accessibility/browser/accessibleView.ts b/src/vs/platform/accessibility/browser/accessibleView.ts index b438dedc42e3..3f2f137d048a 100644 --- a/src/vs/platform/accessibility/browser/accessibleView.ts +++ b/src/vs/platform/accessibility/browser/accessibleView.ts @@ -169,6 +169,10 @@ export class AccessibleContentProvider extends Disposable implements IAccessible } } +export function isIAccessibleViewContentProvider(obj: any): obj is IAccessibleViewContentProvider { + return obj && obj.id && obj.options && obj.provideContent && obj.onClose && obj.verbositySettingKey; +} + export class ExtensionContentProvider extends Disposable implements IBasicContentProvider { constructor( diff --git a/src/vs/platform/accessibilitySignal/browser/accessibilitySignalService.ts b/src/vs/platform/accessibilitySignal/browser/accessibilitySignalService.ts index 9b161fcadb1b..0e919686c6a2 100644 --- a/src/vs/platform/accessibilitySignal/browser/accessibilitySignalService.ts +++ b/src/vs/platform/accessibilitySignal/browser/accessibilitySignalService.ts @@ -315,6 +315,7 @@ export class Sound { public static readonly voiceRecordingStarted = Sound.register({ fileName: 'voiceRecordingStarted.mp3' }); public static readonly voiceRecordingStopped = Sound.register({ fileName: 'voiceRecordingStopped.mp3' }); public static readonly progress = Sound.register({ fileName: 'progress.mp3' }); + public static readonly chatEditModifiedFile = Sound.register({ fileName: 'chatEditModifiedFile.mp3' }); private constructor(public readonly fileName: string) { } } @@ -543,7 +544,7 @@ export class AccessibilitySignal { public static readonly chatEditModifiedFile = AccessibilitySignal.register({ name: localize('accessibilitySignals.chatEditModifiedFile', 'Chat Edit Modified File'), - sound: Sound.success, + sound: Sound.chatEditModifiedFile, announcementMessage: localize('accessibility.signals.chatEditModifiedFile', 'File Modified from Chat Edits'), settingsKey: 'accessibility.signals.chatEditModifiedFile', }); diff --git a/src/vs/platform/accessibilitySignal/browser/media/chatEditModifiedFile.mp3 b/src/vs/platform/accessibilitySignal/browser/media/chatEditModifiedFile.mp3 new file mode 100644 index 000000000000..bc1af4973d37 Binary files /dev/null and b/src/vs/platform/accessibilitySignal/browser/media/chatEditModifiedFile.mp3 differ diff --git a/src/vs/platform/actions/browser/menuEntryActionViewItem.ts b/src/vs/platform/actions/browser/menuEntryActionViewItem.ts index 0581f04a6441..2e86c3bcdea8 100644 --- a/src/vs/platform/actions/browser/menuEntryActionViewItem.ts +++ b/src/vs/platform/actions/browser/menuEntryActionViewItem.ts @@ -14,7 +14,7 @@ import { Event } from '../../../base/common/event.js'; import { UILabelProvider } from '../../../base/common/keybindingLabels.js'; import { ResolvedKeybinding } from '../../../base/common/keybindings.js'; import { KeyCode } from '../../../base/common/keyCodes.js'; -import { combinedDisposable, MutableDisposable, toDisposable } from '../../../base/common/lifecycle.js'; +import { combinedDisposable, DisposableStore, MutableDisposable, toDisposable } from '../../../base/common/lifecycle.js'; import { isLinux, isWindows, OS } from '../../../base/common/platform.js'; import { ThemeIcon } from '../../../base/common/themables.js'; import { assertType } from '../../../base/common/types.js'; @@ -167,7 +167,7 @@ function fillInActions( export interface IMenuEntryActionViewItemOptions { draggable?: boolean; - keybinding?: string; + keybinding?: string | null; hoverDelegate?: IHoverDelegate; keybindingNotRenderedWithLabel?: boolean; } @@ -429,6 +429,7 @@ export interface IDropdownWithDefaultActionViewItemOptions extends IDropdownMenu export class DropdownWithDefaultActionViewItem extends BaseActionViewItem { private readonly _options: IDropdownWithDefaultActionViewItemOptions | undefined; private _defaultAction: ActionViewItem; + private readonly _defaultActionDisposables = this._register(new DisposableStore()); private readonly _dropdown: DropdownMenuActionViewItem; private _container: HTMLElement | null = null; private readonly _storageKey: string; @@ -461,7 +462,7 @@ export class DropdownWithDefaultActionViewItem extends BaseActionViewItem { defaultAction = submenuAction.actions[0]; } - this._defaultAction = this._instaService.createInstance(MenuEntryActionViewItem, defaultAction, { keybinding: this._getDefaultActionKeybindingLabel(defaultAction) }); + this._defaultAction = this._defaultActionDisposables.add(this._instaService.createInstance(MenuEntryActionViewItem, defaultAction, { keybinding: this._getDefaultActionKeybindingLabel(defaultAction) })); const dropdownOptions: IDropdownMenuActionViewItemOptions = { keybindingProvider: action => this._keybindingService.lookupKeybinding(action.id), @@ -471,7 +472,7 @@ export class DropdownWithDefaultActionViewItem extends BaseActionViewItem { actionRunner: options?.actionRunner ?? this._register(new ActionRunner()), }; - this._dropdown = new DropdownMenuActionViewItem(submenuAction, submenuAction.actions, this._contextMenuService, dropdownOptions); + this._dropdown = this._register(new DropdownMenuActionViewItem(submenuAction, submenuAction.actions, this._contextMenuService, dropdownOptions)); this._register(this._dropdown.actionRunner.onDidRun((e: IRunEvent) => { if (e.action instanceof MenuItemAction) { this.update(e.action); @@ -484,13 +485,13 @@ export class DropdownWithDefaultActionViewItem extends BaseActionViewItem { this._storageService.store(this._storageKey, lastAction.id, StorageScope.WORKSPACE, StorageTarget.MACHINE); } - this._defaultAction.dispose(); - this._defaultAction = this._instaService.createInstance(MenuEntryActionViewItem, lastAction, { keybinding: this._getDefaultActionKeybindingLabel(lastAction) }); - this._defaultAction.actionRunner = new class extends ActionRunner { + this._defaultActionDisposables.clear(); + this._defaultAction = this._defaultActionDisposables.add(this._instaService.createInstance(MenuEntryActionViewItem, lastAction, { keybinding: this._getDefaultActionKeybindingLabel(lastAction) })); + this._defaultAction.actionRunner = this._defaultActionDisposables.add(new class extends ActionRunner { protected override async runAction(action: IAction, context?: unknown): Promise { await action.run(undefined); } - }(); + }()); if (this._container) { this._defaultAction.render(prepend(this._container, $('.action-container'))); @@ -567,12 +568,6 @@ export class DropdownWithDefaultActionViewItem extends BaseActionViewItem { this._dropdown.setFocusable(false); } } - - override dispose() { - this._defaultAction.dispose(); - this._dropdown.dispose(); - super.dispose(); - } } class SubmenuEntrySelectActionViewItem extends SelectActionViewItem { diff --git a/src/vs/platform/actions/common/actions.ts b/src/vs/platform/actions/common/actions.ts index 676182697e79..ab2b28b2bbf0 100644 --- a/src/vs/platform/actions/common/actions.ts +++ b/src/vs/platform/actions/common/actions.ts @@ -234,6 +234,8 @@ export class MenuId { static readonly ChatEditingWidgetToolbar = new MenuId('ChatEditingWidgetToolbar'); static readonly ChatEditingEditorContent = new MenuId('ChatEditingEditorContent'); static readonly ChatEditingEditorHunk = new MenuId('ChatEditingEditorHunk'); + static readonly ChatEditingDeletedNotebookCell = new MenuId('ChatEditingDeletedNotebookCell'); + static readonly ChatInputAttachmentToolbar = new MenuId('ChatInputAttachmentToolbar'); static readonly ChatEditingWidgetModifiedFilesToolbar = new MenuId('ChatEditingWidgetModifiedFilesToolbar'); static readonly ChatInputResourceAttachmentContext = new MenuId('ChatInputResourceAttachmentContext'); static readonly ChatInputSymbolAttachmentContext = new MenuId('ChatInputSymbolAttachmentContext'); diff --git a/src/vs/platform/auxiliaryWindow/electron-main/auxiliaryWindow.ts b/src/vs/platform/auxiliaryWindow/electron-main/auxiliaryWindow.ts index 971d5cd6b2e9..683ca26d49b8 100644 --- a/src/vs/platform/auxiliaryWindow/electron-main/auxiliaryWindow.ts +++ b/src/vs/platform/auxiliaryWindow/electron-main/auxiliaryWindow.ts @@ -20,7 +20,7 @@ export interface IAuxiliaryWindow extends IBaseWindow { export class AuxiliaryWindow extends BaseWindow implements IAuxiliaryWindow { - readonly id = this.webContents.id; + readonly id: number; parentId = -1; override get win() { @@ -43,6 +43,8 @@ export class AuxiliaryWindow extends BaseWindow implements IAuxiliaryWindow { ) { super(configurationService, stateService, environmentMainService, logService); + this.id = this.webContents.id; + // Try to claim window this.tryClaimWindow(); } diff --git a/src/vs/platform/backup/electron-main/backupMainService.ts b/src/vs/platform/backup/electron-main/backupMainService.ts index 36287ebc981f..55b36989ae31 100644 --- a/src/vs/platform/backup/electron-main/backupMainService.ts +++ b/src/vs/platform/backup/electron-main/backupMainService.ts @@ -27,7 +27,7 @@ export class BackupMainService implements IBackupMainService { private static readonly backupWorkspacesMetadataStorageKey = 'backupWorkspaces'; - protected backupHome = this.environmentMainService.backupHome; + protected backupHome: string; private workspaces: IWorkspaceBackupInfo[] = []; private folders: IFolderBackupInfo[] = []; @@ -40,11 +40,12 @@ export class BackupMainService implements IBackupMainService { private readonly backupPathComparer = { isEqual: (pathA: string, pathB: string) => isEqual(pathA, pathB, !isLinux) }; constructor( - @IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService, + @IEnvironmentMainService environmentMainService: IEnvironmentMainService, @IConfigurationService private readonly configurationService: IConfigurationService, @ILogService private readonly logService: ILogService, @IStateService private readonly stateService: IStateService ) { + this.backupHome = environmentMainService.backupHome; } async initialize(): Promise { diff --git a/src/vs/platform/configuration/common/configuration.ts b/src/vs/platform/configuration/common/configuration.ts index 177c4e43e9fe..342b3b956a0c 100644 --- a/src/vs/platform/configuration/common/configuration.ts +++ b/src/vs/platform/configuration/common/configuration.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { assertNever } from '../../../base/common/assert.js'; import { IStringDictionary } from '../../../base/common/collections.js'; import { Event } from '../../../base/common/event.js'; import * as types from '../../../base/common/types.js'; @@ -103,6 +104,29 @@ export interface IConfigurationValue { readonly overrideIdentifiers?: string[]; } +export function getConfigValueInTarget(configValue: IConfigurationValue, scope: ConfigurationTarget): T | undefined { + switch (scope) { + case ConfigurationTarget.APPLICATION: + return configValue.applicationValue; + case ConfigurationTarget.USER: + return configValue.userValue; + case ConfigurationTarget.USER_LOCAL: + return configValue.userLocalValue; + case ConfigurationTarget.USER_REMOTE: + return configValue.userRemoteValue; + case ConfigurationTarget.WORKSPACE: + return configValue.workspaceValue; + case ConfigurationTarget.WORKSPACE_FOLDER: + return configValue.workspaceFolderValue; + case ConfigurationTarget.DEFAULT: + return configValue.defaultValue; + case ConfigurationTarget.MEMORY: + return configValue.memoryValue; + default: + assertNever(scope); + } +} + export function isConfigured(configValue: IConfigurationValue): configValue is IConfigurationValue & { value: T } { return configValue.applicationValue !== undefined || configValue.userValue !== undefined || diff --git a/src/vs/platform/configuration/common/configurationRegistry.ts b/src/vs/platform/configuration/common/configurationRegistry.ts index d2ba316398ee..70a1c98a370d 100644 --- a/src/vs/platform/configuration/common/configurationRegistry.ts +++ b/src/vs/platform/configuration/common/configurationRegistry.ts @@ -164,8 +164,13 @@ export interface IPolicy { /** * The Code version in which this policy was introduced. - */ + */ readonly minimumVersion: `${number}.${number}`; + + /** + * The policy description (optional). + */ + readonly description?: string; } export interface IConfigurationPropertySchema extends IJSONSchema { diff --git a/src/vs/platform/configuration/common/configurations.ts b/src/vs/platform/configuration/common/configurations.ts index 8d2671b4de95..785523cd54b2 100644 --- a/src/vs/platform/configuration/common/configurations.ts +++ b/src/vs/platform/configuration/common/configurations.ts @@ -124,12 +124,12 @@ export class PolicyConfiguration extends Disposable implements IPolicyConfigurat continue; } if (config.policy) { - if (config.type !== 'string' && config.type !== 'number' && config.type !== 'array' && config.type !== 'object') { + if (config.type !== 'string' && config.type !== 'number' && config.type !== 'array' && config.type !== 'object' && config.type !== 'boolean') { this.logService.warn(`Policy ${config.policy.name} has unsupported type ${config.type}`); continue; } keys.push(key); - policyDefinitions[config.policy.name] = { type: config.type === 'number' ? 'number' : 'string' }; + policyDefinitions[config.policy.name] = { type: config.type === 'number' ? 'number' : config.type === 'boolean' ? 'boolean' : 'string' }; } } diff --git a/src/vs/platform/contextkey/common/contextkey.ts b/src/vs/platform/contextkey/common/contextkey.ts index ef011cc6b2c3..57be3923e57d 100644 --- a/src/vs/platform/contextkey/common/contextkey.ts +++ b/src/vs/platform/contextkey/common/contextkey.ts @@ -2005,6 +2005,10 @@ export class RawContextKey extends ContextKeyDefinedE public notEqualsTo(value: any): ContextKeyExpression { return ContextKeyNotEqualsExpr.create(this.key, value); } + + public greater(value: any): ContextKeyExpression { + return ContextKeyGreaterExpr.create(this.key, value); + } } export type ContextKeyValue = null | undefined | boolean | number | string diff --git a/src/vs/platform/contextview/browser/contextViewService.ts b/src/vs/platform/contextview/browser/contextViewService.ts index 4ee1349dcce9..734a1f326034 100644 --- a/src/vs/platform/contextview/browser/contextViewService.ts +++ b/src/vs/platform/contextview/browser/contextViewService.ts @@ -12,13 +12,15 @@ import { getWindow } from '../../../base/browser/dom.js'; export class ContextViewHandler extends Disposable implements IContextViewProvider { private openContextView: IOpenContextView | undefined; - protected readonly contextView = this._register(new ContextView(this.layoutService.mainContainer, ContextViewDOMPosition.ABSOLUTE)); + protected readonly contextView: ContextView; constructor( @ILayoutService private readonly layoutService: ILayoutService ) { super(); + this.contextView = this._register(new ContextView(this.layoutService.mainContainer, ContextViewDOMPosition.ABSOLUTE)); + this.layout(); this._register(layoutService.onDidLayoutContainer(() => this.layout())); } diff --git a/src/vs/platform/diagnostics/node/diagnosticsService.ts b/src/vs/platform/diagnostics/node/diagnosticsService.ts index 5f6efd51dd43..e523217c1085 100644 --- a/src/vs/platform/diagnostics/node/diagnosticsService.ts +++ b/src/vs/platform/diagnostics/node/diagnosticsService.ts @@ -46,6 +46,7 @@ export async function collectWorkspaceStats(folder: string, filter: string[]): P { tag: 'eslint.json', filePattern: /^eslint\.json$/i }, { tag: 'tasks.json', filePattern: /^tasks\.json$/i }, { tag: 'launch.json', filePattern: /^launch\.json$/i }, + { tag: 'mcp.json', filePattern: /^mcp\.json$/i }, { tag: 'settings.json', filePattern: /^settings\.json$/i }, { tag: 'webpack.config.js', filePattern: /^webpack\.config\.js$/i }, { tag: 'project.json', filePattern: /^project\.json$/i }, diff --git a/src/vs/platform/dialogs/browser/dialog.ts b/src/vs/platform/dialogs/browser/dialog.ts new file mode 100644 index 000000000000..91e6d519dc3d --- /dev/null +++ b/src/vs/platform/dialogs/browser/dialog.ts @@ -0,0 +1,41 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { EventHelper } from '../../../base/browser/dom.js'; +import { StandardKeyboardEvent } from '../../../base/browser/keyboardEvent.js'; +import { IDialogOptions } from '../../../base/browser/ui/dialog/dialog.js'; +import { IKeybindingService } from '../../keybinding/common/keybinding.js'; +import { ResultKind } from '../../keybinding/common/keybindingResolver.js'; +import { ILayoutService } from '../../layout/browser/layoutService.js'; +import { defaultButtonStyles, defaultCheckboxStyles, defaultInputBoxStyles, defaultDialogStyles } from '../../theme/browser/defaultStyles.js'; + +const defaultDialogAllowableCommands = [ + 'workbench.action.quit', + 'workbench.action.reloadWindow', + 'copy', + 'cut', + 'editor.action.selectAll', + 'editor.action.clipboardCopyAction', + 'editor.action.clipboardCutAction', + 'editor.action.clipboardPasteAction' +]; + +export function createWorkbenchDialogOptions(options: Partial, keybindingService: IKeybindingService, layoutService: ILayoutService, allowableCommands = defaultDialogAllowableCommands): IDialogOptions { + return { + keyEventProcessor: (event: StandardKeyboardEvent) => { + const resolved = keybindingService.softDispatch(event, layoutService.activeContainer); + if (resolved.kind === ResultKind.KbFound && resolved.commandId) { + if (!allowableCommands.includes(resolved.commandId)) { + EventHelper.stop(event, true); + } + } + }, + buttonStyles: defaultButtonStyles, + checkboxStyles: defaultCheckboxStyles, + inputBoxStyles: defaultInputBoxStyles, + dialogStyles: defaultDialogStyles, + ...options + }; +} diff --git a/src/vs/platform/dialogs/common/dialogs.ts b/src/vs/platform/dialogs/common/dialogs.ts index 7adeddd7013e..bb9270b9c287 100644 --- a/src/vs/platform/dialogs/common/dialogs.ts +++ b/src/vs/platform/dialogs/common/dialogs.ts @@ -283,6 +283,7 @@ export interface ICustomDialogOptions { export interface ICustomDialogMarkdown { readonly markdown: IMarkdownString; readonly classes?: string[]; + readonly dismissOnLinkClick?: boolean; } /** diff --git a/src/vs/platform/dnd/browser/dnd.ts b/src/vs/platform/dnd/browser/dnd.ts index f83103d27a65..f00c4d08a732 100644 --- a/src/vs/platform/dnd/browser/dnd.ts +++ b/src/vs/platform/dnd/browser/dnd.ts @@ -13,7 +13,7 @@ import { ResourceMap } from '../../../base/common/map.js'; import { parse } from '../../../base/common/marshalling.js'; import { Schemas } from '../../../base/common/network.js'; import { isNative, isWeb } from '../../../base/common/platform.js'; -import { URI } from '../../../base/common/uri.js'; +import { URI, UriComponents } from '../../../base/common/uri.js'; import { localize } from '../../../nls.js'; import { IDialogService } from '../../dialogs/common/dialogs.js'; import { IBaseTextResourceEditorInput, ITextEditorSelection } from '../../editor/common/editor.js'; @@ -23,6 +23,7 @@ import { ByteSize, IFileService } from '../../files/common/files.js'; import { IInstantiationService, ServicesAccessor } from '../../instantiation/common/instantiation.js'; import { extractSelection } from '../../opener/common/opener.js'; import { Registry } from '../../registry/common/platform.js'; +import { IMarker } from '../../markers/common/markers.js'; //#region Editor / Resources DND @@ -30,7 +31,8 @@ import { Registry } from '../../registry/common/platform.js'; export const CodeDataTransfers = { EDITORS: 'CodeEditors', FILES: 'CodeFiles', - SYMBOLS: 'application/vnd.code.symbols' + SYMBOLS: 'application/vnd.code.symbols', + MARKERS: 'application/vnd.code.diagnostics', }; export interface IDraggedResourceEditorInput extends IBaseTextResourceEditorInput { @@ -414,8 +416,12 @@ export interface DocumentSymbolTransferData { kind: number; } -export function extractSymbolDropData(e: DragEvent): DocumentSymbolTransferData[] { - const rawSymbolsData = e.dataTransfer?.getData(CodeDataTransfers.SYMBOLS); +function setDataAsJSON(e: DragEvent, kind: string, data: unknown) { + e.dataTransfer?.setData(kind, JSON.stringify(data)); +} + +function getDataAsJSON(e: DragEvent, kind: string, defaultValue: T): T { + const rawSymbolsData = e.dataTransfer?.getData(kind); if (rawSymbolsData) { try { return JSON.parse(rawSymbolsData); @@ -424,11 +430,25 @@ export function extractSymbolDropData(e: DragEvent): DocumentSymbolTransferData[ } } - return []; + return defaultValue; +} + +export function extractSymbolDropData(e: DragEvent): DocumentSymbolTransferData[] { + return getDataAsJSON(e, CodeDataTransfers.SYMBOLS, []); } export function fillInSymbolsDragData(symbolsData: readonly DocumentSymbolTransferData[], e: DragEvent): void { - e.dataTransfer?.setData(CodeDataTransfers.SYMBOLS, JSON.stringify(symbolsData)); + setDataAsJSON(e, CodeDataTransfers.SYMBOLS, symbolsData); +} + +export type MarkerTransferData = IMarker | { uri: UriComponents }; + +export function extractMarkerDropData(e: DragEvent): MarkerTransferData[] | undefined { + return getDataAsJSON(e, CodeDataTransfers.MARKERS, undefined); +} + +export function fillInMarkersDragData(markerData: MarkerTransferData[], e: DragEvent): void { + setDataAsJSON(e, CodeDataTransfers.MARKERS, markerData); } /** diff --git a/src/vs/platform/environment/common/argv.ts b/src/vs/platform/environment/common/argv.ts index ba88dab2f392..1efc1b5de457 100644 --- a/src/vs/platform/environment/common/argv.ts +++ b/src/vs/platform/environment/common/argv.ts @@ -51,6 +51,7 @@ export interface NativeParsedArgs { 'no-cached-data'?: boolean; verbose?: boolean; trace?: boolean; + 'trace-memory-infra'?: boolean; 'trace-category-filter'?: string; 'trace-options'?: string; 'open-devtools'?: boolean; @@ -92,6 +93,7 @@ export interface NativeParsedArgs { 'disable-telemetry'?: boolean; 'export-default-configuration'?: string; 'install-source'?: string; + 'add-mcp'?: string[]; 'disable-updates'?: boolean; 'use-inmemory-secretstorage'?: boolean; 'password-store'?: string; diff --git a/src/vs/platform/environment/node/argv.ts b/src/vs/platform/environment/node/argv.ts index eb2fc365d596..0025e4b59739 100644 --- a/src/vs/platform/environment/node/argv.ts +++ b/src/vs/platform/environment/node/argv.ts @@ -104,6 +104,8 @@ export const OPTIONS: OptionDescriptions> = { 'update-extensions': { type: 'boolean', cat: 'e', description: localize('updateExtensions', "Update the installed extensions.") }, 'enable-proposed-api': { type: 'string[]', allowEmptyValue: true, cat: 'e', args: 'ext-id', description: localize('experimentalApis', "Enables proposed API features for extensions. Can receive one or more extension IDs to enable individually.") }, + 'add-mcp': { type: 'string[]', cat: 'o', args: 'json', description: localize('addMcp', "Adds a Model Context Protocol server definition to the user profile, or workspace or folder when used with --mcp-workspace. Accepts JSON input in the form '{\"name\":\"server-name\",\"command\":...}'") }, + 'version': { type: 'boolean', cat: 't', alias: 'v', description: localize('version', "Print version.") }, 'verbose': { type: 'boolean', cat: 't', global: true, description: localize('verbose', "Print verbose output (implies --wait).") }, 'log': { type: 'string[]', cat: 't', args: 'level', global: true, description: localize('log', "Log level to use. Default is 'info'. Allowed values are 'critical', 'error', 'warn', 'info', 'debug', 'trace', 'off'. You can also configure the log level of an extension by passing extension id and log level in the following format: '${publisher}.${name}:${logLevel}'. For example: 'vscode.csharp:trace'. Can receive one or more such entries.") }, @@ -125,6 +127,7 @@ export const OPTIONS: OptionDescriptions> = { 'disable-gpu': { type: 'boolean', cat: 't', description: localize('disableGPU', "Disable GPU hardware acceleration.") }, 'disable-chromium-sandbox': { type: 'boolean', cat: 't', description: localize('disableChromiumSandbox', "Use this option only when there is requirement to launch the application as sudo user on Linux or when running as an elevated user in an applocker environment on Windows.") }, 'sandbox': { type: 'boolean' }, + 'locate-shell-integration-path': { type: 'string', cat: 't', args: ['shell'], description: localize('locateShellIntegrationPath', "Print the path to a terminal shell integration script. Allowed values are 'bash', 'pwsh', 'zsh' or 'fish'.") }, 'telemetry': { type: 'boolean', cat: 't', description: localize('telemetry', "Shows all telemetry events which VS code collects.") }, 'remote': { type: 'string', allowEmptyValue: true }, @@ -167,6 +170,7 @@ export const OPTIONS: OptionDescriptions> = { 'do-not-sync': { type: 'boolean' }, 'do-not-include-pack-dependencies': { type: 'boolean' }, 'trace': { type: 'boolean' }, + 'trace-memory-infra': { type: 'boolean' }, 'trace-category-filter': { type: 'string' }, 'trace-options': { type: 'string' }, 'preserve-env': { type: 'boolean' }, @@ -178,7 +182,6 @@ export const OPTIONS: OptionDescriptions> = { '__enable-file-policy': { type: 'boolean' }, 'editSessionId': { type: 'string' }, 'continueOn': { type: 'string' }, - 'locate-shell-integration-path': { type: 'string', args: ['bash', 'pwsh', 'zsh', 'fish'] }, 'enable-coi': { type: 'boolean' }, @@ -409,9 +412,12 @@ function indent(count: number): string { function wrapText(text: string, columns: number): string[] { const lines: string[] = []; while (text.length) { - const index = text.length < columns ? text.length : text.lastIndexOf(' ', columns); + let index = text.length < columns ? text.length : text.lastIndexOf(' ', columns); + if (index === 0) { + index = columns; + } const line = text.slice(0, index).trim(); - text = text.slice(index); + text = text.slice(index).trimStart(); lines.push(line); } return lines; diff --git a/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts b/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts index 4bef58907b79..f0f8be2593aa 100644 --- a/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts +++ b/src/vs/platform/extensionManagement/common/abstractExtensionManagementService.ts @@ -321,7 +321,6 @@ export abstract class AbstractExtensionManagementService extends CommontExtensio const isApplicationScoped = options.isApplicationScoped || options.isBuiltin || isApplicationScopedExtension(manifest); const installExtensionTaskOptions: InstallExtensionTaskOptions = { ...options, - installOnlyNewlyAddedFromExtensionPack: options.installOnlyNewlyAddedFromExtensionPack ?? !URI.isUri(extension) /* always true for gallery extensions */, isApplicationScoped, profileLocation: isApplicationScoped ? this.userDataProfilesService.defaultProfile.extensionsResource : options.profileLocation ?? this.getCurrentExtensionsManifestLocation(), productVersion: options.productVersion ?? { version: this.productService.version, date: this.productService.date } @@ -342,11 +341,13 @@ export abstract class AbstractExtensionManagementService extends CommontExtensio this.logService.info('Installing the extension without checking dependencies and pack', task.identifier.id); } else { try { - const allDepsAndPackExtensionsToInstall = await this.getAllDepsAndPackExtensions(task.identifier, task.manifest, !!task.options.installOnlyNewlyAddedFromExtensionPack, !!task.options.installPreReleaseVersion, task.options.profileLocation, task.options.productVersion); + const allDepsAndPackExtensionsToInstall = await this.getAllDepsAndPackExtensions(task.identifier, task.manifest, !!task.options.installPreReleaseVersion, task.options.productVersion); const installed = await this.getInstalled(undefined, task.options.profileLocation, task.options.productVersion); const options: InstallExtensionTaskOptions = { ...task.options, context: { ...task.options.context, [EXTENSION_INSTALL_DEP_PACK_CONTEXT]: true } }; for (const { gallery, manifest } of distinct(allDepsAndPackExtensionsToInstall, ({ gallery }) => gallery.identifier.id)) { - if (installed.some(({ identifier }) => areSameExtensions(identifier, gallery.identifier))) { + const existing = installed.find(e => areSameExtensions(e.identifier, gallery.identifier)); + // Skip if the extension is already installed and has the same application scope + if (existing && existing.isApplicationScoped === !!options.isApplicationScoped) { continue; } createInstallExtensionTask(manifest, gallery, options, task); @@ -578,12 +579,11 @@ export abstract class AbstractExtensionManagementService extends CommontExtensio throw error; } - private async getAllDepsAndPackExtensions(extensionIdentifier: IExtensionIdentifier, manifest: IExtensionManifest, getOnlyNewlyAddedFromExtensionPack: boolean, installPreRelease: boolean, profile: URI | undefined, productVersion: IProductVersion): Promise<{ gallery: IGalleryExtension; manifest: IExtensionManifest }[]> { + private async getAllDepsAndPackExtensions(extensionIdentifier: IExtensionIdentifier, manifest: IExtensionManifest, installPreRelease: boolean, productVersion: IProductVersion): Promise<{ gallery: IGalleryExtension; manifest: IExtensionManifest }[]> { if (!this.galleryService.isEnabled()) { return []; } - const installed = await this.getInstalled(undefined, profile, productVersion); const knownIdentifiers: IExtensionIdentifier[] = []; const allDependenciesAndPacks: { gallery: IGalleryExtension; manifest: IExtensionManifest }[] = []; @@ -592,13 +592,9 @@ export abstract class AbstractExtensionManagementService extends CommontExtensio const dependecies: string[] = manifest.extensionDependencies || []; const dependenciesAndPackExtensions = [...dependecies]; if (manifest.extensionPack) { - const existing = getOnlyNewlyAddedFromExtensionPack ? installed.find(e => areSameExtensions(e.identifier, extensionIdentifier)) : undefined; for (const extension of manifest.extensionPack) { - // add only those extensions which are new in currently installed extension - if (!(existing && existing.manifest.extensionPack && existing.manifest.extensionPack.some(old => areSameExtensions({ id: old }, { id: extension })))) { - if (dependenciesAndPackExtensions.every(e => !areSameExtensions({ id: e }, { id: extension }))) { - dependenciesAndPackExtensions.push(extension); - } + if (dependenciesAndPackExtensions.every(e => !areSameExtensions({ id: e }, { id: extension }))) { + dependenciesAndPackExtensions.push(extension); } } } @@ -639,7 +635,7 @@ export abstract class AbstractExtensionManagementService extends CommontExtensio let compatibleExtension: IGalleryExtension | null; const extensionsControlManifest = await this.getExtensionsControlManifest(); - if (isMalicious(extension.identifier, extensionsControlManifest)) { + if (isMalicious(extension.identifier, extensionsControlManifest.malicious)) { throw new ExtensionManagementError(nls.localize('malicious extension', "Can't install '{0}' extension since it was reported to be problematic.", extension.identifier.id), ExtensionManagementErrorCode.Malicious); } @@ -733,8 +729,16 @@ export abstract class AbstractExtensionManagementService extends CommontExtensio const allTasks: IUninstallExtensionTask[] = []; const processedTasks: IUninstallExtensionTask[] = []; const alreadyRequestedUninstalls: Promise[] = []; + const extensionsToRemove: ILocalExtension[] = []; const installedExtensionsMap = new ResourceMap(); + const getInstalledExtensions = async (profileLocation: URI) => { + let installed = installedExtensionsMap.get(profileLocation); + if (!installed) { + installedExtensionsMap.set(profileLocation, installed = await this.getInstalled(ExtensionType.User, profileLocation)); + } + return installed; + }; for (const { extension, options } of extensions) { const uninstallOptions: UninstallExtensionTaskOptions = { @@ -748,14 +752,32 @@ export abstract class AbstractExtensionManagementService extends CommontExtensio } else { allTasks.push(createUninstallExtensionTask(extension, uninstallOptions)); } + + if (uninstallOptions.remove) { + extensionsToRemove.push(extension); + for (const profile of this.userDataProfilesService.profiles) { + if (this.uriIdentityService.extUri.isEqual(profile.extensionsResource, uninstallOptions.profileLocation)) { + continue; + } + const installed = await getInstalledExtensions(profile.extensionsResource); + const profileExtension = installed.find(e => areSameExtensions(e.identifier, extension.identifier)); + if (profileExtension) { + const uninstallOptionsWithProfile = { ...uninstallOptions, profileLocation: profile.extensionsResource }; + const uninstallExtensionTask = this.uninstallingExtensions.get(getUninstallExtensionTaskKey(profileExtension, uninstallOptionsWithProfile)); + if (uninstallExtensionTask) { + this.logService.info('Extensions is already requested to uninstall', profileExtension.identifier.id); + alreadyRequestedUninstalls.push(uninstallExtensionTask.waitUntilTaskIsFinished()); + } else { + allTasks.push(createUninstallExtensionTask(profileExtension, uninstallOptionsWithProfile)); + } + } + } + } } try { for (const task of allTasks.slice(0)) { - let installed = installedExtensionsMap.get(task.options.profileLocation); - if (!installed) { - installedExtensionsMap.set(task.options.profileLocation, installed = await this.getInstalled(ExtensionType.User, task.options.profileLocation)); - } + const installed = await getInstalledExtensions(task.options.profileLocation); if (task.options.donotIncludePack) { this.logService.info('Uninstalling the extension without including packed extension', `${task.extension.identifier.id}@${task.extension.manifest.version}`); @@ -803,6 +825,10 @@ export abstract class AbstractExtensionManagementService extends CommontExtensio for (const task of allTasks) { postUninstallExtension(task.extension, task.options); } + + if (extensionsToRemove.length) { + await this.joinAllSettled(extensionsToRemove.map(extension => this.removeExtension(extension))); + } } catch (e) { const error = toExtensionManagementError(e); for (const task of allTasks) { @@ -899,6 +925,7 @@ export abstract class AbstractExtensionManagementService extends CommontExtensio protected abstract createInstallExtensionTask(manifest: IExtensionManifest, extension: URI | IGalleryExtension, options: InstallExtensionTaskOptions): IInstallExtensionTask; protected abstract createUninstallExtensionTask(extension: ILocalExtension, options: UninstallExtensionTaskOptions): IUninstallExtensionTask; protected abstract copyExtension(extension: ILocalExtension, fromProfileLocation: URI, toProfileLocation: URI, metadata?: Partial): Promise; + protected abstract removeExtension(extension: ILocalExtension): Promise; } export function toExtensionManagementError(error: Error, code?: ExtensionManagementErrorCode): ExtensionManagementError { diff --git a/src/vs/platform/extensionManagement/common/extensionGalleryManifest.ts b/src/vs/platform/extensionManagement/common/extensionGalleryManifest.ts new file mode 100644 index 000000000000..e1d363506598 --- /dev/null +++ b/src/vs/platform/extensionManagement/common/extensionGalleryManifest.ts @@ -0,0 +1,69 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Event } from '../../../base/common/event.js'; +import { createDecorator } from '../../instantiation/common/instantiation.js'; + +export const enum ExtensionGalleryResourceType { + ExtensionQueryService = 'ExtensionQueryService', + ExtensionLatestVersionUri = 'ExtensionLatestVersionUriTemplate', + ExtensionStatisticsUri = 'ExtensionStatisticsUriTemplate', + WebExtensionStatisticsUri = 'WebExtensionStatisticsUriTemplate', + PublisherViewUri = 'PublisherViewUriTemplate', + ExtensionDetailsViewUri = 'ExtensionDetailsViewUriTemplate', + ExtensionRatingViewUri = 'ExtensionRatingViewUriTemplate', +} + +export const enum Flag { + None = 'None', + IncludeVersions = 'IncludeVersions', + IncludeFiles = 'IncludeFiles', + IncludeCategoryAndTags = 'IncludeCategoryAndTags', + IncludeSharedAccounts = 'IncludeSharedAccounts', + IncludeVersionProperties = 'IncludeVersionProperties', + ExcludeNonValidated = 'ExcludeNonValidated', + IncludeInstallationTargets = 'IncludeInstallationTargets', + IncludeAssetUri = 'IncludeAssetUri', + IncludeStatistics = 'IncludeStatistics', + IncludeLatestVersionOnly = 'IncludeLatestVersionOnly', + Unpublished = 'Unpublished', + IncludeNameConflictInfo = 'IncludeNameConflictInfo', + IncludeLatestPrereleaseAndStableVersionOnly = 'IncludeLatestPrereleaseAndStableVersionOnly', +} + +export type ExtensionGalleryManifestResource = { + readonly id: string; + readonly type: string; +}; + +export type ExtensionQueryCapabilityValue = { + readonly name: string; + readonly value: number; +}; + +export interface IExtensionGalleryManifest { + readonly version: string; + readonly resources: readonly ExtensionGalleryManifestResource[]; + readonly capabilities: { + readonly extensionQuery: { + readonly filtering?: readonly ExtensionQueryCapabilityValue[]; + readonly sorting?: readonly ExtensionQueryCapabilityValue[]; + readonly flags?: readonly ExtensionQueryCapabilityValue[]; + }; + readonly signing?: { + readonly allRepositorySigned: boolean; + }; + }; +} + +export const IExtensionGalleryManifestService = createDecorator('IExtensionGalleryManifestService'); + +export interface IExtensionGalleryManifestService { + readonly _serviceBrand: undefined; + + readonly onDidChangeExtensionGalleryManifest: Event; + isEnabled(): boolean; + getExtensionGalleryManifest(): Promise; +} diff --git a/src/vs/platform/extensionManagement/common/extensionGalleryManifestService.ts b/src/vs/platform/extensionManagement/common/extensionGalleryManifestService.ts new file mode 100644 index 000000000000..53d79c003c41 --- /dev/null +++ b/src/vs/platform/extensionManagement/common/extensionGalleryManifestService.ts @@ -0,0 +1,224 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Event } from '../../../base/common/event.js'; +import { Disposable } from '../../../base/common/lifecycle.js'; +import { IProductService } from '../../product/common/productService.js'; +import { ExtensionGalleryResourceType, Flag, IExtensionGalleryManifest, IExtensionGalleryManifestService } from './extensionGalleryManifest.js'; +import { FilterType, SortBy } from './extensionManagement.js'; + +type ExtensionGalleryConfig = { + readonly serviceUrl: string; + readonly itemUrl: string; + readonly publisherUrl: string; + readonly resourceUrlTemplate: string; + readonly extensionUrlTemplate: string; + readonly controlUrl: string; + readonly nlsBaseUrl: string; +}; + +export class ExtensionGalleryManifestService extends Disposable implements IExtensionGalleryManifestService { + + readonly _serviceBrand: undefined; + readonly onDidChangeExtensionGalleryManifest = Event.None; + + constructor( + @IProductService private readonly productService: IProductService, + ) { + super(); + } + + isEnabled(): boolean { + return !!this.productService.extensionsGallery?.serviceUrl; + } + + async getExtensionGalleryManifest(): Promise { + const extensionsGallery = this.productService.extensionsGallery as ExtensionGalleryConfig | undefined; + if (!extensionsGallery?.serviceUrl) { + return null; + } + + const resources = [ + { + id: `${extensionsGallery.serviceUrl}/extensionquery`, + type: ExtensionGalleryResourceType.ExtensionQueryService + }, + { + id: `${extensionsGallery.serviceUrl}/vscode/{publisher}/{name}/latest`, + type: ExtensionGalleryResourceType.ExtensionLatestVersionUri + }, + { + id: `${extensionsGallery.serviceUrl}/publishers/{publisher}/extensions/{name}/{version}/stats?statType={statTypeName}`, + type: ExtensionGalleryResourceType.ExtensionStatisticsUri + }, + { + id: `${extensionsGallery.serviceUrl}/itemName/{publisher}.{name}/version/{version}/statType/{statTypeValue}/vscodewebextension`, + type: ExtensionGalleryResourceType.WebExtensionStatisticsUri + }, + ]; + + if (extensionsGallery.publisherUrl) { + resources.push({ + id: `${extensionsGallery.publisherUrl}/{publisher}`, + type: ExtensionGalleryResourceType.PublisherViewUri + }); + } + + if (extensionsGallery.itemUrl) { + resources.push({ + id: `${extensionsGallery.itemUrl}/?itemName={publisher}.{name}`, + type: ExtensionGalleryResourceType.ExtensionDetailsViewUri + }); + resources.push({ + id: `${extensionsGallery.itemUrl}/?itemName={publisher}.{name}&ssr=false#review-details`, + type: ExtensionGalleryResourceType.ExtensionRatingViewUri + }); + } + + const filtering = [ + { + name: FilterType.Tag, + value: 1, + }, + { + name: FilterType.ExtensionId, + value: 4, + }, + { + name: FilterType.Category, + value: 5, + }, + { + name: FilterType.ExtensionName, + value: 7, + }, + { + name: FilterType.Target, + value: 8, + }, + { + name: FilterType.Featured, + value: 9, + }, + { + name: FilterType.SearchText, + value: 10, + }, + { + name: FilterType.ExcludeWithFlags, + value: 12, + }, + ]; + + const sorting = [ + { + name: SortBy.NoneOrRelevance, + value: 0, + }, + { + name: SortBy.LastUpdatedDate, + value: 1, + }, + { + name: SortBy.Title, + value: 2, + }, + { + name: SortBy.PublisherName, + value: 3, + }, + { + name: SortBy.InstallCount, + value: 4, + }, + { + name: SortBy.AverageRating, + value: 6, + }, + { + name: SortBy.PublishedDate, + value: 10, + }, + { + name: SortBy.WeightedRating, + value: 12, + }, + ]; + + const flags = [ + { + name: Flag.None, + value: 0x0, + }, + { + name: Flag.IncludeVersions, + value: 0x1, + }, + { + name: Flag.IncludeFiles, + value: 0x2, + }, + { + name: Flag.IncludeCategoryAndTags, + value: 0x4, + }, + { + name: Flag.IncludeSharedAccounts, + value: 0x8, + }, + { + name: Flag.IncludeVersionProperties, + value: 0x10, + }, + { + name: Flag.ExcludeNonValidated, + value: 0x20, + }, + { + name: Flag.IncludeInstallationTargets, + value: 0x40, + }, + { + name: Flag.IncludeAssetUri, + value: 0x80, + }, + { + name: Flag.IncludeStatistics, + value: 0x100, + }, + { + name: Flag.IncludeLatestVersionOnly, + value: 0x200, + }, + { + name: Flag.Unpublished, + value: 0x1000, + }, + { + name: Flag.IncludeNameConflictInfo, + value: 0x8000, + }, + { + name: Flag.IncludeLatestPrereleaseAndStableVersionOnly, + value: 0x10000, + }, + ]; + + return { + version: '', + resources, + capabilities: { + extensionQuery: { + filtering, + sorting, + flags, + }, + signing: { + allRepositorySigned: true, + } + } + }; + } +} diff --git a/src/vs/platform/extensionManagement/common/extensionGalleryManifestServiceIpc.ts b/src/vs/platform/extensionManagement/common/extensionGalleryManifestServiceIpc.ts new file mode 100644 index 000000000000..36ce53a0eaaf --- /dev/null +++ b/src/vs/platform/extensionManagement/common/extensionGalleryManifestServiceIpc.ts @@ -0,0 +1,50 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Barrier } from '../../../base/common/async.js'; +import { Emitter, Event } from '../../../base/common/event.js'; +import { IPCServer } from '../../../base/parts/ipc/common/ipc.js'; +import { IProductService } from '../../product/common/productService.js'; +import { IExtensionGalleryManifest, IExtensionGalleryManifestService } from './extensionGalleryManifest.js'; +import { ExtensionGalleryManifestService } from './extensionGalleryManifestService.js'; + +export class ExtensionGalleryManifestIPCService extends ExtensionGalleryManifestService implements IExtensionGalleryManifestService { + + declare readonly _serviceBrand: undefined; + + private _onDidChangeExtensionGalleryManifest = this._register(new Emitter()); + override readonly onDidChangeExtensionGalleryManifest = this._onDidChangeExtensionGalleryManifest.event; + + private extensionGalleryManifest: IExtensionGalleryManifest | null | undefined; + private readonly barrier = new Barrier(); + + constructor( + server: IPCServer, + @IProductService productService: IProductService + ) { + super(productService); + server.registerChannel('extensionGalleryManifest', { + listen: () => Event.None, + call: async (context: any, command: string, args?: any): Promise => { + switch (command) { + case 'setExtensionGalleryManifest': return Promise.resolve(this.setExtensionGalleryManifest(args[0])); + } + throw new Error('Invalid call'); + } + }); + } + + override async getExtensionGalleryManifest(): Promise { + await this.barrier.wait(); + return this.extensionGalleryManifest ?? null; + } + + private setExtensionGalleryManifest(manifest: IExtensionGalleryManifest | null): void { + this.barrier.open(); + this.extensionGalleryManifest = manifest; + this._onDidChangeExtensionGalleryManifest.fire(manifest); + } + +} diff --git a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts index a107e590a129..4ff64b25d5f3 100644 --- a/src/vs/platform/extensionManagement/common/extensionGalleryService.ts +++ b/src/vs/platform/extensionManagement/common/extensionGalleryService.ts @@ -15,7 +15,7 @@ import { URI } from '../../../base/common/uri.js'; import { IHeaders, IRequestContext, IRequestOptions, isOfflineError } from '../../../base/parts/request/common/request.js'; import { IConfigurationService } from '../../configuration/common/configuration.js'; import { IEnvironmentService } from '../../environment/common/environment.js'; -import { getTargetPlatform, IExtensionGalleryService, IExtensionIdentifier, IExtensionInfo, IGalleryExtension, IGalleryExtensionAsset, IGalleryExtensionAssets, IGalleryExtensionVersion, InstallOperation, IQueryOptions, IExtensionsControlManifest, isNotWebExtensionInWebTargetPlatform, isTargetPlatformCompatible, ITranslation, SortBy, SortOrder, StatisticType, toTargetPlatform, WEB_EXTENSION_TAG, IExtensionQueryOptions, IDeprecationInfo, ISearchPrefferedResults, ExtensionGalleryError, ExtensionGalleryErrorCode, IProductVersion, UseUnpkgResourceApiConfigKey, IAllowedExtensionsService, EXTENSION_IDENTIFIER_REGEX } from './extensionManagement.js'; +import { getTargetPlatform, IExtensionGalleryService, IExtensionIdentifier, IExtensionInfo, IGalleryExtension, IGalleryExtensionAsset, IGalleryExtensionAssets, IGalleryExtensionVersion, InstallOperation, IQueryOptions, IExtensionsControlManifest, isNotWebExtensionInWebTargetPlatform, isTargetPlatformCompatible, ITranslation, SortOrder, StatisticType, toTargetPlatform, WEB_EXTENSION_TAG, IExtensionQueryOptions, IDeprecationInfo, ISearchPrefferedResults, ExtensionGalleryError, ExtensionGalleryErrorCode, IProductVersion, UseUnpkgResourceApiConfigKey, IAllowedExtensionsService, EXTENSION_IDENTIFIER_REGEX, SortBy, FilterType } from './extensionManagement.js'; import { adoptToGalleryExtensionId, areSameExtensions, getGalleryExtensionId, getGalleryExtensionTelemetryData } from './extensionManagementUtil.js'; import { IExtensionManifest, TargetPlatform } from '../../extensions/common/extensions.js'; import { areApiProposalsCompatible, isEngineValid } from '../../extensions/common/extensionValidator.js'; @@ -28,6 +28,8 @@ import { IStorageService } from '../../storage/common/storage.js'; import { ITelemetryService } from '../../telemetry/common/telemetry.js'; import { StopWatch } from '../../../base/common/stopwatch.js'; import { format2 } from '../../../base/common/strings.js'; +import { IAssignmentService } from '../../assignment/common/assignment.js'; +import { ExtensionGalleryResourceType, Flag, IExtensionGalleryManifest, IExtensionGalleryManifestService } from './extensionGalleryManifest.js'; const CURRENT_TARGET_PLATFORM = isWeb ? TargetPlatform.WEB : getTargetPlatform(platform, arch); const ACTIVITY_HEADER_NAME = 'X-Market-Search-Activity-Id'; @@ -100,100 +102,6 @@ interface IRawGalleryQueryResult { }[]; } -enum Flags { - - /** - * None is used to retrieve only the basic extension details. - */ - None = 0x0, - - /** - * IncludeVersions will return version information for extensions returned - */ - IncludeVersions = 0x1, - - /** - * IncludeFiles will return information about which files were found - * within the extension that were stored independent of the manifest. - * When asking for files, versions will be included as well since files - * are returned as a property of the versions. - * These files can be retrieved using the path to the file without - * requiring the entire manifest be downloaded. - */ - IncludeFiles = 0x2, - - /** - * Include the Categories and Tags that were added to the extension definition. - */ - IncludeCategoryAndTags = 0x4, - - /** - * Include the details about which accounts the extension has been shared - * with if the extension is a private extension. - */ - IncludeSharedAccounts = 0x8, - - /** - * Include properties associated with versions of the extension - */ - IncludeVersionProperties = 0x10, - - /** - * Excluding non-validated extensions will remove any extension versions that - * either are in the process of being validated or have failed validation. - */ - ExcludeNonValidated = 0x20, - - /** - * Include the set of installation targets the extension has requested. - */ - IncludeInstallationTargets = 0x40, - - /** - * Include the base uri for assets of this extension - */ - IncludeAssetUri = 0x80, - - /** - * Include the statistics associated with this extension - */ - IncludeStatistics = 0x100, - - /** - * When retrieving versions from a query, only include the latest - * version of the extensions that matched. This is useful when the - * caller doesn't need all the published versions. It will save a - * significant size in the returned payload. - */ - IncludeLatestVersionOnly = 0x200, - - /** - * The Unpublished extension flag indicates that the extension can't be installed/downloaded. - * Users who have installed such an extension can continue to use the extension. - */ - Unpublished = 0x1000, - - /** - * Include the details if an extension is in conflict list or not - */ - IncludeNameConflictInfo = 0x8000, -} - -function flagsToString(...flags: Flags[]): string { - return String(flags.reduce((r, f) => r | f, 0)); -} - -enum FilterType { - Tag = 1, - ExtensionId = 4, - Category = 5, - ExtensionName = 7, - Target = 8, - Featured = 9, - SearchText = 10, - ExcludeWithFlags = 12 -} - const AssetType = { Icon: 'Microsoft.VisualStudio.Services.Icons.Default', Details: 'Microsoft.VisualStudio.Services.Content.Details', @@ -216,6 +124,7 @@ const PropertyType = { SponsorLink: 'Microsoft.VisualStudio.Code.SponsorLink', SupportLink: 'Microsoft.VisualStudio.Services.Links.Support', ExecutesCode: 'Microsoft.VisualStudio.Code.ExecutesCode', + Private: 'PrivateMarketplace', }; interface ICriterium { @@ -230,7 +139,7 @@ interface IQueryState { readonly pageSize: number; readonly sortBy: SortBy; readonly sortOrder: SortOrder; - readonly flags: Flags; + readonly flags: Flag[]; readonly criteria: ICriterium[]; readonly assetTypes: string[]; readonly source?: string; @@ -241,7 +150,7 @@ const DefaultQueryState: IQueryState = { pageSize: DefaultPageSize, sortBy: SortBy.NoneOrRelevance, sortOrder: SortOrder.Default, - flags: Flags.None, + flags: [], criteria: [], assetTypes: [] }; @@ -266,8 +175,8 @@ type GalleryServiceQueryClassification = { }; type QueryTelemetryData = { - readonly flags: number; readonly filterTypes: string[]; + readonly flags: string[]; readonly sortBy: string; readonly sortOrder: string; readonly pageNumber: string; @@ -303,6 +212,7 @@ type ExtensionsCriteria = { readonly compatible: boolean; readonly includePreRelease: boolean | (IExtensionIdentifier & { includePreRelease: boolean })[]; readonly versions?: (IExtensionIdentifier & { version: string })[]; + readonly isQueryForReleaseVersionFromPreReleaseVersion?: boolean; }; const enum VersionKind { @@ -324,10 +234,17 @@ class Query { get pageNumber(): number { return this.state.pageNumber; } get pageSize(): number { return this.state.pageSize; } - get sortBy(): number { return this.state.sortBy; } + get sortBy(): SortBy { return this.state.sortBy; } get sortOrder(): number { return this.state.sortOrder; } - get flags(): number { return this.state.flags; } + get flags(): Flag[] { return this.state.flags; } get criteria(): ICriterium[] { return this.state.criteria; } + get assetTypes(): string[] { return this.state.assetTypes; } + get source(): string | undefined { return this.state.source; } + get searchText(): string { + const criterium = this.state.criteria.filter(criterium => criterium.filterType === FilterType.SearchText)[0]; + return criterium && criterium.value ? criterium.value : ''; + } + withPage(pageNumber: number, pageSize: number = this.state.pageSize): Query { return new Query({ ...this.state, pageNumber, pageSize }); @@ -350,8 +267,8 @@ class Query { return new Query({ ...this.state, sortOrder }); } - withFlags(...flags: Flags[]): Query { - return new Query({ ...this.state, flags: flags.reduce((r, f) => r | f, 0) }); + withFlags(...flags: Flag[]): Query { + return new Query({ ...this.state, flags: distinct(flags) }); } withAssetTypes(...assetTypes: string[]): Query { @@ -361,29 +278,6 @@ class Query { withSource(source: string): Query { return new Query({ ...this.state, source }); } - - get raw() { - const { criteria, pageNumber, pageSize, sortBy, sortOrder, flags, assetTypes } = this.state; - const filters = [{ criteria, pageNumber, pageSize, sortBy, sortOrder }]; - return { filters, assetTypes, flags }; - } - - get searchText(): string { - const criterium = this.state.criteria.filter(criterium => criterium.filterType === FilterType.SearchText)[0]; - return criterium && criterium.value ? criterium.value : ''; - } - - get telemetryData(): QueryTelemetryData { - return { - filterTypes: this.state.criteria.map(criterium => String(criterium.filterType)), - flags: this.state.flags, - sortBy: String(this.sortBy), - sortOrder: String(this.sortOrder), - pageNumber: String(this.pageNumber), - source: this.state.source, - searchTextLength: this.searchText.length - }; - } } function getStatistic(statistics: IRawGalleryExtensionStatistics[], name: string): number { @@ -446,6 +340,11 @@ function isPreReleaseVersion(version: IRawGalleryExtensionVersion): boolean { return values.length > 0 && values[0].value === 'true'; } +function isPrivateExtension(version: IRawGalleryExtensionVersion): boolean { + const values = version.properties ? version.properties.filter(p => p.key === PropertyType.Private) : []; + return values.length > 0 && values[0].value === 'true'; +} + function executesCode(version: IRawGalleryExtensionVersion): boolean | undefined { const values = version.properties ? version.properties.filter(p => p.key === PropertyType.ExecutesCode) : []; return values.length > 0 ? values[0].value === 'true' : undefined; @@ -533,7 +432,21 @@ function setTelemetry(extension: IGalleryExtension, index: number, querySource?: extension.telemetryData = { index, querySource, queryActivityId: extension.queryContext?.[ACTIVITY_HEADER_NAME] }; } -function toExtension(galleryExtension: IRawGalleryExtension, version: IRawGalleryExtensionVersion, allTargetPlatforms: TargetPlatform[], queryContext?: IStringDictionary): IGalleryExtension { +function getExtensionGalleryManifestResourceUri(manifest: IExtensionGalleryManifest, type: ExtensionGalleryResourceType, version?: string): string | undefined { + for (const resource of manifest.resources) { + const [r, v] = resource.type.split('/'); + if (r !== type) { + continue; + } + if (!version || v === version) { + return resource.id; + } + break; + } + return undefined; +} + +function toExtension(galleryExtension: IRawGalleryExtension, version: IRawGalleryExtensionVersion, allTargetPlatforms: TargetPlatform[], extensionGalleryManifest: IExtensionGalleryManifest, queryContext?: IStringDictionary): IGalleryExtension { const latestVersion = galleryExtension.versions[0]; const assets: IGalleryExtensionAssets = { manifest: getVersionAsset(version, AssetType.Manifest), @@ -547,6 +460,10 @@ function toExtension(galleryExtension: IRawGalleryExtension, version: IRawGaller coreTranslations: getCoreTranslationAssets(version) }; + const detailsViewUri = getExtensionGalleryManifestResourceUri(extensionGalleryManifest, ExtensionGalleryResourceType.ExtensionDetailsViewUri); + const publisherViewUri = getExtensionGalleryManifestResourceUri(extensionGalleryManifest, ExtensionGalleryResourceType.PublisherViewUri); + const ratingViewUri = getExtensionGalleryManifestResourceUri(extensionGalleryManifest, ExtensionGalleryResourceType.ExtensionRatingViewUri); + return { type: 'gallery', identifier: { @@ -583,10 +500,14 @@ function toExtension(galleryExtension: IRawGalleryExtension, version: IRawGaller }, hasPreReleaseVersion: isPreReleaseVersion(latestVersion), hasReleaseVersion: true, + private: isPrivateExtension(latestVersion), preview: getIsPreview(galleryExtension.flags), isSigned: !!assets.signature, queryContext, - supportLink: getSupportLink(latestVersion) + supportLink: getSupportLink(latestVersion), + detailsLink: detailsViewUri ? format2(detailsViewUri, { publisher: galleryExtension.publisher.publisherName, name: galleryExtension.extensionName }) : undefined, + publisherLink: publisherViewUri ? format2(publisherViewUri, { publisher: galleryExtension.publisher.publisherName }) : undefined, + ratingLink: ratingViewUri ? format2(ratingViewUri, { publisher: galleryExtension.publisher.publisherName, name: galleryExtension.extensionName }) : undefined, }; } @@ -611,19 +532,19 @@ interface IRawExtensionsControlManifest { extensionsEnabledWithPreRelease?: string[]; } -abstract class AbstractExtensionGalleryService implements IExtensionGalleryService { +export abstract class AbstractExtensionGalleryService implements IExtensionGalleryService { declare readonly _serviceBrand: undefined; - private readonly extensionsGalleryUrl: string | undefined; private readonly extensionsControlUrl: string | undefined; - private readonly extensionUrlTemplate: string | undefined; + private readonly unpkgResourceApi: string | undefined; private readonly commonHeadersPromise: Promise; private readonly extensionsEnabledWithApiProposalVersion: string[]; constructor( storageService: IStorageService | undefined, + private readonly assignmentService: IAssignmentService | undefined, @IRequestService private readonly requestService: IRequestService, @ILogService private readonly logService: ILogService, @IEnvironmentService private readonly environmentService: IEnvironmentService, @@ -632,11 +553,10 @@ abstract class AbstractExtensionGalleryService implements IExtensionGalleryServi @IProductService private readonly productService: IProductService, @IConfigurationService private readonly configurationService: IConfigurationService, @IAllowedExtensionsService private readonly allowedExtensionsService: IAllowedExtensionsService, + @IExtensionGalleryManifestService private readonly extensionGalleryManifestService: IExtensionGalleryManifestService, ) { - const config = productService.extensionsGallery; - this.extensionsGalleryUrl = config?.serviceUrl; - this.extensionsControlUrl = config?.controlUrl; - this.extensionUrlTemplate = config?.extensionUrlTemplate; + this.extensionsControlUrl = productService.extensionsGallery?.controlUrl; + this.unpkgResourceApi = productService.extensionsGallery?.extensionUrlTemplate; this.extensionsEnabledWithApiProposalVersion = productService.extensionsEnabledWithApiProposalVersion?.map(id => id.toLowerCase()) ?? []; this.commonHeadersPromise = resolveMarketplaceHeaders( productService.version, @@ -648,28 +568,25 @@ abstract class AbstractExtensionGalleryService implements IExtensionGalleryServi this.telemetryService); } - private api(path = ''): string { - return `${this.extensionsGalleryUrl}${path}`; - } - isEnabled(): boolean { - return !!this.extensionsGalleryUrl; + return this.extensionGalleryManifestService.isEnabled(); } getExtensions(extensionInfos: ReadonlyArray, token: CancellationToken): Promise; getExtensions(extensionInfos: ReadonlyArray, options: IExtensionQueryOptions, token: CancellationToken): Promise; async getExtensions(extensionInfos: ReadonlyArray, arg1: any, arg2?: any): Promise { - if (!this.isEnabled()) { + const extensionGalleryManifest = await this.extensionGalleryManifestService.getExtensionGalleryManifest(); + if (!extensionGalleryManifest) { throw new Error('No extension gallery service configured.'); } const options = CancellationToken.isCancellationToken(arg1) ? {} : arg1 as IExtensionQueryOptions; const token = CancellationToken.isCancellationToken(arg1) ? arg1 : arg2 as CancellationToken; - const useResourceApi = options.preferResourceApi && (this.configurationService.getValue(UseUnpkgResourceApiConfigKey) ?? false); - const result = useResourceApi - ? await this.getExtensionsUsingResourceApi(extensionInfos, options, token) - : await this.doGetExtensions(extensionInfos, options, token); + const resourceApi = (options.preferResourceApi && (this.configurationService.getValue(UseUnpkgResourceApiConfigKey) ?? false)) ? await this.getResourceApi(extensionGalleryManifest) : undefined; + const result = resourceApi + ? await this.getExtensionsUsingResourceApi(extensionInfos, options, resourceApi, extensionGalleryManifest, token) + : await this.getExtensionsUsingQueryApi(extensionInfos, options, extensionGalleryManifest, token); const uuids = result.map(r => r.identifier.uuid); const extensionInfosByName: IExtensionInfo[] = []; @@ -691,29 +608,61 @@ abstract class AbstractExtensionGalleryService implements IExtensionGalleryServi count: extensionInfosByName.length }); - const extensions = await this.doGetExtensions(extensionInfosByName, options, token); + const extensions = await this.getExtensionsUsingQueryApi(extensionInfosByName, options, extensionGalleryManifest, token); result.push(...extensions); } return result; } - private async doGetExtensions(extensionInfos: ReadonlyArray, options: IExtensionQueryOptions, token: CancellationToken): Promise { - const names: string[] = []; const ids: string[] = [], includePreReleases: (IExtensionIdentifier & { includePreRelease: boolean })[] = [], versions: (IExtensionIdentifier & { version: string })[] = []; + private async getResourceApi(extensionGalleryManifest: IExtensionGalleryManifest): Promise<{ uri: string; fallback?: string } | undefined> { + const latestVersionResource = getExtensionGalleryManifestResourceUri(extensionGalleryManifest, ExtensionGalleryResourceType.ExtensionLatestVersionUri); + if (!latestVersionResource) { + return undefined; + } + + if (this.productService.quality !== 'stable') { + return { + uri: latestVersionResource, + fallback: this.unpkgResourceApi + }; + } + + const value = await this.assignmentService?.getTreatment<'unpkg' | 'marketplace' | 'none'>('extensions.gallery.useResourceApi') ?? 'unpkg'; + + if (value === 'marketplace') { + return { + uri: latestVersionResource, + fallback: this.unpkgResourceApi + }; + } + + if (value === 'unpkg' && this.unpkgResourceApi) { + return { uri: this.unpkgResourceApi }; + } + + return undefined; + } + + private async getExtensionsUsingQueryApi(extensionInfos: ReadonlyArray, options: IExtensionQueryOptions, extensionGalleryManifest: IExtensionGalleryManifest, token: CancellationToken): Promise { + const names: string[] = [], + ids: string[] = [], + includePreRelease: (IExtensionIdentifier & { includePreRelease: boolean })[] = [], + versions: (IExtensionIdentifier & { version: string })[] = []; let isQueryForReleaseVersionFromPreReleaseVersion = true; + for (const extensionInfo of extensionInfos) { if (extensionInfo.uuid) { ids.push(extensionInfo.uuid); } else { names.push(extensionInfo.id); } - // Set includePreRelease to true if version is set, because the version can be a pre-release version - const includePreRelease = !!(extensionInfo.version || extensionInfo.preRelease); - includePreReleases.push({ id: extensionInfo.id, uuid: extensionInfo.uuid, includePreRelease }); if (extensionInfo.version) { versions.push({ id: extensionInfo.id, uuid: extensionInfo.uuid, version: extensionInfo.version }); + } else { + includePreRelease.push({ id: extensionInfo.id, uuid: extensionInfo.uuid, includePreRelease: !!extensionInfo.preRelease }); } - isQueryForReleaseVersionFromPreReleaseVersion = isQueryForReleaseVersionFromPreReleaseVersion && (!!extensionInfo.hasPreRelease && !includePreRelease); + isQueryForReleaseVersionFromPreReleaseVersion = isQueryForReleaseVersionFromPreReleaseVersion && (!!extensionInfo.hasPreRelease && !extensionInfo.preRelease); } if (!ids.length && !names.length) { @@ -727,63 +676,62 @@ abstract class AbstractExtensionGalleryService implements IExtensionGalleryServi if (names.length) { query = query.withFilter(FilterType.ExtensionName, ...names); } - if (options.queryAllVersions || isQueryForReleaseVersionFromPreReleaseVersion /* Inlcude all versions if every requested extension is for release version and has pre-release version */) { - query = query.withFlags(query.flags, Flags.IncludeVersions); + if (options.queryAllVersions) { + query = query.withFlags(...query.flags, Flag.IncludeVersions); } if (options.source) { query = query.withSource(options.source); } - const { extensions } = await this.queryGalleryExtensions(query, { targetPlatform: options.targetPlatform ?? CURRENT_TARGET_PLATFORM, includePreRelease: includePreReleases, versions, compatible: !!options.compatible, productVersion: options.productVersion ?? { version: this.productService.version, date: this.productService.date } }, token); + const { extensions } = await this.queryGalleryExtensions( + query, + { + targetPlatform: options.targetPlatform ?? CURRENT_TARGET_PLATFORM, + includePreRelease, + versions, + compatible: !!options.compatible, + productVersion: options.productVersion ?? { version: this.productService.version, date: this.productService.date }, + isQueryForReleaseVersionFromPreReleaseVersion + }, + extensionGalleryManifest, + token); + if (options.source) { extensions.forEach((e, index) => setTelemetry(e, index, options.source)); } + return extensions; } - private async getExtensionsUsingResourceApi(extensionInfos: ReadonlyArray, options: IExtensionQueryOptions, token: CancellationToken): Promise { + private async getExtensionsUsingResourceApi(extensionInfos: ReadonlyArray, options: IExtensionQueryOptions, resourceApi: { uri: string; fallback?: string }, extensionGalleryManifest: IExtensionGalleryManifest, token: CancellationToken): Promise { - const toQuery: IExtensionInfo[] = []; const result: IGalleryExtension[] = []; + const toQuery: IExtensionInfo[] = []; + const toFetchLatest: IExtensionInfo[] = []; - await Promise.allSettled(extensionInfos.map(async extensionInfo => { + for (const extensionInfo of extensionInfos) { + if (!EXTENSION_IDENTIFIER_REGEX.test(extensionInfo.id)) { + continue; + } if (extensionInfo.version) { toQuery.push(extensionInfo); - return; - } - - if (!EXTENSION_IDENTIFIER_REGEX.test(extensionInfo.id)) { - return; + } else { + toFetchLatest.push(extensionInfo); } + } + await Promise.allSettled(toFetchLatest.map(async extensionInfo => { + let galleryExtension: IGalleryExtension | null | 'NOT_FOUND'; try { - const rawGalleryExtension = await this.getLatestRawGalleryExtension(extensionInfo.id, token); - if (!rawGalleryExtension) { - if (extensionInfo.uuid) { - toQuery.push(extensionInfo); + try { + galleryExtension = await this.getLatestGalleryExtension(extensionInfo, options, resourceApi.uri, extensionGalleryManifest, token); + } catch (error) { + if (!resourceApi.fallback) { + throw error; } - return; - } - const allTargetPlatforms = getAllTargetPlatforms(rawGalleryExtension); - const rawGalleryExtensionVersion = await this.getRawGalleryExtensionVersion( - rawGalleryExtension, - { - targetPlatform: options.targetPlatform ?? CURRENT_TARGET_PLATFORM, - compatible: !!options.compatible, - productVersion: options.productVersion ?? { - version: this.productService.version, - date: this.productService.date - }, - version: extensionInfo.preRelease ? VersionKind.Prerelease : VersionKind.Release - }, allTargetPlatforms); - - if (rawGalleryExtensionVersion) { - result.push(toExtension(rawGalleryExtension, rawGalleryExtensionVersion, allTargetPlatforms)); - } - - // report telemetry - else { + // fallback to unpkg + this.logService.error(`Error while getting the latest version for the extension ${extensionInfo.id} from ${resourceApi.uri}. Trying the fallback ${resourceApi.fallback}`, getErrorMessage(error)); this.telemetryService.publicLog2< { extension: string; @@ -792,32 +740,92 @@ abstract class AbstractExtensionGalleryService implements IExtensionGalleryServi }, { owner: 'sandy081'; - comment: 'Report the fallback to the Marketplace query for fetching extensions'; + comment: 'Report the fallback to the unpkg service for getting latest extension'; extension: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Extension id' }; preRelease: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Get pre-release version' }; compatible: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Get compatible version' }; - }>('galleryService:fallbacktoquery', { + }>('galleryService:fallbacktounpkg', { extension: extensionInfo.id, preRelease: !!extensionInfo.preRelease, compatible: !!options.compatible }); - if (!options.compatible || this.allowedExtensionsService.isAllowed({ id: extensionInfo.id, publisherDisplayName: rawGalleryExtension.publisher.displayName }) === true) { + galleryExtension = await this.getLatestGalleryExtension(extensionInfo, options, resourceApi.fallback, extensionGalleryManifest, token); + } + + if (galleryExtension === 'NOT_FOUND') { + if (extensionInfo.uuid) { + // Fallback to query if extension with UUID is not found. Probably extension is renamed. toQuery.push(extensionInfo); } + return; } + + if (galleryExtension) { + result.push(galleryExtension); + } + } catch (error) { - // Skip if there is an error while getting the latest version + // fallback to query this.logService.error(`Error while getting the latest version for the extension ${extensionInfo.id}.`, getErrorMessage(error)); + this.telemetryService.publicLog2< + { + extension: string; + preRelease: boolean; + compatible: boolean; + }, + { + owner: 'sandy081'; + comment: 'Report the fallback to the Marketplace query for fetching extensions'; + extension: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Extension id' }; + preRelease: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Get pre-release version' }; + compatible: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Get compatible version' }; + }>('galleryService:fallbacktoquery', { + extension: extensionInfo.id, + preRelease: !!extensionInfo.preRelease, + compatible: !!options.compatible + }); toQuery.push(extensionInfo); } + })); - const extensions = await this.doGetExtensions(toQuery, options, token); - result.push(...extensions); + if (toQuery.length) { + const extensions = await this.getExtensionsUsingQueryApi(toQuery, options, extensionGalleryManifest, token); + result.push(...extensions); + } return result; } + private async getLatestGalleryExtension(extensionInfo: IExtensionInfo, options: IExtensionQueryOptions, resourceUriTemplate: string, extensionGalleryManifest: IExtensionGalleryManifest, token: CancellationToken): Promise { + const [publisher, name] = extensionInfo.id.split('.'); + const uri = URI.parse(format2(resourceUriTemplate, { publisher, name })); + const rawGalleryExtension = await this.getLatestRawGalleryExtension(extensionInfo.id, uri, token); + + if (!rawGalleryExtension) { + return 'NOT_FOUND'; + } + + const allTargetPlatforms = getAllTargetPlatforms(rawGalleryExtension); + const rawGalleryExtensionVersion = await this.getRawGalleryExtensionVersion( + rawGalleryExtension, + { + targetPlatform: options.targetPlatform ?? CURRENT_TARGET_PLATFORM, + compatible: !!options.compatible, + productVersion: options.productVersion ?? { + version: this.productService.version, + date: this.productService.date + }, + version: extensionInfo.preRelease ? VersionKind.Prerelease : VersionKind.Release + }, allTargetPlatforms); + + if (rawGalleryExtensionVersion) { + return toExtension(rawGalleryExtension, rawGalleryExtensionVersion, allTargetPlatforms, extensionGalleryManifest); + } + + return null; + } + async getCompatibleExtension(extension: IGalleryExtension, includePreRelease: boolean, targetPlatform: TargetPlatform, productVersion: IProductVersion = { version: this.productService.version, date: this.productService.date }): Promise { if (isNotWebExtensionInWebTargetPlatform(extension.allTargetPlatforms, targetPlatform)) { return null; @@ -828,12 +836,18 @@ abstract class AbstractExtensionGalleryService implements IExtensionGalleryServi if (this.allowedExtensionsService.isAllowed({ id: extension.identifier.id, publisherDisplayName: extension.publisherDisplayName }) !== true) { return null; } - const query = new Query() - .withFlags(Flags.IncludeVersions) - .withPage(1, 1) - .withFilter(FilterType.ExtensionId, extension.identifier.uuid); - const { extensions } = await this.queryGalleryExtensions(query, { targetPlatform, compatible: true, includePreRelease, productVersion }, CancellationToken.None); - return extensions[0] || null; + const result = await this.getExtensions([{ + ...extension.identifier, + preRelease: includePreRelease, + hasPreRelease: extension.hasPreReleaseVersion, + }], { + compatible: true, + productVersion, + queryAllVersions: true, + targetPlatform, + }, CancellationToken.None); + + return result[0] ?? null; } async isExtensionCompatible(extension: IGalleryExtension, includePreRelease: boolean, targetPlatform: TargetPlatform, productVersion: IProductVersion = { version: this.productService.version, date: this.productService.date }): Promise { @@ -926,6 +940,12 @@ abstract class AbstractExtensionGalleryService implements IExtensionGalleryServi } async query(options: IQueryOptions, token: CancellationToken): Promise> { + const extensionGalleryManifest = await this.extensionGalleryManifestService.getExtensionGalleryManifest(); + + if (!extensionGalleryManifest) { + throw new Error('No extension gallery service configured.'); + } + let text = options.text || ''; const pageSize = options.pageSize ?? 50; @@ -958,12 +978,16 @@ abstract class AbstractExtensionGalleryService implements IExtensionGalleryServi query = query.withFilter(FilterType.SearchText, text); } - query = query.withSortBy(SortBy.NoneOrRelevance); + if (extensionGalleryManifest.capabilities.extensionQuery.sorting?.some(c => c.name === SortBy.NoneOrRelevance)) { + query = query.withSortBy(SortBy.NoneOrRelevance); + } } else { - query = query.withSortBy(SortBy.InstallCount); + if (extensionGalleryManifest.capabilities.extensionQuery.sorting?.some(c => c.name === SortBy.InstallCount)) { + query = query.withSortBy(SortBy.InstallCount); + } } - if (typeof options.sortBy === 'number') { + if (options.sortBy && extensionGalleryManifest.capabilities.extensionQuery.sorting?.some(c => c.name === options.sortBy)) { query = query.withSortBy(options.sortBy); } @@ -976,7 +1000,7 @@ abstract class AbstractExtensionGalleryService implements IExtensionGalleryServi } const runQuery = async (query: Query, token: CancellationToken) => { - const { extensions, total } = await this.queryGalleryExtensions(query, { targetPlatform: CURRENT_TARGET_PLATFORM, compatible: false, includePreRelease: !!options.includePreRelease, productVersion: options.productVersion ?? { version: this.productService.version, date: this.productService.date } }, token); + const { extensions, total } = await this.queryGalleryExtensions(query, { targetPlatform: CURRENT_TARGET_PLATFORM, compatible: false, includePreRelease: !!options.includePreRelease, productVersion: options.productVersion ?? { version: this.productService.version, date: this.productService.date } }, extensionGalleryManifest, token); extensions.forEach((e, index) => setTelemetry(e, ((query.pageNumber - 1) * query.pageSize) + index, options.source)); return { extensions, total }; }; @@ -992,37 +1016,48 @@ abstract class AbstractExtensionGalleryService implements IExtensionGalleryServi return { firstPage: extensions, total, pageSize: query.pageSize, getPage }; } - private async queryGalleryExtensions(query: Query, criteria: ExtensionsCriteria, token: CancellationToken): Promise<{ extensions: IGalleryExtension[]; total: number }> { + private async queryGalleryExtensions(query: Query, criteria: ExtensionsCriteria, extensionGalleryManifest: IExtensionGalleryManifest, token: CancellationToken): Promise<{ extensions: IGalleryExtension[]; total: number }> { + if ( + this.productService.quality !== 'stable' + && (await this.assignmentService?.getTreatment('useLatestPrereleaseAndStableVersionFlag')) + ) { + return this.queryGalleryExtensionsUsingIncludeLatestPrereleaseAndStableVersionFlag(query, criteria, extensionGalleryManifest, token); + } + + return this.queryGalleryExtensionsWithAllVersionsAsFallback(query, criteria, extensionGalleryManifest, token); + } + + private async queryGalleryExtensionsWithAllVersionsAsFallback(query: Query, criteria: ExtensionsCriteria, extensionGalleryManifest: IExtensionGalleryManifest, token: CancellationToken): Promise<{ extensions: IGalleryExtension[]; total: number }> { const flags = query.flags; /** * If both version flags (IncludeLatestVersionOnly and IncludeVersions) are included, then only include latest versions (IncludeLatestVersionOnly) flag. */ - if (!!(query.flags & Flags.IncludeLatestVersionOnly) && !!(query.flags & Flags.IncludeVersions)) { - query = query.withFlags(query.flags & ~Flags.IncludeVersions, Flags.IncludeLatestVersionOnly); + if (query.flags.includes(Flag.IncludeLatestVersionOnly) && query.flags.includes(Flag.IncludeVersions)) { + query = query.withFlags(...query.flags.filter(flag => flag !== Flag.IncludeVersions)); } /** * If version flags (IncludeLatestVersionOnly and IncludeVersions) are not included, default is to query for latest versions (IncludeLatestVersionOnly). */ - if (!(query.flags & Flags.IncludeLatestVersionOnly) && !(query.flags & Flags.IncludeVersions)) { - query = query.withFlags(query.flags, Flags.IncludeLatestVersionOnly); + if (!query.flags.includes(Flag.IncludeLatestVersionOnly) && !query.flags.includes(Flag.IncludeVersions)) { + query = query.withFlags(...query.flags, Flag.IncludeLatestVersionOnly); } /** - * If versions criteria exist, then remove IncludeLatestVersionOnly flag and add IncludeVersions flag. + * If versions criteria exist or every requested extension is for release version and has a pre-release version, then remove latest flags and add all versions flag. */ - if (criteria.versions?.length) { - query = query.withFlags(query.flags & ~Flags.IncludeLatestVersionOnly, Flags.IncludeVersions); + if (criteria.versions?.length || criteria.isQueryForReleaseVersionFromPreReleaseVersion) { + query = query.withFlags(...query.flags.filter(flag => flag !== Flag.IncludeLatestVersionOnly), Flag.IncludeVersions); } /** * Add necessary extension flags */ - query = query.withFlags(query.flags, Flags.IncludeAssetUri, Flags.IncludeCategoryAndTags, Flags.IncludeFiles, Flags.IncludeStatistics, Flags.IncludeVersionProperties); - const { galleryExtensions: rawGalleryExtensions, total, context } = await this.queryRawGalleryExtensions(query, token); + query = query.withFlags(...query.flags, Flag.IncludeAssetUri, Flag.IncludeCategoryAndTags, Flag.IncludeFiles, Flag.IncludeStatistics, Flag.IncludeVersionProperties); + const { galleryExtensions: rawGalleryExtensions, total, context } = await this.queryRawGalleryExtensions(query, extensionGalleryManifest, token); - const hasAllVersions: boolean = !(query.flags & Flags.IncludeLatestVersionOnly); + const hasAllVersions: boolean = !query.flags.includes(Flag.IncludeLatestVersionOnly); if (hasAllVersions) { const extensions: IGalleryExtension[] = []; for (const rawGalleryExtension of rawGalleryExtensions) { @@ -1041,7 +1076,7 @@ abstract class AbstractExtensionGalleryService implements IExtensionGalleryServi allTargetPlatforms ); if (rawGalleryExtensionVersion) { - extensions.push(toExtension(rawGalleryExtension, rawGalleryExtensionVersion, allTargetPlatforms, context)); + extensions.push(toExtension(rawGalleryExtension, rawGalleryExtensionVersion, allTargetPlatforms, extensionGalleryManifest, context)); } } return { extensions, total }; @@ -1075,7 +1110,7 @@ abstract class AbstractExtensionGalleryService implements IExtensionGalleryServi }, allTargetPlatforms ); - const extension = rawGalleryExtensionVersion ? toExtension(rawGalleryExtension, rawGalleryExtensionVersion, allTargetPlatforms, context) : null; + const extension = rawGalleryExtensionVersion ? toExtension(rawGalleryExtension, rawGalleryExtensionVersion, allTargetPlatforms, extensionGalleryManifest, context) : null; if (!extension /** Need all versions if the extension is a pre-release version but * - the query is to look for a release version or @@ -1099,10 +1134,10 @@ abstract class AbstractExtensionGalleryService implements IExtensionGalleryServi if (needAllVersions.size) { const stopWatch = new StopWatch(); const query = new Query() - .withFlags(flags & ~Flags.IncludeLatestVersionOnly, Flags.IncludeVersions) + .withFlags(...flags.filter(flag => flag !== Flag.IncludeLatestVersionOnly), Flag.IncludeVersions) .withPage(1, needAllVersions.size) .withFilter(FilterType.ExtensionId, ...needAllVersions.keys()); - const { extensions } = await this.queryGalleryExtensions(query, criteria, token); + const { extensions } = await this.queryGalleryExtensions(query, criteria, extensionGalleryManifest, token); this.telemetryService.publicLog2('galleryService:additionalQuery', { duration: stopWatch.elapsed(), count: needAllVersions.size @@ -1116,6 +1151,72 @@ abstract class AbstractExtensionGalleryService implements IExtensionGalleryServi return { extensions: result.sort((a, b) => a[0] - b[0]).map(([, extension]) => extension), total }; } + private async queryGalleryExtensionsUsingIncludeLatestPrereleaseAndStableVersionFlag(query: Query, criteria: ExtensionsCriteria, extensionGalleryManifest: IExtensionGalleryManifest, token: CancellationToken): Promise<{ extensions: IGalleryExtension[]; total: number }> { + + /** + * If versions criteria exist, then remove latest flags and add all versions flag. + */ + if (criteria.versions?.length) { + query = query.withFlags(...query.flags.filter(flag => flag !== Flag.IncludeLatestVersionOnly && flag !== Flag.IncludeLatestPrereleaseAndStableVersionOnly), Flag.IncludeVersions); + } + + /** + * If the query does not specify all versions flag, handle latest versions. + */ + else if (!query.flags.includes(Flag.IncludeVersions)) { + const includeLatest = isBoolean(criteria.includePreRelease) ? criteria.includePreRelease : criteria.includePreRelease.every(({ includePreRelease }) => includePreRelease); + query = includeLatest ? query.withFlags(...query.flags.filter(flag => flag !== Flag.IncludeLatestPrereleaseAndStableVersionOnly), Flag.IncludeLatestVersionOnly) : query.withFlags(...query.flags.filter(flag => flag !== Flag.IncludeLatestVersionOnly), Flag.IncludeLatestPrereleaseAndStableVersionOnly); + } + + /** + * If all versions flag is set, remove latest flags. + */ + if (query.flags.includes(Flag.IncludeVersions) && (query.flags.includes(Flag.IncludeLatestVersionOnly) || query.flags.includes(Flag.IncludeLatestPrereleaseAndStableVersionOnly))) { + query = query.withFlags(...query.flags.filter(flag => flag !== Flag.IncludeLatestVersionOnly && flag !== Flag.IncludeLatestPrereleaseAndStableVersionOnly), Flag.IncludeVersions); + } + + /** + * Add necessary extension flags + */ + query = query.withFlags(...query.flags, Flag.IncludeAssetUri, Flag.IncludeCategoryAndTags, Flag.IncludeFiles, Flag.IncludeStatistics, Flag.IncludeVersionProperties); + const { galleryExtensions: rawGalleryExtensions, total, context } = await this.queryRawGalleryExtensions(query, extensionGalleryManifest, token); + + const extensions: IGalleryExtension[] = []; + for (let index = 0; index < rawGalleryExtensions.length; index++) { + const rawGalleryExtension = rawGalleryExtensions[index]; + const extensionIdentifier = { id: getGalleryExtensionId(rawGalleryExtension.publisher.publisherName, rawGalleryExtension.extensionName), uuid: rawGalleryExtension.extensionId }; + const allTargetPlatforms = getAllTargetPlatforms(rawGalleryExtension); + if (criteria.compatible) { + // Skip looking for all versions if requested for a web-compatible extension and it is not a web extension. + if (isNotWebExtensionInWebTargetPlatform(allTargetPlatforms, criteria.targetPlatform)) { + continue; + } + // Skip looking for all versions if the extension is not allowed. + if (this.allowedExtensionsService.isAllowed({ id: extensionIdentifier.id, publisherDisplayName: rawGalleryExtension.publisher.displayName }) !== true) { + continue; + } + } + + const version = criteria.versions?.find(extensionIdentifierWithVersion => areSameExtensions(extensionIdentifierWithVersion, extensionIdentifier))?.version + ?? ((isBoolean(criteria.includePreRelease) ? criteria.includePreRelease : !!criteria.includePreRelease.find(extensionIdentifierWithPreRelease => areSameExtensions(extensionIdentifierWithPreRelease, extensionIdentifier))?.includePreRelease) ? VersionKind.Latest : VersionKind.Release); + const rawGalleryExtensionVersion = await this.getRawGalleryExtensionVersion( + rawGalleryExtension, + { + compatible: criteria.compatible, + targetPlatform: criteria.targetPlatform, + productVersion: criteria.productVersion, + version + }, + allTargetPlatforms + ); + if (rawGalleryExtensionVersion) { + extensions.push(toExtension(rawGalleryExtension, rawGalleryExtensionVersion, allTargetPlatforms, extensionGalleryManifest, context)); + } + } + + return { extensions, total }; + } + private async getRawGalleryExtensionVersion(rawGalleryExtension: IRawGalleryExtension, criteria: ExtensionVersionCriteria, allTargetPlatforms: TargetPlatform[]): Promise { const extensionIdentifier = { id: getGalleryExtensionId(rawGalleryExtension.publisher.publisherName, rawGalleryExtension.extensionName), uuid: rawGalleryExtension.extensionId }; const rawGalleryExtensionVersions = sortExtensionVersions(rawGalleryExtension.versions, criteria.targetPlatform); @@ -1156,20 +1257,54 @@ abstract class AbstractExtensionGalleryService implements IExtensionGalleryServi return rawGalleryExtension.versions[0]; } - private async queryRawGalleryExtensions(query: Query, token: CancellationToken): Promise { - if (!this.isEnabled()) { - throw new Error('No extension gallery service configured.'); + private async queryRawGalleryExtensions(query: Query, extensionGalleryManifest: IExtensionGalleryManifest, token: CancellationToken): Promise { + const extensionsQueryApi = getExtensionGalleryManifestResourceUri(extensionGalleryManifest, ExtensionGalleryResourceType.ExtensionQueryService); + + if (!extensionsQueryApi) { + throw new Error('No extension gallery query service configured.'); } query = query /* Always exclude non validated extensions */ - .withFlags(query.flags, Flags.ExcludeNonValidated) - .withFilter(FilterType.Target, 'Microsoft.VisualStudio.Code') - /* Always exclude unpublished extensions */ - .withFilter(FilterType.ExcludeWithFlags, flagsToString(Flags.Unpublished)); + .withFlags(...query.flags, Flag.ExcludeNonValidated) + .withFilter(FilterType.Target, 'Microsoft.VisualStudio.Code'); + + const unpublishedFlag = extensionGalleryManifest.capabilities.extensionQuery.flags?.find(f => f.name === Flag.Unpublished); + /* Always exclude unpublished extensions */ + if (unpublishedFlag) { + query = query.withFilter(FilterType.ExcludeWithFlags, String(unpublishedFlag.value)); + } + + const data = JSON.stringify({ + filters: [ + { + criteria: query.criteria.reduce<{ filterType: number; value?: string }[]>((criteria, c) => { + const criterium = extensionGalleryManifest.capabilities.extensionQuery.filtering?.find(f => f.name === c.filterType); + if (criterium) { + criteria.push({ + filterType: criterium.value, + value: c.value, + }); + } + return criteria; + }, []), + pageNumber: query.pageNumber, + pageSize: query.pageSize, + sortBy: extensionGalleryManifest.capabilities.extensionQuery.sorting?.find(s => s.name === query.sortBy)?.value, + sortOrder: query.sortOrder, + } + ], + assetTypes: query.assetTypes, + flags: query.flags.reduce((flags, flag) => { + const flagValue = extensionGalleryManifest.capabilities.extensionQuery.flags?.find(f => f.name === flag); + if (flagValue) { + flags |= flagValue.value; + } + return flags; + }, 0) + }); const commonHeaders = await this.commonHeadersPromise; - const data = JSON.stringify(query.raw); const headers = { ...commonHeaders, 'Content-Type': 'application/json', @@ -1184,7 +1319,7 @@ abstract class AbstractExtensionGalleryService implements IExtensionGalleryServi try { context = await this.requestService.request({ type: 'POST', - url: this.api('/extensionquery'), + url: extensionsQueryApi, data, headers }, token); @@ -1225,7 +1360,13 @@ abstract class AbstractExtensionGalleryService implements IExtensionGalleryServi } } finally { this.telemetryService.publicLog2('galleryService:query', { - ...query.telemetryData, + filterTypes: query.criteria.map(criterium => criterium.filterType), + flags: query.flags, + sortBy: query.sortBy, + sortOrder: String(query.sortOrder), + pageNumber: String(query.pageNumber), + source: query.source, + searchTextLength: query.searchText.length, requestBodySize: String(data.length), duration: stopWatch.elapsed(), success: !!context && isSuccess(context), @@ -1237,13 +1378,11 @@ abstract class AbstractExtensionGalleryService implements IExtensionGalleryServi } } - private async getLatestRawGalleryExtension(extensionId: string, token: CancellationToken): Promise { + private async getLatestRawGalleryExtension(extension: string, uri: URI, token: CancellationToken): Promise { let errorCode: string | undefined; const stopWatch = new StopWatch(); try { - const [publisher, name] = extensionId.split('.'); - const uri = URI.parse(format2(this.extensionUrlTemplate!, { publisher, name })); const commonHeaders = await this.commonHeadersPromise; const headers = { ...commonHeaders, @@ -1294,27 +1433,44 @@ abstract class AbstractExtensionGalleryService implements IExtensionGalleryServi type GalleryServiceGetLatestEventClassification = { owner: 'sandy081'; comment: 'Report the query to the Marketplace for fetching latest version of an extension'; + host: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The host of the end point' }; extension: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The identifier of the extension' }; duration: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; isMeasurement: true; comment: 'Duration in ms for the query' }; errorCode?: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'The error code in case of error' }; }; type GalleryServiceGetLatestEvent = { extension: string; + host: string; duration: number; errorCode?: string; }; - this.telemetryService.publicLog2('galleryService:getLatest', { extension: extensionId, duration: stopWatch.elapsed(), errorCode }); + this.telemetryService.publicLog2('galleryService:getLatest', { extension, host: uri.authority, duration: stopWatch.elapsed(), errorCode }); } } async reportStatistic(publisher: string, name: string, version: string, type: StatisticType): Promise { - if (!this.isEnabled()) { + const manifest = await this.extensionGalleryManifestService.getExtensionGalleryManifest(); + if (!manifest) { return undefined; } - const url = isWeb ? this.api(`/itemName/${publisher}.${name}/version/${version}/statType/${type === StatisticType.Install ? '1' : '3'}/vscodewebextension`) : this.api(`/publishers/${publisher}/extensions/${name}/${version}/stats?statType=${type}`); - const Accept = isWeb ? 'api-version=6.1-preview.1' : '*/*;api-version=4.0-preview.1'; + let url: string; + + if (isWeb) { + const resource = getExtensionGalleryManifestResourceUri(manifest, ExtensionGalleryResourceType.WebExtensionStatisticsUri); + if (!resource) { + return; + } + url = format2(resource, { publisher, name, version, statTypeValue: type === StatisticType.Install ? '1' : '3' }); + } else { + const resource = getExtensionGalleryManifestResourceUri(manifest, ExtensionGalleryResourceType.ExtensionStatisticsUri); + if (!resource) { + return; + } + url = format2(resource, { publisher, name, version, statTypeName: type }); + } + const Accept = isWeb ? 'api-version=6.1-preview.1' : '*/*;api-version=4.0-preview.1'; const commonHeaders = await this.commonHeadersPromise; const headers = { ...commonHeaders, Accept }; try { @@ -1434,8 +1590,13 @@ abstract class AbstractExtensionGalleryService implements IExtensionGalleryServi } async getAllCompatibleVersions(extensionIdentifier: IExtensionIdentifier, includePreRelease: boolean, targetPlatform: TargetPlatform): Promise { + const extensionGalleryManifest = await this.extensionGalleryManifestService.getExtensionGalleryManifest(); + if (!extensionGalleryManifest) { + throw new Error('No extension gallery service configured.'); + } + let query = new Query() - .withFlags(Flags.IncludeVersions, Flags.IncludeCategoryAndTags, Flags.IncludeFiles, Flags.IncludeVersionProperties) + .withFlags(Flag.IncludeVersions, Flag.IncludeCategoryAndTags, Flag.IncludeFiles, Flag.IncludeVersionProperties) .withPage(1, 1); if (extensionIdentifier.uuid) { @@ -1444,7 +1605,7 @@ abstract class AbstractExtensionGalleryService implements IExtensionGalleryServi query = query.withFilter(FilterType.ExtensionName, extensionIdentifier.id); } - const { galleryExtensions } = await this.queryRawGalleryExtensions(query, CancellationToken.None); + const { galleryExtensions } = await this.queryRawGalleryExtensions(query, extensionGalleryManifest, CancellationToken.None); if (!galleryExtensions.length) { return []; } @@ -1624,6 +1785,7 @@ abstract class AbstractExtensionGalleryService implements IExtensionGalleryServi return { malicious, deprecated, search, extensionsEnabledWithPreRelease }; } + } export class ExtensionGalleryService extends AbstractExtensionGalleryService { @@ -1638,8 +1800,9 @@ export class ExtensionGalleryService extends AbstractExtensionGalleryService { @IProductService productService: IProductService, @IConfigurationService configurationService: IConfigurationService, @IAllowedExtensionsService allowedExtensionsService: IAllowedExtensionsService, + @IExtensionGalleryManifestService extensionGalleryManifestService: IExtensionGalleryManifestService, ) { - super(storageService, requestService, logService, environmentService, telemetryService, fileService, productService, configurationService, allowedExtensionsService); + super(storageService, undefined, requestService, logService, environmentService, telemetryService, fileService, productService, configurationService, allowedExtensionsService, extensionGalleryManifestService); } } @@ -1654,7 +1817,8 @@ export class ExtensionGalleryServiceWithNoStorageService extends AbstractExtensi @IProductService productService: IProductService, @IConfigurationService configurationService: IConfigurationService, @IAllowedExtensionsService allowedExtensionsService: IAllowedExtensionsService, + @IExtensionGalleryManifestService extensionGalleryManifestService: IExtensionGalleryManifestService, ) { - super(undefined, requestService, logService, environmentService, telemetryService, fileService, productService, configurationService, allowedExtensionsService); + super(undefined, undefined, requestService, logService, environmentService, telemetryService, fileService, productService, configurationService, allowedExtensionsService, extensionGalleryManifestService); } } diff --git a/src/vs/platform/extensionManagement/common/extensionManagement.ts b/src/vs/platform/extensionManagement/common/extensionManagement.ts index cd1dd853677b..ab30c7ca90c2 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagement.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagement.ts @@ -218,6 +218,7 @@ export interface IGalleryExtension { publisher: string; publisherDisplayName: string; publisherDomain?: { link: string; verified: boolean }; + publisherLink?: string; publisherSponsorLink?: string; description: string; installCount: number; @@ -228,15 +229,18 @@ export interface IGalleryExtension { releaseDate: number; lastUpdated: number; preview: boolean; + private: boolean; hasPreReleaseVersion: boolean; hasReleaseVersion: boolean; isSigned: boolean; allTargetPlatforms: TargetPlatform[]; assets: IGalleryExtensionAssets; properties: IGalleryExtensionProperties; + detailsLink?: string; + ratingLink?: string; + supportLink?: string; telemetryData?: any; queryContext?: IStringDictionary; - supportLink?: string; } export type InstallSource = 'gallery' | 'vsix' | 'resource'; @@ -244,6 +248,7 @@ export type InstallSource = 'gallery' | 'vsix' | 'resource'; export interface IGalleryMetadata { id: string; publisherId: string; + private: boolean; publisherDisplayName: string; isPreReleaseVersion: boolean; targetPlatform?: TargetPlatform; @@ -271,6 +276,7 @@ export interface ILocalExtension extends IExtension { installedTimestamp?: number; isPreReleaseVersion: boolean; hasPreReleaseVersion: boolean; + private: boolean; preRelease: boolean; updated: boolean; pinned: boolean; @@ -279,14 +285,14 @@ export interface ILocalExtension extends IExtension { } export const enum SortBy { - NoneOrRelevance = 0, - LastUpdatedDate = 1, - Title = 2, - PublisherName = 3, - InstallCount = 4, - PublishedDate = 10, - AverageRating = 6, - WeightedRating = 12 + NoneOrRelevance = 'NoneOrRelevance', + LastUpdatedDate = 'LastUpdatedDate', + Title = 'Title', + PublisherName = 'PublisherName', + InstallCount = 'InstallCount', + PublishedDate = 'PublishedDate', + AverageRating = 'AverageRating', + WeightedRating = 'WeightedRating' } export const enum SortOrder { @@ -295,6 +301,17 @@ export const enum SortOrder { Descending = 2 } +export const enum FilterType { + Category = 'Category', + ExtensionId = 'ExtensionId', + ExtensionName = 'ExtensionName', + ExcludeWithFlags = 'ExcludeWithFlags', + Featured = 'Featured', + SearchText = 'SearchText', + Tag = 'Tag', + Target = 'Target', +} + export interface IQueryOptions { text?: string; exclude?: string[]; @@ -361,6 +378,14 @@ export interface IExtensionQueryOptions { preferResourceApi?: boolean; } +export interface IExtensionGalleryCapabilities { + readonly query: { + readonly sortBy: readonly SortBy[]; + readonly filters: readonly FilterType[]; + }; + readonly allRepositorySigned: boolean; +} + export const IExtensionGalleryService = createDecorator('extensionGalleryService'); /** @@ -534,9 +559,9 @@ export type InstallOptions = { donotVerifySignature?: boolean; operation?: InstallOperation; profileLocation?: URI; - installOnlyNewlyAddedFromExtensionPack?: boolean; productVersion?: IProductVersion; keepExisting?: boolean; + downloadExtensionsLocally?: boolean; /** * Context passed through to InstallExtensionResult */ diff --git a/src/vs/platform/extensionManagement/common/extensionManagementUtil.ts b/src/vs/platform/extensionManagement/common/extensionManagementUtil.ts index 7a2cd54e8654..ece6b6241632 100644 --- a/src/vs/platform/extensionManagement/common/extensionManagementUtil.ts +++ b/src/vs/platform/extensionManagement/common/extensionManagementUtil.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { compareIgnoreCase } from '../../../base/common/strings.js'; -import { IExtensionIdentifier, IExtensionsControlManifest, IGalleryExtension, ILocalExtension, getTargetPlatform } from './extensionManagement.js'; +import { IExtensionIdentifier, IGalleryExtension, ILocalExtension, getTargetPlatform } from './extensionManagement.js'; import { ExtensionIdentifier, IExtension, TargetPlatform, UNDEFINED_PUBLISHER } from '../../extensions/common/extensions.js'; import { IFileService } from '../../files/common/files.js'; import { isLinux, platform } from '../../../base/common/platform.js'; @@ -198,8 +198,8 @@ export async function computeTargetPlatform(fileService: IFileService, logServic return targetPlatform; } -export function isMalicious(identifier: IExtensionIdentifier, controlManifest: IExtensionsControlManifest): boolean { - return controlManifest.malicious.some(publisherOrIdentifier => { +export function isMalicious(identifier: IExtensionIdentifier, malicious: ReadonlyArray): boolean { + return malicious.some(publisherOrIdentifier => { if (isString(publisherOrIdentifier)) { return compareIgnoreCase(identifier.id.split('.')[0], publisherOrIdentifier) === 0; } diff --git a/src/vs/platform/extensionManagement/common/extensionsProfileScannerService.ts b/src/vs/platform/extensionManagement/common/extensionsProfileScannerService.ts index c0eefb920636..16ba453fd05b 100644 --- a/src/vs/platform/extensionManagement/common/extensionsProfileScannerService.ts +++ b/src/vs/platform/extensionManagement/common/extensionsProfileScannerService.ts @@ -270,8 +270,9 @@ export abstract class AbstractExtensionsProfileScannerService extends Disposable migrate = true; e.metadata.hasPreReleaseVersion = true; } + const uuid = e.metadata?.id ?? e.identifier.uuid; extensions.push({ - identifier: e.identifier, + identifier: uuid ? { id: e.identifier.id, uuid } : { id: e.identifier.id }, location, version: e.version, metadata: e.metadata, diff --git a/src/vs/platform/extensionManagement/common/extensionsScannerService.ts b/src/vs/platform/extensionManagement/common/extensionsScannerService.ts index c613e0669b4e..ddcacaf37b2d 100644 --- a/src/vs/platform/extensionManagement/common/extensionsScannerService.ts +++ b/src/vs/platform/extensionManagement/common/extensionsScannerService.ts @@ -35,7 +35,12 @@ import { IUserDataProfile, IUserDataProfilesService } from '../../userDataProfil import { IUriIdentityService } from '../../uriIdentity/common/uriIdentity.js'; import { localizeManifest } from './extensionNls.js'; -export type IScannedExtensionManifest = IRelaxedExtensionManifest & { __metadata?: Metadata }; +export type ManifestMetadata = Partial<{ + installedTimestamp: number; + size: number; +}>; + +export type IScannedExtensionManifest = IRelaxedExtensionManifest & { __metadata?: ManifestMetadata }; interface IRelaxedScannedExtension { type: ExtensionType; @@ -136,7 +141,7 @@ export interface IExtensionsScannerService { scanMultipleExtensions(extensionLocations: URI[], extensionType: ExtensionType, scanOptions: ScanOptions): Promise; scanOneOrMultipleExtensions(extensionLocation: URI, extensionType: ExtensionType, scanOptions: ScanOptions): Promise; - updateMetadata(extensionLocation: URI, metadata: Partial): Promise; + updateManifestMetadata(extensionLocation: URI, metadata: ManifestMetadata): Promise; initializeDefaultProfileExtensions(): Promise; } @@ -270,18 +275,10 @@ export abstract class AbstractExtensionsScannerService extends Disposable implem return this.applyScanOptions(extensions, extensionType, { includeInvalid: scanOptions.includeInvalid, pickLatest: true }); } - async updateMetadata(extensionLocation: URI, metaData: Partial): Promise { + async updateManifestMetadata(extensionLocation: URI, metaData: ManifestMetadata): Promise { const manifestLocation = joinPath(extensionLocation, 'package.json'); const content = (await this.fileService.readFile(manifestLocation)).value.toString(); const manifest: IScannedExtensionManifest = JSON.parse(content); - - // unset if false - if (metaData.isMachineScoped === false) { - delete metaData.isMachineScoped; - } - if (metaData.isBuiltin === false) { - delete metaData.isBuiltin; - } manifest.__metadata = { ...manifest.__metadata, ...metaData }; await this.fileService.writeFile(joinPath(extensionLocation, 'package.json'), VSBuffer.fromString(JSON.stringify(manifest, null, '\t'))); @@ -665,10 +662,20 @@ class ExtensionsScanner extends Disposable { if (!manifest.publisher) { manifest.publisher = UNDEFINED_PUBLISHER; } - const metadata = scannedProfileExtension?.metadata ?? manifest.__metadata; - if (metadata && !metadata?.size && manifest.__metadata?.size) { - metadata.size = manifest.__metadata?.size; + + let metadata: Metadata | undefined; + if (scannedProfileExtension) { + metadata = { + ...scannedProfileExtension.metadata, + size: manifest.__metadata?.size, + }; + } else if (manifest.__metadata) { + metadata = { + installedTimestamp: manifest.__metadata.installedTimestamp, + size: manifest.__metadata.size, + }; } + delete manifest.__metadata; const id = getGalleryExtensionId(manifest.publisher, manifest.name); const identifier = metadata?.id ? { id, uuid: metadata.id } : { id }; diff --git a/src/vs/platform/extensionManagement/common/unsupportedExtensionsMigration.ts b/src/vs/platform/extensionManagement/common/unsupportedExtensionsMigration.ts index 1fea9a2abf81..de38da03b0a6 100644 --- a/src/vs/platform/extensionManagement/common/unsupportedExtensionsMigration.ts +++ b/src/vs/platform/extensionManagement/common/unsupportedExtensionsMigration.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { CancellationToken } from '../../../base/common/cancellation.js'; -import { IExtensionGalleryService, IExtensionManagementService, IGlobalExtensionEnablementService, InstallOperation } from './extensionManagement.js'; +import { EXTENSION_INSTALL_SKIP_PUBLISHER_TRUST_CONTEXT, IExtensionGalleryService, IExtensionManagementService, IGlobalExtensionEnablementService, InstallOperation } from './extensionManagement.js'; import { areSameExtensions, getExtensionId } from './extensionManagementUtil.js'; import { IExtensionStorageService } from './extensionStorage.js'; import { ExtensionType } from '../../extensions/common/extensions.js'; @@ -53,7 +53,7 @@ export async function migrateUnsupportedExtensions(extensionManagementService: I let preReleaseExtension = installed.find(i => areSameExtensions(i.identifier, { id: preReleaseExtensionId })); if (!preReleaseExtension || (!preReleaseExtension.isPreReleaseVersion && isUnsupportedExtensionEnabled)) { - preReleaseExtension = await extensionManagementService.installFromGallery(gallery, { installPreReleaseVersion: true, isMachineScoped: unsupportedExtension.isMachineScoped, operation: InstallOperation.Migrate }); + preReleaseExtension = await extensionManagementService.installFromGallery(gallery, { installPreReleaseVersion: true, isMachineScoped: unsupportedExtension.isMachineScoped, operation: InstallOperation.Migrate, context: { [EXTENSION_INSTALL_SKIP_PUBLISHER_TRUST_CONTEXT]: true } }); logService.info(`Installed the pre-release extension '${preReleaseExtension.identifier.id}'`); if (!isUnsupportedExtensionEnabled) { await extensionEnablementService.disableExtension(preReleaseExtension.identifier); diff --git a/src/vs/platform/extensionManagement/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index 05182a5c8b94..8d137c47d4e7 100644 --- a/src/vs/platform/extensionManagement/node/extensionManagementService.ts +++ b/src/vs/platform/extensionManagement/node/extensionManagementService.ts @@ -37,7 +37,7 @@ import { } from '../common/extensionManagement.js'; import { areSameExtensions, computeTargetPlatform, ExtensionKey, getGalleryExtensionId, groupByExtension } from '../common/extensionManagementUtil.js'; import { IExtensionsProfileScannerService, IScannedProfileExtension } from '../common/extensionsProfileScannerService.js'; -import { IExtensionsScannerService, IScannedExtension, UserExtensionsScanOptions } from '../common/extensionsScannerService.js'; +import { IExtensionsScannerService, IScannedExtension, ManifestMetadata, UserExtensionsScanOptions } from '../common/extensionsScannerService.js'; import { ExtensionsDownloader } from './extensionDownloader.js'; import { ExtensionsLifecycle } from './extensionLifecycle.js'; import { fromExtractError, getManifest } from './extensionManagementUtil.js'; @@ -53,6 +53,8 @@ import { ITelemetryService } from '../../telemetry/common/telemetry.js'; import { IUriIdentityService } from '../../uriIdentity/common/uriIdentity.js'; import { IUserDataProfilesService } from '../../userDataProfile/common/userDataProfile.js'; import { IConfigurationService } from '../../configuration/common/configuration.js'; +import { isLinux } from '../../../base/common/platform.js'; +import { IExtensionGalleryManifestService } from '../common/extensionGalleryManifest.js'; export const INativeServerExtensionManagementService = refineServiceDecorator(IExtensionManagementService); export interface INativeServerExtensionManagementService extends IExtensionManagementService { @@ -85,6 +87,7 @@ export class ExtensionManagementService extends AbstractExtensionManagementServi @IInstantiationService private readonly instantiationService: IInstantiationService, @IFileService private readonly fileService: IFileService, @IConfigurationService private readonly configurationService: IConfigurationService, + @IExtensionGalleryManifestService protected readonly extensionGalleryManifestService: IExtensionGalleryManifestService, @IProductService productService: IProductService, @IAllowedExtensionsService allowedExtensionsService: IAllowedExtensionsService, @IUriIdentityService uriIdentityService: IUriIdentityService, @@ -213,6 +216,10 @@ export class ExtensionManagementService extends AbstractExtensionManagementServi return local; } + protected removeExtension(extension: ILocalExtension): Promise { + return this.extensionsScanner.deleteExtension(extension, 'remove'); + } + protected copyExtension(extension: ILocalExtension, fromProfileLocation: URI, toProfileLocation: URI, metadata: Partial): Promise { return this.extensionsScanner.copyExtension(extension, fromProfileLocation, toProfileLocation, metadata); } @@ -327,8 +334,15 @@ export class ExtensionManagementService extends AbstractExtensionManagementServi verifySignature = isBoolean(value) ? value : true; } const { location, verificationStatus } = await this.extensionsDownloader.download(extension, operation, verifySignature, clientTargetPlatform); - - if (verificationStatus !== ExtensionSignatureVerificationCode.Success && verificationStatus !== ExtensionSignatureVerificationCode.NotSigned && verifySignature && this.environmentService.isBuilt) { + const shouldRequireSignature = (await this.extensionGalleryManifestService.getExtensionGalleryManifest())?.capabilities.signing?.allRepositorySigned; + + if ( + verificationStatus !== ExtensionSignatureVerificationCode.Success + && !(verificationStatus === ExtensionSignatureVerificationCode.NotSigned && !shouldRequireSignature) + && verifySignature + && this.environmentService.isBuilt + && !(isLinux && this.productService.quality === 'stable') + ) { try { await this.extensionsDownloader.delete(location); } catch (e) { @@ -351,6 +365,7 @@ export class ExtensionManagementService extends AbstractExtensionManagementServi case ExtensionSignatureVerificationCode.CertificateRevoked: case ExtensionSignatureVerificationCode.SignatureIsNotValid: case ExtensionSignatureVerificationCode.SignatureArchiveHasTooManyEntries: + case ExtensionSignatureVerificationCode.NotSigned: throw new ExtensionManagementError(nls.localize('signature verification failed', "Signature verification failed with '{0}' error.", verificationStatus), ExtensionManagementErrorCode.SignatureVerificationFailed); } @@ -623,7 +638,7 @@ export class ExtensionsScanner extends Disposable { throw fromExtractError(e); } - const metadata: Metadata = { installedTimestamp: Date.now() }; + const metadata: ManifestMetadata = { installedTimestamp: Date.now() }; try { metadata.size = await computeSize(tempLocation, this.fileService); } catch (error) { @@ -632,7 +647,7 @@ export class ExtensionsScanner extends Disposable { } try { - await this.extensionsScannerService.updateMetadata(tempLocation, metadata); + await this.extensionsScannerService.updateManifestMetadata(tempLocation, metadata); } catch (error) { this.telemetryService.publicLog2('extension:extract', { extensionId: extensionKey.id, code: `${toFileOperationResult(error)}` }); throw toExtensionManagementError(error, ExtensionManagementErrorCode.UpdateMetadata); @@ -677,13 +692,9 @@ export class ExtensionsScanner extends Disposable { return extensions.find(e => areSameExtensions(e.identifier, local.identifier)); } - async updateMetadata(local: ILocalExtension, metadata: Partial, profileLocation?: URI): Promise { + async updateMetadata(local: ILocalExtension, metadata: Partial, profileLocation: URI): Promise { try { - if (profileLocation) { - await this.extensionsProfileScannerService.updateMetadata([[local, metadata]], profileLocation); - } else { - await this.extensionsScannerService.updateMetadata(local.location, metadata); - } + await this.extensionsProfileScannerService.updateMetadata([[local, metadata]], profileLocation); } catch (error) { this.telemetryService.publicLog2('extension:extract', { extensionId: local.identifier.id, code: `${toFileOperationResult(error)}`, isProfile: !!profileLocation }); throw toExtensionManagementError(error, ExtensionManagementErrorCode.UpdateMetadata); @@ -860,6 +871,7 @@ export class ExtensionsScanner extends Disposable { installedTimestamp: extension.metadata?.installedTimestamp, updated: !!extension.metadata?.updated, pinned: !!extension.metadata?.pinned, + private: !!extension.metadata?.private, isWorkspaceScoped: false, source: extension.metadata?.source ?? (extension.identifier.uuid ? 'gallery' : 'vsix'), size: extension.metadata?.size ?? 0, @@ -872,7 +884,7 @@ export class ExtensionsScanner extends Disposable { // set size if not set before if (isDefined(extension.metadata?.installedTimestamp) && isUndefined(extension.metadata?.size)) { const size = await computeSize(extension.location, this.fileService); - await this.extensionsScannerService.updateMetadata(extension.location, { size }); + await this.extensionsScannerService.updateManifestMetadata(extension.location, { size }); } })); } @@ -1032,6 +1044,7 @@ class InstallExtensionInProfileTask extends AbstractExtensionTask { assert.ok(target4.notCalled); }); + test('read extension when uuid is different in identifier and manifest', async () => { + const extensionsManifest = joinPath(extensionsLocation, 'extensions.json'); + await instantiationService.get(IFileService).writeFile(extensionsManifest, VSBuffer.fromString(JSON.stringify([{ + identifier: { + id: 'pub.a', + uuid: 'uuid1`' + }, + version: '1.0.0', + location: joinPath(extensionsLocation, 'pub.a-1.0.0').toString(), + relativeLocation: 'pub.a-1.0.0', + metadata: { + id: 'uuid', + } + }]))); + + const testObject = disposables.add(instantiationService.createInstance(TestObject, extensionsLocation)); + const actual = await testObject.scanProfileExtensions(extensionsManifest); + assert.deepStrictEqual(actual.length, 1); + assert.deepStrictEqual(actual[0].identifier.id, 'pub.a'); + assert.deepStrictEqual(actual[0].identifier.uuid, 'uuid'); + }); + function aExtension(id: string, location: URI, e?: Partial, manifest?: Partial): IExtension { return { identifier: { id }, diff --git a/src/vs/platform/extensionManagement/test/node/extensionsScannerService.test.ts b/src/vs/platform/extensionManagement/test/node/extensionsScannerService.test.ts index 1d349f5822ab..ea0a4661df03 100644 --- a/src/vs/platform/extensionManagement/test/node/extensionsScannerService.test.ts +++ b/src/vs/platform/extensionManagement/test/node/extensionsScannerService.test.ts @@ -105,20 +105,20 @@ suite('NativeExtensionsScanerService Test', () => { }); test('scan user extensions', async () => { - const manifest: Partial = anExtensionManifest({ 'name': 'name', 'publisher': 'pub', __metadata: { id: 'uuid' } }); + const manifest: Partial = anExtensionManifest({ 'name': 'name', 'publisher': 'pub' }); const extensionLocation = await aUserExtension(manifest); const testObject: IExtensionsScannerService = disposables.add(instantiationService.createInstance(ExtensionsScannerService)); const actual = await testObject.scanAllUserExtensions(); assert.deepStrictEqual(actual.length, 1); - assert.deepStrictEqual(actual[0].identifier, { id: 'pub.name', uuid: 'uuid' }); + assert.deepStrictEqual(actual[0].identifier, { id: 'pub.name' }); assert.deepStrictEqual(actual[0].location.toString(), extensionLocation.toString()); assert.deepStrictEqual(actual[0].isBuiltin, false); assert.deepStrictEqual(actual[0].type, ExtensionType.User); assert.deepStrictEqual(actual[0].isValid, true); assert.deepStrictEqual(actual[0].validations, []); - assert.deepStrictEqual(actual[0].metadata, { id: 'uuid' }); + assert.deepStrictEqual(actual[0].metadata, undefined); assert.deepStrictEqual(actual[0].targetPlatform, TargetPlatform.UNDEFINED); delete manifest.__metadata; assert.deepStrictEqual(actual[0].manifest, manifest); @@ -298,6 +298,27 @@ suite('NativeExtensionsScanerService Test', () => { assert.deepStrictEqual(actual!.manifest.displayName, 'Hello World'); }); + test('scan single extension with manifest metadata retains manifest metadata', async () => { + const manifest: Partial = anExtensionManifest({ 'name': 'name', 'publisher': 'pub' }); + const extensionLocation = await aUserExtension({ + ...manifest, + __metadata: { size: 12345, installedTimestamp: 1234567890 } + }); + const testObject: IExtensionsScannerService = disposables.add(instantiationService.createInstance(ExtensionsScannerService)); + + const actual = await testObject.scanExistingExtension(extensionLocation, ExtensionType.User, {}); + + assert.notStrictEqual(actual, null); + assert.deepStrictEqual(actual!.identifier, { id: 'pub.name' }); + assert.deepStrictEqual(actual!.location.toString(), extensionLocation.toString()); + assert.deepStrictEqual(actual!.isBuiltin, false); + assert.deepStrictEqual(actual!.type, ExtensionType.User); + assert.deepStrictEqual(actual!.isValid, true); + assert.deepStrictEqual(actual!.validations, []); + assert.deepStrictEqual(actual!.metadata, { size: 12345, installedTimestamp: 1234567890 }); + assert.deepStrictEqual(actual!.manifest, manifest); + }); + async function aUserExtension(manifest: Partial): Promise { const environmentService = instantiationService.get(INativeEnvironmentService); return anExtension(manifest, URI.file(environmentService.extensionsPath)); @@ -310,7 +331,7 @@ suite('NativeExtensionsScanerService Test', () => { async function anExtension(manifest: Partial, root: URI): Promise { const fileService = instantiationService.get(IFileService); - const extensionLocation = joinPath(root, `${manifest.publisher}.${manifest.name}-${manifest.version}-${manifest.__metadata?.targetPlatform ?? TargetPlatform.UNDEFINED}`); + const extensionLocation = joinPath(root, `${manifest.publisher}.${manifest.name}-${manifest.version}`); await fileService.writeFile(joinPath(extensionLocation, 'package.json'), VSBuffer.fromString(JSON.stringify(manifest))); return extensionLocation; } diff --git a/src/vs/platform/extensions/common/extensions.ts b/src/vs/platform/extensions/common/extensions.ts index b2d6194419a1..b38ba82a1d9a 100644 --- a/src/vs/platform/extensions/common/extensions.ts +++ b/src/vs/platform/extensions/common/extensions.ts @@ -184,6 +184,11 @@ export interface IToolContribution { userDescription?: string; } +export interface IMcpCollectionContribution { + readonly id: string; + readonly label: string; +} + export interface IExtensionContributions { commands?: ICommand[]; configuration?: any; @@ -211,6 +216,7 @@ export interface IExtensionContributions { readonly debugVisualizers?: IDebugVisualizationContribution[]; readonly chatParticipants?: ReadonlyArray; readonly languageModelTools?: ReadonlyArray; + readonly modelContextServerCollections?: ReadonlyArray; } export interface IExtensionCapabilities { diff --git a/src/vs/platform/extensions/common/extensionsApiProposals.ts b/src/vs/platform/extensions/common/extensionsApiProposals.ts index 96770454dac8..18efd7a0934d 100644 --- a/src/vs/platform/extensions/common/extensionsApiProposals.ts +++ b/src/vs/platform/extensions/common/extensionsApiProposals.ts @@ -33,17 +33,17 @@ const _allApiProposals = { }, chatParticipantPrivate: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatParticipantPrivate.d.ts', - version: 3 + version: 6 }, chatProvider: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatProvider.d.ts', }, - chatReadonlyPromptReference: { - proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatReadonlyPromptReference.d.ts', - }, chatReferenceBinaryData: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatReferenceBinaryData.d.ts', }, + chatReferenceDiagnostic: { + proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatReferenceDiagnostic.d.ts', + }, chatTab: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatTab.d.ts', }, @@ -151,7 +151,7 @@ const _allApiProposals = { }, defaultChatParticipant: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.defaultChatParticipant.d.ts', - version: 2 + version: 3 }, diffCommand: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.diffCommand.d.ts', @@ -226,12 +226,18 @@ const _allApiProposals = { languageModelSystem: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.languageModelSystem.d.ts', }, + languageModelToolsForAgent: { + proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.languageModelToolsForAgent.d.ts', + }, languageStatusText: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.languageStatusText.d.ts', }, mappedEditsProvider: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.mappedEditsProvider.d.ts', }, + mcpConfigurationProvider: { + proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.mcpConfigurationProvider.d.ts', + }, multiDocumentHighlightProvider: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.multiDocumentHighlightProvider.d.ts', }, @@ -331,6 +337,9 @@ const _allApiProposals = { taskPresentationGroup: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.taskPresentationGroup.d.ts', }, + taskStatus: { + proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.taskStatus.d.ts', + }, telemetry: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.telemetry.d.ts', }, @@ -355,6 +364,9 @@ const _allApiProposals = { terminalShellEnv: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.terminalShellEnv.d.ts', }, + terminalShellKind: { + proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.terminalShellKind.d.ts', + }, terminalShellType: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.terminalShellType.d.ts', }, diff --git a/src/vs/platform/files/common/fileService.ts b/src/vs/platform/files/common/fileService.ts index 978cac73b675..33666b18f45b 100644 --- a/src/vs/platform/files/common/fileService.ts +++ b/src/vs/platform/files/common/fileService.ts @@ -379,8 +379,8 @@ export class FileService extends Disposable implements IFileService { try { - // validate write - const stat = await this.validateWriteFile(provider, resource, writeFileOptions); + // validate write (this may already return a peeked-at buffer) + let { stat, buffer: bufferOrReadableOrStreamOrBufferedStream } = await this.validateWriteFile(provider, resource, bufferOrReadableOrStream, writeFileOptions); // mkdir recursively as needed if (!stat) { @@ -391,20 +391,8 @@ export class FileService extends Disposable implements IFileService { // to write is not a buffer, we consume up to 3 chunks and try to write the data // unbuffered to reduce the overhead. If the stream or readable has more data // to provide we continue to write buffered. - let bufferOrReadableOrStreamOrBufferedStream: VSBuffer | VSBufferReadable | VSBufferReadableStream | VSBufferReadableBufferedStream; - if (hasReadWriteCapability(provider) && !(bufferOrReadableOrStream instanceof VSBuffer)) { - if (isReadableStream(bufferOrReadableOrStream)) { - const bufferedStream = await peekStream(bufferOrReadableOrStream, 3); - if (bufferedStream.ended) { - bufferOrReadableOrStreamOrBufferedStream = VSBuffer.concat(bufferedStream.buffer); - } else { - bufferOrReadableOrStreamOrBufferedStream = bufferedStream; - } - } else { - bufferOrReadableOrStreamOrBufferedStream = peekReadable(bufferOrReadableOrStream, data => VSBuffer.concat(data), 3); - } - } else { - bufferOrReadableOrStreamOrBufferedStream = bufferOrReadableOrStream; + if (!bufferOrReadableOrStreamOrBufferedStream) { + bufferOrReadableOrStreamOrBufferedStream = await this.peekBufferForWriting(provider, bufferOrReadableOrStream); } // write file: unbuffered @@ -430,7 +418,28 @@ export class FileService extends Disposable implements IFileService { return this.resolve(resource, { resolveMetadata: true }); } - private async validateWriteFile(provider: IFileSystemProvider, resource: URI, options?: IWriteFileOptions): Promise { + + private async peekBufferForWriting(provider: IFileSystemProviderWithFileReadWriteCapability | IFileSystemProviderWithOpenReadWriteCloseCapability, bufferOrReadableOrStream: VSBuffer | VSBufferReadable | VSBufferReadableStream): Promise { + let peekResult: VSBuffer | VSBufferReadable | VSBufferReadableStream | VSBufferReadableBufferedStream; + if (hasReadWriteCapability(provider) && !(bufferOrReadableOrStream instanceof VSBuffer)) { + if (isReadableStream(bufferOrReadableOrStream)) { + const bufferedStream = await peekStream(bufferOrReadableOrStream, 3); + if (bufferedStream.ended) { + peekResult = VSBuffer.concat(bufferedStream.buffer); + } else { + peekResult = bufferedStream; + } + } else { + peekResult = peekReadable(bufferOrReadableOrStream, data => VSBuffer.concat(data), 3); + } + } else { + peekResult = bufferOrReadableOrStream; + } + + return peekResult; + } + + private async validateWriteFile(provider: IFileSystemProviderWithFileReadWriteCapability | IFileSystemProviderWithOpenReadWriteCloseCapability, resource: URI, bufferOrReadableOrStream: VSBuffer | VSBufferReadable | VSBufferReadableStream, options?: IWriteFileOptions): Promise<{ stat: IStat | undefined; buffer: VSBuffer | VSBufferReadable | VSBufferReadableStream | VSBufferReadableBufferedStream | undefined }> { // Validate unlock support const unlock = !!options?.unlock; @@ -459,7 +468,7 @@ export class FileService extends Disposable implements IFileService { try { stat = await provider.stat(resource); } catch (error) { - return undefined; // file might not exist + return Object.create(null); // file might not exist } // File cannot be directory @@ -482,15 +491,32 @@ export class FileService extends Disposable implements IFileService { // check for size is a weaker check because it can return a false negative if the file has changed // but to the same length. This is a compromise we take to avoid having to produce checksums of // the file content for comparison which would be much slower to compute. + // + // Third, if the etag() turns out to be different, we do one attempt to compare the buffer we + // are about to write with the contents on disk to figure out if the contents are identical. + // In that case we allow the writing as it would result in the same contents in the file. + let buffer: VSBuffer | VSBufferReadable | VSBufferReadableStream | VSBufferReadableBufferedStream | undefined; if ( typeof options?.mtime === 'number' && typeof options.etag === 'string' && options.etag !== ETAG_DISABLED && typeof stat.mtime === 'number' && typeof stat.size === 'number' && options.mtime < stat.mtime && options.etag !== etag({ mtime: options.mtime /* not using stat.mtime for a reason, see above */, size: stat.size }) ) { + buffer = await this.peekBufferForWriting(provider, bufferOrReadableOrStream); + if (buffer instanceof VSBuffer && buffer.byteLength === stat.size) { + try { + const { value } = await this.readFile(resource, { limits: { size: stat.size } }); + if (buffer.equals(value)) { + return { stat, buffer }; // allow writing since contents are identical + } + } catch (error) { + // ignore, throw the FILE_MODIFIED_SINCE error + } + } + throw new FileOperationError(localize('fileModifiedError', "File Modified Since"), FileOperationResult.FILE_MODIFIED_SINCE, options); } - return stat; + return { stat, buffer }; } async readFile(resource: URI, options?: IReadFileOptions, token?: CancellationToken): Promise { diff --git a/src/vs/platform/files/node/diskFileSystemProviderServer.ts b/src/vs/platform/files/node/diskFileSystemProviderServer.ts index d626904949ef..e5a847eb635d 100644 --- a/src/vs/platform/files/node/diskFileSystemProviderServer.ts +++ b/src/vs/platform/files/node/diskFileSystemProviderServer.ts @@ -272,16 +272,18 @@ export abstract class AbstractSessionFileWatcher extends Disposable implements I // This is important because we want to ensure that we only // forward events from the watched paths for this session and // not other clients that asked to watch other paths. - private readonly fileWatcher = this._register(new DiskFileSystemProvider(this.logService)); + private readonly fileWatcher: DiskFileSystemProvider; constructor( private readonly uriTransformer: IURITransformer, sessionEmitter: Emitter, - private readonly logService: ILogService, + logService: ILogService, private readonly environmentService: IEnvironmentService ) { super(); + this.fileWatcher = this._register(new DiskFileSystemProvider(logService)); + this.registerListeners(sessionEmitter); } diff --git a/src/vs/platform/files/node/watcher/nodejs/nodejsWatcherLib.ts b/src/vs/platform/files/node/watcher/nodejs/nodejsWatcherLib.ts index b016dde0a9ff..3d92b0e33615 100644 --- a/src/vs/platform/files/node/watcher/nodejs/nodejsWatcherLib.ts +++ b/src/vs/platform/files/node/watcher/nodejs/nodejsWatcherLib.ts @@ -15,9 +15,10 @@ import { joinPath } from '../../../../../base/common/resources.js'; import { URI } from '../../../../../base/common/uri.js'; import { realpath } from '../../../../../base/node/extpath.js'; import { Promises } from '../../../../../base/node/pfs.js'; -import { FileChangeType, IFileChange } from '../../../common/files.js'; +import { FileChangeFilter, FileChangeType, IFileChange } from '../../../common/files.js'; import { ILogMessage, coalesceEvents, INonRecursiveWatchRequest, parseWatcherPatterns, IRecursiveWatcherWithSubscribe, isFiltered, isWatchRequestWithCorrelation } from '../../../common/watcher.js'; import { Lazy } from '../../../../../base/common/lazy.js'; +import { ParsedPattern } from '../../../../../base/common/glob.js'; export class NodeJSFileWatcherLibrary extends Disposable { @@ -50,9 +51,9 @@ export class NodeJSFileWatcherLibrary extends Disposable { // to coalesce events and reduce spam. private readonly fileChangesAggregator = this._register(new RunOnceWorker(events => this.handleFileChanges(events), NodeJSFileWatcherLibrary.FILE_CHANGES_HANDLER_DELAY)); - private readonly excludes = parseWatcherPatterns(this.request.path, this.request.excludes); - private readonly includes = this.request.includes ? parseWatcherPatterns(this.request.path, this.request.includes) : undefined; - private readonly filter = isWatchRequestWithCorrelation(this.request) ? this.request.filter : undefined; // filtering is only enabled when correlating because watchers are otherwise potentially reused + private readonly excludes: ParsedPattern[]; + private readonly includes: ParsedPattern[] | undefined; + private readonly filter: FileChangeFilter | undefined; private readonly cts = new CancellationTokenSource(); @@ -78,7 +79,7 @@ export class NodeJSFileWatcherLibrary extends Disposable { return result; }); - readonly ready = this.watch(); + readonly ready: Promise; private _isReusingRecursiveWatcher = false; get isReusingRecursiveWatcher(): boolean { return this._isReusingRecursiveWatcher; } @@ -95,6 +96,12 @@ export class NodeJSFileWatcherLibrary extends Disposable { private verboseLogging?: boolean ) { super(); + + this.excludes = parseWatcherPatterns(this.request.path, this.request.excludes); + this.includes = this.request.includes ? parseWatcherPatterns(this.request.path, this.request.includes) : undefined; + this.filter = isWatchRequestWithCorrelation(this.request) ? this.request.filter : undefined; // filtering is only enabled when correlating because watchers are otherwise potentially reused + + this.ready = this.watch(); } private async watch(): Promise { diff --git a/src/vs/platform/files/node/watcher/parcel/parcelWatcher.ts b/src/vs/platform/files/node/watcher/parcel/parcelWatcher.ts index a24ef066a5f7..70a36d3be486 100644 --- a/src/vs/platform/files/node/watcher/parcel/parcelWatcher.ts +++ b/src/vs/platform/files/node/watcher/parcel/parcelWatcher.ts @@ -12,7 +12,7 @@ import { CancellationToken, CancellationTokenSource } from '../../../../../base/ import { toErrorMessage } from '../../../../../base/common/errorMessage.js'; import { Emitter, Event } from '../../../../../base/common/event.js'; import { randomPath, isEqual, isEqualOrParent } from '../../../../../base/common/extpath.js'; -import { GLOBSTAR, patternsEquals } from '../../../../../base/common/glob.js'; +import { GLOBSTAR, ParsedPattern, patternsEquals } from '../../../../../base/common/glob.js'; import { BaseWatcher } from '../baseWatcher.js'; import { TernarySearchTree } from '../../../../../base/common/ternarySearchTree.js'; import { normalizeNFC } from '../../../../../base/common/normalization.js'; @@ -37,8 +37,8 @@ export class ParcelWatcherInstance extends Disposable { private didStop = false; get stopped(): boolean { return this.didStop; } - private readonly includes = this.request.includes ? parseWatcherPatterns(this.request.path, this.request.includes) : undefined; - private readonly excludes = this.request.excludes ? parseWatcherPatterns(this.request.path, this.request.excludes) : undefined; + private readonly includes: ParsedPattern[] | undefined; + private readonly excludes: ParsedPattern[] | undefined; private readonly subscriptions = new Map void>>(); @@ -65,6 +65,9 @@ export class ParcelWatcherInstance extends Disposable { ) { super(); + this.includes = this.request.includes ? parseWatcherPatterns(this.request.path, this.request.includes) : undefined; + this.excludes = this.request.excludes ? parseWatcherPatterns(this.request.path, this.request.excludes) : undefined; + this._register(toDisposable(() => this.subscriptions.clear())); } diff --git a/src/vs/platform/files/test/node/diskFileService.integrationTest.ts b/src/vs/platform/files/test/node/diskFileService.integrationTest.ts index 38d1af9be8f7..555eb780087d 100644 --- a/src/vs/platform/files/test/node/diskFileService.integrationTest.ts +++ b/src/vs/platform/files/test/node/diskFileService.integrationTest.ts @@ -2406,6 +2406,46 @@ flakySuite('Disk File Service', function () { assert.ok(!error); }); + test('writeFile - no error when writing to file where content is the same', async () => { + const resource = URI.file(join(testDir, 'small.txt')); + + await service.resolve(resource); + + const content = readFileSync(resource.fsPath).toString(); + assert.strictEqual(content, 'Small File'); + + const newContent = content; // same content + let error: FileOperationError | undefined = undefined; + try { + await service.writeFile(resource, VSBuffer.fromString(newContent), { etag: 'anything', mtime: 0 } /* fake it */); + } catch (err) { + error = err; + } + + assert.ok(!error); + }); + + test('writeFile - error when writing to file where content is the same length but different', async () => { + const resource = URI.file(join(testDir, 'small.txt')); + + await service.resolve(resource); + + const content = readFileSync(resource.fsPath).toString(); + assert.strictEqual(content, 'Small File'); + + const newContent = content.split('').reverse().join(''); // reverse content + let error: FileOperationError | undefined = undefined; + try { + await service.writeFile(resource, VSBuffer.fromString(newContent), { etag: 'anything', mtime: 0 } /* fake it */); + } catch (err) { + error = err; + } + + assert.ok(error); + assert.ok(error instanceof FileOperationError); + assert.strictEqual(error.fileOperationResult, FileOperationResult.FILE_MODIFIED_SINCE); + }); + test('writeFile - no error when writing to same nonexistent folder multiple times different new files', async () => { const newFolder = URI.file(join(testDir, 'some', 'new', 'folder')); diff --git a/src/vs/platform/hover/browser/hover.ts b/src/vs/platform/hover/browser/hover.ts index 64fe7c21defe..01fcf3cb7a34 100644 --- a/src/vs/platform/hover/browser/hover.ts +++ b/src/vs/platform/hover/browser/hover.ts @@ -9,7 +9,7 @@ import { IHoverDelegate, IHoverDelegateOptions } from '../../../base/browser/ui/ import { IConfigurationService } from '../../configuration/common/configuration.js'; import { addStandardDisposableListener, isHTMLElement } from '../../../base/browser/dom.js'; import { KeyCode } from '../../../base/common/keyCodes.js'; -import type { IHoverDelegate2, IHoverOptions, IHoverWidget } from '../../../base/browser/ui/hover/hover.js'; +import type { IHoverDelegate2, IHoverOptions, IHoverWidget, IManagedHoverContentOrFactory } from '../../../base/browser/ui/hover/hover.js'; export const IHoverService = createDecorator('hoverService'); @@ -17,16 +17,26 @@ export interface IHoverService extends IHoverDelegate2 { readonly _serviceBrand: undefined; } +export interface IHoverDelayOptions { + readonly instantHover?: boolean; + readonly dynamicDelay?: (content?: IManagedHoverContentOrFactory) => number | undefined; +} + export class WorkbenchHoverDelegate extends Disposable implements IHoverDelegate { private lastHoverHideTime = 0; private timeLimit = 200; private _delay: number; - get delay(): number { + get delay(): number | ((content: IManagedHoverContentOrFactory) => number) { if (this.isInstantlyHovering()) { return 0; // show instantly when a hover was recently shown } + + if (this.hoverOptions?.dynamicDelay) { + return content => this.hoverOptions?.dynamicDelay?.(content) ?? this._delay; + } + return this._delay; } @@ -34,7 +44,7 @@ export class WorkbenchHoverDelegate extends Disposable implements IHoverDelegate constructor( public readonly placement: 'mouse' | 'element', - private readonly instantHover: boolean, + private readonly hoverOptions: IHoverDelayOptions | undefined, private overrideOptions: Partial | ((options: IHoverDelegateOptions, focus?: boolean) => Partial) = {}, @IConfigurationService private readonly configurationService: IConfigurationService, @IHoverService private readonly hoverService: IHoverService, @@ -69,7 +79,7 @@ export class WorkbenchHoverDelegate extends Disposable implements IHoverDelegate ? options.content.toString() : options.content.value; - return this.hoverService.showHover({ + return this.hoverService.showInstantHover({ ...options, ...overrideOptions, persistence: { @@ -87,11 +97,11 @@ export class WorkbenchHoverDelegate extends Disposable implements IHoverDelegate } private isInstantlyHovering(): boolean { - return this.instantHover && Date.now() - this.lastHoverHideTime < this.timeLimit; + return !!this.hoverOptions?.instantHover && Date.now() - this.lastHoverHideTime < this.timeLimit; } setInstantHoverTimeLimit(timeLimit: number): void { - if (!this.instantHover) { + if (!this.hoverOptions?.instantHover) { throw new Error('Instant hover is not enabled'); } this.timeLimit = timeLimit; @@ -99,7 +109,7 @@ export class WorkbenchHoverDelegate extends Disposable implements IHoverDelegate onDidHideHover(): void { this.hoverDisposables.clear(); - if (this.instantHover) { + if (this.hoverOptions?.instantHover) { this.lastHoverHideTime = Date.now(); } } diff --git a/src/vs/platform/hover/test/browser/nullHoverService.ts b/src/vs/platform/hover/test/browser/nullHoverService.ts index 4644330752bf..2f846c8e225b 100644 --- a/src/vs/platform/hover/test/browser/nullHoverService.ts +++ b/src/vs/platform/hover/test/browser/nullHoverService.ts @@ -9,7 +9,7 @@ import type { IHoverService } from '../../browser/hover.js'; export const NullHoverService: IHoverService = { _serviceBrand: undefined, hideHover: () => undefined, - showHover: () => undefined, + showInstantHover: () => undefined, showDelayedHover: () => undefined, setupDelayedHover: () => Disposable.None, setupDelayedHoverAtMouse: () => Disposable.None, diff --git a/src/vs/platform/languagePacks/node/languagePacks.ts b/src/vs/platform/languagePacks/node/languagePacks.ts index 9527de9f2c3d..f666ed0ddb19 100644 --- a/src/vs/platform/languagePacks/node/languagePacks.ts +++ b/src/vs/platform/languagePacks/node/languagePacks.ts @@ -170,11 +170,11 @@ class LanguagePacksCache extends Disposable { private updateHash(languagePack: ILanguagePack): void { if (languagePack) { - const md5 = createHash('md5'); // CodeQL [SM04514] Used to create an hash for language pack extension version, which is not a security issue + const sha256 = createHash('sha256'); // Secure hash algorithm for language pack extension version for (const extension of languagePack.extensions) { - md5.update(extension.extensionIdentifier.uuid || extension.extensionIdentifier.id).update(extension.version); // CodeQL [SM01510] The extension UUID is not sensitive info and is not manually created by a user + sha256.update(extension.extensionIdentifier.uuid || extension.extensionIdentifier.id).update(extension.version); // Using secure hash for identifier } - languagePack.hash = md5.digest('hex'); + languagePack.hash = sha256.digest('hex'); } } diff --git a/src/vs/platform/markers/common/markerService.ts b/src/vs/platform/markers/common/markerService.ts index 8e9968a94c92..68e6d0391bdf 100644 --- a/src/vs/platform/markers/common/markerService.ts +++ b/src/vs/platform/markers/common/markerService.ts @@ -6,10 +6,11 @@ import { isFalsyOrEmpty, isNonEmptyArray } from '../../../base/common/arrays.js'; import { DebounceEmitter } from '../../../base/common/event.js'; import { Iterable } from '../../../base/common/iterator.js'; -import { IDisposable } from '../../../base/common/lifecycle.js'; -import { ResourceMap } from '../../../base/common/map.js'; +import { IDisposable, toDisposable } from '../../../base/common/lifecycle.js'; +import { ResourceMap, ResourceSet } from '../../../base/common/map.js'; import { Schemas } from '../../../base/common/network.js'; import { URI } from '../../../base/common/uri.js'; +import { localize } from '../../../nls.js'; import { IMarker, IMarkerData, IMarkerService, IResourceMarker, MarkerSeverity, MarkerStatistics } from './markers.js'; export const unsupportedSchemas = new Set([ @@ -158,6 +159,7 @@ export class MarkerService implements IMarkerService { private readonly _data = new DoubleResourceMap(); private readonly _stats = new MarkerStats(this); + private readonly _filteredResources = new ResourceMap(); dispose(): void { this._stats.dispose(); @@ -197,6 +199,32 @@ export class MarkerService implements IMarkerService { } } + installResourceFilter(resource: URI, reason: string): IDisposable { + let reasons = this._filteredResources.get(resource); + + if (!reasons) { + reasons = []; + this._filteredResources.set(resource, reasons); + } + reasons.push(reason); + this._onMarkerChanged.fire([resource]); + + return toDisposable(() => { + const reasons = this._filteredResources.get(resource); + if (!reasons) { + return; + } + const reasonIndex = reasons.indexOf(reason); + if (reasonIndex !== -1) { + reasons.splice(reasonIndex, 1); + if (reasons.length === 0) { + this._filteredResources.delete(resource); + } + this._onMarkerChanged.fire([resource]); + } + }); + } + private static _toMarker(owner: string, resource: URI, data: IMarkerData): IMarker | undefined { let { code, severity, @@ -278,6 +306,26 @@ export class MarkerService implements IMarkerService { } } + /** + * Creates an information marker for filtered resources + */ + private _createFilteredMarker(resource: URI, reasons: string[]): IMarker { + const message = reasons.length === 1 + ? localize('filtered', "Problems are paused because: \"{0}\"", reasons[0]) + : localize('filtered.network', "Problems are paused because: \"{0}\" and {1} more", reasons[0], reasons.length - 1); + + return { + owner: 'markersFilter', + resource, + severity: MarkerSeverity.Info, + message, + startLineNumber: 1, + startColumn: 1, + endLineNumber: 1, + endColumn: 1, + }; + } + read(filter: { owner?: string; resource?: URI; severities?: number; take?: number } = Object.create(null)): IMarker[] { let { owner, resource, severities, take } = filter; @@ -288,48 +336,56 @@ export class MarkerService implements IMarkerService { if (owner && resource) { // exactly one owner AND resource + const reasons = this._filteredResources.get(resource); + if (reasons?.length) { + const infoMarker = this._createFilteredMarker(resource, reasons); + return [infoMarker]; + } + const data = this._data.get(resource, owner); if (!data) { return []; - } else { - const result: IMarker[] = []; - for (const marker of data) { - if (MarkerService._accept(marker, severities)) { - const newLen = result.push(marker); - if (take > 0 && newLen === take) { - break; - } - } - } - return result; } - } else if (!owner && !resource) { - // all const result: IMarker[] = []; - for (const markers of this._data.values()) { - for (const data of markers) { - if (MarkerService._accept(data, severities)) { - const newLen = result.push(data); - if (take > 0 && newLen === take) { - return result; - } - } + for (const marker of data) { + if (take > 0 && result.length === take) { + break; + } + const reasons = this._filteredResources.get(resource); + if (reasons?.length) { + result.push(this._createFilteredMarker(resource, reasons)); + + } else if (MarkerService._accept(marker, severities)) { + result.push(marker); } } return result; } else { // of one resource OR owner - const iterable = this._data.values(resource ?? owner!); + const iterable = !owner && !resource + ? this._data.values() + : this._data.values(resource ?? owner!); + const result: IMarker[] = []; + const filtered = new ResourceSet(); + for (const markers of iterable) { for (const data of markers) { - if (MarkerService._accept(data, severities)) { - const newLen = result.push(data); - if (take > 0 && newLen === take) { - return result; - } + if (filtered.has(data.resource)) { + continue; + } + if (take > 0 && result.length === take) { + break; + } + const reasons = this._filteredResources.get(data.resource); + if (reasons?.length) { + result.push(this._createFilteredMarker(data.resource, reasons)); + filtered.add(data.resource); + + } else if (MarkerService._accept(data, severities)) { + result.push(data); } } } diff --git a/src/vs/platform/markers/common/markers.ts b/src/vs/platform/markers/common/markers.ts index 73cb120869a3..2c7f16685144 100644 --- a/src/vs/platform/markers/common/markers.ts +++ b/src/vs/platform/markers/common/markers.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { Event } from '../../../base/common/event.js'; +import { IDisposable } from '../../../base/common/lifecycle.js'; import Severity from '../../../base/common/severity.js'; import { URI } from '../../../base/common/uri.js'; import { localize } from '../../../nls.js'; @@ -22,6 +23,8 @@ export interface IMarkerService { read(filter?: { owner?: string; resource?: URI; severities?: number; take?: number }): IMarker[]; + installResourceFilter(resource: URI, reason: string): IDisposable; + readonly onMarkerChanged: Event; } @@ -64,6 +67,15 @@ export namespace MarkerSeverity { return _displayStrings[a] || ''; } + const _displayStringsPlural: { [value: number]: string } = Object.create(null); + _displayStringsPlural[MarkerSeverity.Error] = localize('sev.errors', "Errors"); + _displayStringsPlural[MarkerSeverity.Warning] = localize('sev.warnings', "Warnings"); + _displayStringsPlural[MarkerSeverity.Info] = localize('sev.infos', "Infos"); + + export function toStringPlural(a: MarkerSeverity): string { + return _displayStringsPlural[a] || ''; + } + export function fromSeverity(severity: Severity): MarkerSeverity { switch (severity) { case Severity.Error: return MarkerSeverity.Error; diff --git a/src/vs/platform/markers/test/common/markerService.test.ts b/src/vs/platform/markers/test/common/markerService.test.ts index b3d1ff9a2ad4..4ccdd62b50e9 100644 --- a/src/vs/platform/markers/test/common/markerService.test.ts +++ b/src/vs/platform/markers/test/common/markerService.test.ts @@ -211,4 +211,215 @@ suite('Marker Service', () => { assert.strictEqual(marker.length, 1); assert.strictEqual(marker[0].code, '0'); }); + + test('resource filter hides markers for the filtered resource', () => { + service = new markerService.MarkerService(); + const resource1 = URI.parse('file:///path/file1.cs'); + const resource2 = URI.parse('file:///path/file2.cs'); + + // Add markers to both resources + service.changeOne('owner1', resource1, [randomMarkerData()]); + service.changeOne('owner1', resource2, [randomMarkerData()]); + + // Verify both resources have markers + assert.strictEqual(service.read().length, 2); + assert.strictEqual(service.read({ resource: resource1 }).length, 1); + assert.strictEqual(service.read({ resource: resource2 }).length, 1); + + // Install filter for resource1 + const filter = service.installResourceFilter(resource1, 'Test filter'); + + // Verify resource1 markers are filtered out, but have 1 info marker instead + assert.strictEqual(service.read().length, 2); // 1 real + 1 info + assert.strictEqual(service.read({ resource: resource1 }).length, 1); // 1 info + assert.strictEqual(service.read({ resource: resource2 }).length, 1); + + // Dispose filter + filter.dispose(); + + // Verify resource1 markers are visible again + assert.strictEqual(service.read().length, 2); + assert.strictEqual(service.read({ resource: resource1 }).length, 1); + assert.strictEqual(service.read({ resource: resource2 }).length, 1); + }); + + test('resource filter affects all filter combinations', () => { + service = new markerService.MarkerService(); + const resource = URI.parse('file:///path/file.cs'); + + service.changeOne('owner1', resource, [randomMarkerData(MarkerSeverity.Error)]); + service.changeOne('owner2', resource, [randomMarkerData(MarkerSeverity.Warning)]); + + // Verify initial state + assert.strictEqual(service.read().length, 2); + assert.strictEqual(service.read({ resource }).length, 2); + assert.strictEqual(service.read({ owner: 'owner1' }).length, 1); + assert.strictEqual(service.read({ owner: 'owner2' }).length, 1); + assert.strictEqual(service.read({ owner: 'owner1', resource }).length, 1); + assert.strictEqual(service.read({ severities: MarkerSeverity.Error }).length, 1); + assert.strictEqual(service.read({ severities: MarkerSeverity.Warning }).length, 1); + + // Install filter + const filter = service.installResourceFilter(resource, 'Filter reason'); + + // Verify information marker is shown for resource queries + assert.strictEqual(service.read().length, 1); // 1 info marker + assert.strictEqual(service.read({ resource }).length, 1); // 1 info marker + assert.strictEqual(service.read({ owner: 'owner1' }).length, 1); // 1 info marker + assert.strictEqual(service.read({ owner: 'owner2' }).length, 1); // 1 info marker + + // Verify owner+resource query returns an info marker for filtered resources + const ownerResourceMarkers = service.read({ owner: 'owner1', resource }); + assert.strictEqual(ownerResourceMarkers.length, 1); + assert.strictEqual(ownerResourceMarkers[0].severity, MarkerSeverity.Info); + assert.strictEqual(ownerResourceMarkers[0].owner, 'markersFilter'); + + assert.strictEqual(service.read({ severities: MarkerSeverity.Error }).length, 1); // 1 info marker + assert.strictEqual(service.read({ severities: MarkerSeverity.Warning }).length, 1); // 1 info marker + assert.strictEqual(service.read({ severities: MarkerSeverity.Info }).length, 1); // Our info marker + + // Remove filter and verify markers are visible again + filter.dispose(); + assert.strictEqual(service.read().length, 2); + }); + + test('multiple filters for same resource are handled correctly', () => { + service = new markerService.MarkerService(); + const resource = URI.parse('file:///path/file.cs'); + + // Add marker to resource + service.changeOne('owner1', resource, [randomMarkerData()]); + + // Verify resource has markers + assert.strictEqual(service.read().length, 1); + assert.strictEqual(service.read({ resource }).length, 1); + + // Install two filters for the same resource + const filter1 = service.installResourceFilter(resource, 'First filter'); + const filter2 = service.installResourceFilter(resource, 'Second filter'); + + // Verify resource markers are filtered out but info marker is shown + assert.strictEqual(service.read().length, 1); // 1 info marker + assert.strictEqual(service.read({ resource }).length, 1); // 1 info marker + + // Dispose only one filter + filter1.dispose(); + + // Verify resource markers are still filtered out because one filter remains + assert.strictEqual(service.read().length, 1); // still 1 info marker + assert.strictEqual(service.read({ resource }).length, 1); // still 1 info marker + + // Dispose the second filter + filter2.dispose(); + + // Now all filters are gone, so markers should be visible again + assert.strictEqual(service.read().length, 1); + assert.strictEqual(service.read({ resource }).length, 1); + }); + + test('resource filter with reason shows info marker when markers are filtered', () => { + service = new markerService.MarkerService(); + const resource = URI.parse('file:///path/file.cs'); + + // Add error and warning to the resource + service.changeOne('owner1', resource, [ + randomMarkerData(MarkerSeverity.Error), + randomMarkerData(MarkerSeverity.Warning) + ]); + + // Verify initial state + assert.strictEqual(service.read().length, 2); + assert.strictEqual(service.read({ resource }).length, 2); + + // Apply a filter with reason + const filterReason = 'Test filter reason'; + const filter = service.installResourceFilter(resource, filterReason); + + // Verify that we get a single info marker with our reason + const markers = service.read({ resource }); + assert.strictEqual(markers.length, 1); + assert.strictEqual(markers[0].severity, MarkerSeverity.Info); + assert.ok(markers[0].message.includes(filterReason)); + + // Remove filter and verify the original markers are back + filter.dispose(); + assert.strictEqual(service.read({ resource }).length, 2); + }); + + test('reading all markers shows info marker for filtered resources', () => { + service = new markerService.MarkerService(); + const resource1 = URI.parse('file:///path/file1.cs'); + const resource2 = URI.parse('file:///path/file2.cs'); + + // Add markers to both resources + service.changeOne('owner1', resource1, [randomMarkerData()]); + service.changeOne('owner1', resource2, [randomMarkerData()]); + + // Verify initial state + assert.strictEqual(service.read().length, 2); + + // Filter one resource with a reason + const filterReason = 'Resource is being edited'; + const filter = service.installResourceFilter(resource1, filterReason); + + // Read all markers + const allMarkers = service.read(); + + // Should have 2 markers - one real marker and one info marker + assert.strictEqual(allMarkers.length, 2); + + // Find the info marker + const infoMarker = allMarkers.find(marker => + marker.owner === 'markersFilter' && + marker.severity === MarkerSeverity.Info + ); + + // Verify the info marker + assert.ok(infoMarker); + assert.strictEqual(infoMarker?.resource.toString(), resource1.toString()); + assert.ok(infoMarker?.message.includes(filterReason)); + + // Remove filter + filter.dispose(); + }); + + test('out of order filter disposal works correctly', () => { + service = new markerService.MarkerService(); + const resource = URI.parse('file:///path/file.cs'); + + // Add marker to resource + service.changeOne('owner1', resource, [randomMarkerData()]); + + // Verify resource has markers + assert.strictEqual(service.read().length, 1); + assert.strictEqual(service.read({ resource }).length, 1); + + // Install three filters for the same resource + const filter1 = service.installResourceFilter(resource, 'First filter'); + const filter2 = service.installResourceFilter(resource, 'Second filter'); + const filter3 = service.installResourceFilter(resource, 'Third filter'); + + // Verify resource markers are filtered out but info marker is shown + assert.strictEqual(service.read().length, 1); // 1 info marker + assert.strictEqual(service.read({ resource }).length, 1); // 1 info marker + + // Dispose filters in a different order than they were created + filter2.dispose(); // Remove the second filter first + + // Verify resource markers are still filtered out with 2 filters remaining + assert.strictEqual(service.read().length, 1); // still 1 info marker + assert.strictEqual(service.read({ resource }).length, 1); // still 1 info marker + + // Check if message contains the correct count of filters + const markers = service.read({ resource }); + assert.ok(markers[0].message.includes('Problems are paused because')); + + // Remove remaining filters in any order + filter3.dispose(); + filter1.dispose(); + + // Now all filters are gone, so markers should be visible again + assert.strictEqual(service.read().length, 1); + assert.strictEqual(service.read({ resource }).length, 1); + }); }); diff --git a/src/vs/platform/mcp/common/mcpManagementCli.ts b/src/vs/platform/mcp/common/mcpManagementCli.ts new file mode 100644 index 000000000000..f25efca69997 --- /dev/null +++ b/src/vs/platform/mcp/common/mcpManagementCli.ts @@ -0,0 +1,63 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IConfigurationService } from '../../configuration/common/configuration.js'; +import { ILogger } from '../../log/common/log.js'; +import { IMcpConfiguration, IMcpConfigurationSSE, IMcpConfigurationStdio } from './mcpPlatformTypes.js'; + +type ValidatedConfig = { name: string; config: IMcpConfigurationStdio | IMcpConfigurationSSE }; + +export class McpManagementCli { + constructor( + private readonly _logger: ILogger, + @IConfigurationService private readonly _userConfigurationService: IConfigurationService, + ) { } + + async addMcpDefinitions( + definitions: string[], + ) { + const configs = definitions.map((config) => this.validateConfiguration(config)); + await this.updateMcpInConfig(this._userConfigurationService, configs); + this._logger.info(`Added MCP servers: ${configs.map(c => c.name).join(', ')}`); + } + + private async updateMcpInConfig(service: IConfigurationService, configs: ValidatedConfig[]) { + const mcp = service.getValue('mcp') || { servers: {} }; + mcp.servers ??= {}; + + for (const config of configs) { + mcp.servers[config.name] = config.config; + } + + await service.updateValue('mcp', mcp); + } + + private validateConfiguration(config: string): ValidatedConfig { + let parsed: (IMcpConfigurationStdio | IMcpConfigurationSSE) & { name: string }; + try { + parsed = JSON.parse(config); + } catch (e) { + throw new InvalidMcpOperationError(`Invalid JSON '${config}': ${e}`); + } + + if (!parsed.name) { + throw new InvalidMcpOperationError(`Missing name property in ${config}`); + } + + if (!('command' in parsed) && !('url' in parsed)) { + throw new InvalidMcpOperationError(`Missing command or URL property in ${config}`); + } + + const { name, ...rest } = parsed; + return { name, config: rest as IMcpConfigurationStdio | IMcpConfigurationSSE }; + } +} + +class InvalidMcpOperationError extends Error { + constructor(message: string) { + super(message); + this.stack = message; + } +} diff --git a/src/vs/platform/mcp/common/mcpPlatformTypes.ts b/src/vs/platform/mcp/common/mcpPlatformTypes.ts new file mode 100644 index 000000000000..47df96028fba --- /dev/null +++ b/src/vs/platform/mcp/common/mcpPlatformTypes.ts @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export interface IMcpConfiguration { + inputs?: unknown[]; + /** @deprecated Only for rough cross-compat with other formats */ + mcpServers?: Record; + servers?: Record; +} + +export type McpConfigurationServer = IMcpConfigurationStdio | IMcpConfigurationSSE; + +export interface IMcpConfigurationStdio { + type?: 'stdio'; + command: string; + args?: readonly string[]; + env?: Record; +} + +export interface IMcpConfigurationSSE { + type: 'sse'; + url: string; + headers?: Record; +} diff --git a/src/vs/platform/mcp/common/nativeMcpDiscoveryHelper.ts b/src/vs/platform/mcp/common/nativeMcpDiscoveryHelper.ts new file mode 100644 index 000000000000..31fc9e2604a6 --- /dev/null +++ b/src/vs/platform/mcp/common/nativeMcpDiscoveryHelper.ts @@ -0,0 +1,26 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Platform } from '../../../base/common/platform.js'; +import { URI } from '../../../base/common/uri.js'; +import { createDecorator } from '../../instantiation/common/instantiation.js'; + +export const INativeMcpDiscoveryHelperService = createDecorator('INativeMcpDiscoveryHelperService'); + +export const NativeMcpDiscoveryHelperChannelName = 'NativeMcpDiscoveryHelper'; + +export interface INativeMcpDiscoveryData { + // platform and homedir are duplicated by the remote/native environment, but here for convenience + platform: Platform; + homedir: URI; + winAppData?: URI; + xdgHome?: URI; +} + +export interface INativeMcpDiscoveryHelperService { + readonly _serviceBrand: undefined; + + load(): Promise; +} diff --git a/src/vs/platform/mcp/node/nativeMcpDiscoveryHelperChannel.ts b/src/vs/platform/mcp/node/nativeMcpDiscoveryHelperChannel.ts new file mode 100644 index 000000000000..b09f36ba58ef --- /dev/null +++ b/src/vs/platform/mcp/node/nativeMcpDiscoveryHelperChannel.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 { Event } from '../../../base/common/event.js'; +import { IURITransformer, transformOutgoingURIs } from '../../../base/common/uriIpc.js'; +import { IServerChannel } from '../../../base/parts/ipc/common/ipc.js'; +import { INativeMcpDiscoveryHelperService } from '../common/nativeMcpDiscoveryHelper.js'; + +export class NativeMcpDiscoveryHelperChannel implements IServerChannel { + + constructor( + private getUriTransformer: undefined | ((requestContext: any) => IURITransformer), + @INativeMcpDiscoveryHelperService private nativeMcpDiscoveryHelperService: INativeMcpDiscoveryHelperService + ) { } + + listen(context: any, event: string): Event { + throw new Error('Invalid listen'); + } + + async call(context: any, command: string, args?: any): Promise { + const uriTransformer = this.getUriTransformer?.(context); + switch (command) { + case 'load': { + const result = await this.nativeMcpDiscoveryHelperService.load(); + return uriTransformer ? transformOutgoingURIs(result, uriTransformer) : result; + } + } + throw new Error('Invalid call'); + } +} + diff --git a/src/vs/platform/mcp/node/nativeMcpDiscoveryHelperService.ts b/src/vs/platform/mcp/node/nativeMcpDiscoveryHelperService.ts new file mode 100644 index 000000000000..987f25f9a7cd --- /dev/null +++ b/src/vs/platform/mcp/node/nativeMcpDiscoveryHelperService.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 { homedir } from 'os'; +import { platform } from '../../../base/common/platform.js'; +import { URI } from '../../../base/common/uri.js'; +import { INativeMcpDiscoveryData, INativeMcpDiscoveryHelperService } from '../common/nativeMcpDiscoveryHelper.js'; + +export class NativeMcpDiscoveryHelperService implements INativeMcpDiscoveryHelperService { + declare readonly _serviceBrand: undefined; + + constructor() { } + + load(): Promise { + return Promise.resolve({ + platform, + homedir: URI.file(homedir()), + winAppData: this.uriFromEnvVariable('APPDATA'), + xdgHome: this.uriFromEnvVariable('XDG_CONFIG_HOME'), + }); + } + + private uriFromEnvVariable(varName: string) { + const envVar = process.env[varName]; + if (!envVar) { + return undefined; + } + return URI.file(envVar); + } +} + diff --git a/src/vs/platform/menubar/electron-main/menubarMainService.ts b/src/vs/platform/menubar/electron-main/menubarMainService.ts index a80966d417af..b26f617df2fd 100644 --- a/src/vs/platform/menubar/electron-main/menubarMainService.ts +++ b/src/vs/platform/menubar/electron-main/menubarMainService.ts @@ -20,7 +20,7 @@ export class MenubarMainService extends Disposable implements IMenubarMainServic declare readonly _serviceBrand: undefined; - private readonly menubar = this.installMenuBarAfterWindowOpen(); + private readonly menubar: Promise; constructor( @IInstantiationService private readonly instantiationService: IInstantiationService, @@ -28,6 +28,8 @@ export class MenubarMainService extends Disposable implements IMenubarMainServic @ILogService private readonly logService: ILogService ) { super(); + + this.menubar = this.installMenuBarAfterWindowOpen(); } private async installMenuBarAfterWindowOpen(): Promise { diff --git a/src/vs/platform/native/common/native.ts b/src/vs/platform/native/common/native.ts index 03a2d54de4ec..4d60c1d9f1bb 100644 --- a/src/vs/platform/native/common/native.ts +++ b/src/vs/platform/native/common/native.ts @@ -86,6 +86,9 @@ export interface ICommonNativeHostService { getCursorScreenPoint(): Promise<{ readonly point: IPoint; readonly display: IRectangle }>; isMaximized(options?: INativeHostOptions): Promise; + maximizeWindow(options?: INativeHostOptions): Promise; + unmaximizeWindow(options?: INativeHostOptions): Promise; + minimizeWindow(options?: INativeHostOptions): Promise; moveWindowTop(options?: INativeHostOptions): Promise; positionWindow(position: IRectangle, options?: INativeHostOptions): Promise; @@ -110,9 +113,6 @@ export interface ICommonNativeHostService { */ focusWindow(options?: INativeHostOptions & { force?: boolean }): Promise; - // Titlebar default style override - overrideDefaultTitlebarStyle(style: 'custom' | undefined): Promise; - // Dialogs showMessageBox(options: MessageBoxOptions & INativeHostOptions): Promise; showSaveDialog(options: SaveDialogOptions & INativeHostOptions): Promise; diff --git a/src/vs/platform/native/electron-main/nativeHostMainService.ts b/src/vs/platform/native/electron-main/nativeHostMainService.ts index b24a24ea61bb..6324e98e290f 100644 --- a/src/vs/platform/native/electron-main/nativeHostMainService.ts +++ b/src/vs/platform/native/electron-main/nativeHostMainService.ts @@ -48,7 +48,6 @@ import { IConfigurationService } from '../../configuration/common/configuration. import { IProxyAuthService } from './auth.js'; import { AuthInfo, Credentials, IRequestService } from '../../request/common/request.js'; import { randomPath } from '../../../base/common/extpath.js'; -import { IStateService } from '../../state/node/state.js'; export interface INativeHostMainService extends AddFirstParameterToFunctions /* only methods, not events */, number | undefined /* window ID */> { } @@ -71,10 +70,63 @@ export class NativeHostMainService extends Disposable implements INativeHostMain @IConfigurationService private readonly configurationService: IConfigurationService, @IRequestService private readonly requestService: IRequestService, @IProxyAuthService private readonly proxyAuthService: IProxyAuthService, - @IInstantiationService private readonly instantiationService: IInstantiationService, - @IStateService private readonly stateService: IStateService + @IInstantiationService private readonly instantiationService: IInstantiationService ) { super(); + + // Events + { + this.onDidOpenMainWindow = Event.map(this.windowsMainService.onDidOpenWindow, window => window.id); + + this.onDidTriggerWindowSystemContextMenu = Event.any( + Event.map(this.windowsMainService.onDidTriggerSystemContextMenu, ({ window, x, y }) => ({ windowId: window.id, x, y })), + Event.map(this.auxiliaryWindowsMainService.onDidTriggerSystemContextMenu, ({ window, x, y }) => ({ windowId: window.id, x, y })) + ); + + this.onDidMaximizeWindow = Event.any( + Event.map(this.windowsMainService.onDidMaximizeWindow, window => window.id), + Event.map(this.auxiliaryWindowsMainService.onDidMaximizeWindow, window => window.id) + ); + this.onDidUnmaximizeWindow = Event.any( + Event.map(this.windowsMainService.onDidUnmaximizeWindow, window => window.id), + Event.map(this.auxiliaryWindowsMainService.onDidUnmaximizeWindow, window => window.id) + ); + + this.onDidChangeWindowFullScreen = Event.any( + Event.map(this.windowsMainService.onDidChangeFullScreen, e => ({ windowId: e.window.id, fullscreen: e.fullscreen })), + Event.map(this.auxiliaryWindowsMainService.onDidChangeFullScreen, e => ({ windowId: e.window.id, fullscreen: e.fullscreen })) + ); + + this.onDidBlurMainWindow = Event.filter(Event.fromNodeEventEmitter(app, 'browser-window-blur', (event, window: BrowserWindow) => window.id), windowId => !!this.windowsMainService.getWindowById(windowId)); + this.onDidFocusMainWindow = Event.any( + Event.map(Event.filter(Event.map(this.windowsMainService.onDidChangeWindowsCount, () => this.windowsMainService.getLastActiveWindow()), window => !!window), window => window!.id), + Event.filter(Event.fromNodeEventEmitter(app, 'browser-window-focus', (event, window: BrowserWindow) => window.id), windowId => !!this.windowsMainService.getWindowById(windowId)) + ); + + this.onDidBlurMainOrAuxiliaryWindow = Event.any( + this.onDidBlurMainWindow, + Event.map(Event.filter(Event.fromNodeEventEmitter(app, 'browser-window-blur', (event, window: BrowserWindow) => this.auxiliaryWindowsMainService.getWindowByWebContents(window.webContents)), window => !!window), window => window!.id) + ); + this.onDidFocusMainOrAuxiliaryWindow = Event.any( + this.onDidFocusMainWindow, + Event.map(Event.filter(Event.fromNodeEventEmitter(app, 'browser-window-focus', (event, window: BrowserWindow) => this.auxiliaryWindowsMainService.getWindowByWebContents(window.webContents)), window => !!window), window => window!.id) + ); + + this.onDidResumeOS = Event.fromNodeEventEmitter(powerMonitor, 'resume'); + + this.onDidChangeColorScheme = this.themeMainService.onDidChangeColorScheme; + + this.onDidChangeDisplay = Event.debounce(Event.any( + Event.filter(Event.fromNodeEventEmitter(screen, 'display-metrics-changed', (event: Electron.Event, display: Display, changedMetrics?: string[]) => changedMetrics), changedMetrics => { + // Electron will emit 'display-metrics-changed' events even when actually + // going fullscreen, because the dock hides. However, we do not want to + // react on this event as there is no change in display bounds. + return !(Array.isArray(changedMetrics) && changedMetrics.length === 1 && changedMetrics[0] === 'workArea'); + }), + Event.fromNodeEventEmitter(screen, 'display-added'), + Event.fromNodeEventEmitter(screen, 'display-removed') + ), () => { }, 100); + } } @@ -87,59 +139,29 @@ export class NativeHostMainService extends Disposable implements INativeHostMain //#region Events - readonly onDidOpenMainWindow = Event.map(this.windowsMainService.onDidOpenWindow, window => window.id); - - readonly onDidTriggerWindowSystemContextMenu = Event.any( - Event.map(this.windowsMainService.onDidTriggerSystemContextMenu, ({ window, x, y }) => ({ windowId: window.id, x, y })), - Event.map(this.auxiliaryWindowsMainService.onDidTriggerSystemContextMenu, ({ window, x, y }) => ({ windowId: window.id, x, y })) - ); - - readonly onDidMaximizeWindow = Event.any( - Event.map(this.windowsMainService.onDidMaximizeWindow, window => window.id), - Event.map(this.auxiliaryWindowsMainService.onDidMaximizeWindow, window => window.id) - ); - readonly onDidUnmaximizeWindow = Event.any( - Event.map(this.windowsMainService.onDidUnmaximizeWindow, window => window.id), - Event.map(this.auxiliaryWindowsMainService.onDidUnmaximizeWindow, window => window.id) - ); - - readonly onDidChangeWindowFullScreen = Event.any( - Event.map(this.windowsMainService.onDidChangeFullScreen, e => ({ windowId: e.window.id, fullscreen: e.fullscreen })), - Event.map(this.auxiliaryWindowsMainService.onDidChangeFullScreen, e => ({ windowId: e.window.id, fullscreen: e.fullscreen })) - ); - - readonly onDidBlurMainWindow = Event.filter(Event.fromNodeEventEmitter(app, 'browser-window-blur', (event, window: BrowserWindow) => window.id), windowId => !!this.windowsMainService.getWindowById(windowId)); - readonly onDidFocusMainWindow = Event.any( - Event.map(Event.filter(Event.map(this.windowsMainService.onDidChangeWindowsCount, () => this.windowsMainService.getLastActiveWindow()), window => !!window), window => window!.id), - Event.filter(Event.fromNodeEventEmitter(app, 'browser-window-focus', (event, window: BrowserWindow) => window.id), windowId => !!this.windowsMainService.getWindowById(windowId)) - ); - - readonly onDidBlurMainOrAuxiliaryWindow = Event.any( - this.onDidBlurMainWindow, - Event.map(Event.filter(Event.fromNodeEventEmitter(app, 'browser-window-blur', (event, window: BrowserWindow) => this.auxiliaryWindowsMainService.getWindowByWebContents(window.webContents)), window => !!window), window => window!.id) - ); - readonly onDidFocusMainOrAuxiliaryWindow = Event.any( - this.onDidFocusMainWindow, - Event.map(Event.filter(Event.fromNodeEventEmitter(app, 'browser-window-focus', (event, window: BrowserWindow) => this.auxiliaryWindowsMainService.getWindowByWebContents(window.webContents)), window => !!window), window => window!.id) - ); - - readonly onDidResumeOS = Event.fromNodeEventEmitter(powerMonitor, 'resume'); - - readonly onDidChangeColorScheme = this.themeMainService.onDidChangeColorScheme; + readonly onDidOpenMainWindow: Event; + + readonly onDidTriggerWindowSystemContextMenu: Event<{ windowId: number; x: number; y: number }>; + + readonly onDidMaximizeWindow: Event; + readonly onDidUnmaximizeWindow: Event; + + readonly onDidChangeWindowFullScreen: Event<{ readonly windowId: number; readonly fullscreen: boolean }>; + + readonly onDidBlurMainWindow: Event; + readonly onDidFocusMainWindow: Event; + + readonly onDidBlurMainOrAuxiliaryWindow: Event; + readonly onDidFocusMainOrAuxiliaryWindow: Event; + + readonly onDidResumeOS: Event; + + readonly onDidChangeColorScheme: Event; private readonly _onDidChangePassword = this._register(new Emitter<{ account: string; service: string }>()); readonly onDidChangePassword = this._onDidChangePassword.event; - readonly onDidChangeDisplay = Event.debounce(Event.any( - Event.filter(Event.fromNodeEventEmitter(screen, 'display-metrics-changed', (event: Electron.Event, display: Display, changedMetrics?: string[]) => changedMetrics), changedMetrics => { - // Electron will emit 'display-metrics-changed' events even when actually - // going fullscreen, because the dock hides. However, we do not want to - // react on this event as there is no change in display bounds. - return !(Array.isArray(changedMetrics) && changedMetrics.length === 1 && changedMetrics[0] === 'workArea'); - }), - Event.fromNodeEventEmitter(screen, 'display-added'), - Event.fromNodeEventEmitter(screen, 'display-removed') - ), () => { }, 100); + readonly onDidChangeDisplay: Event; //#endregion @@ -262,6 +284,21 @@ export class NativeHostMainService extends Disposable implements INativeHostMain return window?.win?.isMaximized() ?? false; } + async maximizeWindow(windowId: number | undefined, options?: INativeHostOptions): Promise { + const window = this.windowById(options?.targetWindowId, windowId); + window?.win?.maximize(); + } + + async unmaximizeWindow(windowId: number | undefined, options?: INativeHostOptions): Promise { + const window = this.windowById(options?.targetWindowId, windowId); + window?.win?.unmaximize(); + } + + async minimizeWindow(windowId: number | undefined, options?: INativeHostOptions): Promise { + const window = this.windowById(options?.targetWindowId, windowId); + window?.win?.minimize(); + } + async moveWindowTop(windowId: number | undefined, options?: INativeHostOptions): Promise { const window = this.windowById(options?.targetWindowId, windowId); window?.win?.moveTop(); @@ -313,14 +350,6 @@ export class NativeHostMainService extends Disposable implements INativeHostMain this.themeMainService.saveWindowSplash(windowId, window?.openedWorkspace, splash); } - async overrideDefaultTitlebarStyle(windowId: number | undefined, style: 'custom' | undefined): Promise { - if (style === 'custom') { - this.stateService.setItem('window.titleBarStyleOverride', style); - } else { - this.stateService.removeItem('window.titleBarStyleOverride'); - } - } - //#endregion diff --git a/src/vs/platform/observable/common/observableMemento.ts b/src/vs/platform/observable/common/observableMemento.ts new file mode 100644 index 000000000000..cf38e2fb80b2 --- /dev/null +++ b/src/vs/platform/observable/common/observableMemento.ts @@ -0,0 +1,95 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { strictEquals } from '../../../base/common/equals.js'; +import { DisposableStore, IDisposable } from '../../../base/common/lifecycle.js'; +import { ObservableValue } from '../../../base/common/observableInternal/base.js'; +import { DebugNameData } from '../../../base/common/observableInternal/debugName.js'; +import { IStorageService, StorageScope, StorageTarget } from '../../storage/common/storage.js'; + +interface IObservableMementoOpts { + defaultValue: T; + key: string; + /** Storage options, defaults to JSON storage if the defaultValue is an object */ + toStorage?: (value: T) => string; + fromStorage?: (value: string) => T; +} + +/** + * Defines an observable memento. Returns a function that can be called with + * the specific storage scope, target, and service to use in a class. + * + * Note that the returned Observable is a disposable, because it interacts + * with storage service events, and must be tracked appropriately. + */ +export function observableMemento(opts: IObservableMementoOpts) { + return (scope: StorageScope, target: StorageTarget, storageService: IStorageService): ObservableMemento => { + return new ObservableMemento(opts, scope, target, storageService); + }; +} + +/** + * A value that is stored, and is also observable. Note: T should be readonly. + */ +export class ObservableMemento extends ObservableValue implements IDisposable { + private readonly _store = new DisposableStore(); + private _didChange = false; + + constructor( + opts: IObservableMementoOpts, + storageScope: StorageScope, + storageTarget: StorageTarget, + @IStorageService storageService: IStorageService, + ) { + if (opts.defaultValue && typeof opts.defaultValue === 'object') { + opts.toStorage ??= (value: T) => JSON.stringify(value); + opts.fromStorage ??= (value: string) => JSON.parse(value); + } + + let initialValue = opts.defaultValue; + + const fromStorage = storageService.get(opts.key, storageScope); + if (fromStorage !== undefined) { + if (opts.fromStorage) { + try { + initialValue = opts.fromStorage(fromStorage); + } catch { + initialValue = opts.defaultValue; + } + } + } + + super(new DebugNameData(undefined, `storage/${opts.key}`, undefined), initialValue, strictEquals); + + const didChange = storageService.onDidChangeValue(storageScope, opts.key, this._store); + // only take external changes if there aren't local changes we've made + this._store.add(didChange((e) => { + if (e.external && e.key === opts.key && !this._didChange) { + this.set(opts.defaultValue, undefined); + } + })); + + this._store.add(storageService.onWillSaveState(() => { + if (this._didChange) { + this._didChange = false; + const value = this.get(); + if (opts.toStorage) { + storageService.store(opts.key, opts.toStorage(value), storageScope, storageTarget); + } else { + storageService.store(opts.key, String(value), storageScope, storageTarget); + } + } + })); + } + + protected override _setValue(newValue: T): void { + super._setValue(newValue); + this._didChange = true; + } + + dispose(): void { + this._store.dispose(); + } +} diff --git a/src/vs/platform/policy/common/policy.ts b/src/vs/platform/policy/common/policy.ts index 5da12a2a56a9..b7313ff03ccb 100644 --- a/src/vs/platform/policy/common/policy.ts +++ b/src/vs/platform/policy/common/policy.ts @@ -10,8 +10,8 @@ import { Disposable } from '../../../base/common/lifecycle.js'; import { createDecorator } from '../../instantiation/common/instantiation.js'; export type PolicyName = string; -export type PolicyValue = string | number; -export type PolicyDefinition = { type: 'string' | 'number' }; +export type PolicyValue = string | number | boolean; +export type PolicyDefinition = { type: 'string' | 'number' | 'boolean' }; export const IPolicyService = createDecorator('policy'); diff --git a/src/vs/platform/policy/common/policyIpc.ts b/src/vs/platform/policy/common/policyIpc.ts index c95719592bce..803f140ae2d4 100644 --- a/src/vs/platform/policy/common/policyIpc.ts +++ b/src/vs/platform/policy/common/policyIpc.ts @@ -72,5 +72,4 @@ export class PolicyChannelClient extends AbstractPolicyService implements IPolic this.policies.set(name, result[name]); } } - } diff --git a/src/vs/platform/product/common/product.ts b/src/vs/platform/product/common/product.ts index 1a2a6192ec8b..d27cdce900fd 100644 --- a/src/vs/platform/product/common/product.ts +++ b/src/vs/platform/product/common/product.ts @@ -8,7 +8,9 @@ import { IProductConfiguration } from '../../../base/common/product.js'; import { ISandboxConfiguration } from '../../../base/parts/sandbox/common/sandboxTypes.js'; /** - * @deprecated You MUST use `IProductService` if possible. + * @deprecated It is preferred that you use `IProductService` if you can. This + * allows web embedders to override our defaults. But for things like `product.quality`, + * the use is fine because that property is not overridable. */ let product: IProductConfiguration; @@ -72,7 +74,4 @@ else { } } -/** - * @deprecated You MUST use `IProductService` if possible. - */ export default product; diff --git a/src/vs/platform/prompts/common/config.ts b/src/vs/platform/prompts/common/config.ts index 06032e80341a..4ea1187b61e3 100644 --- a/src/vs/platform/prompts/common/config.ts +++ b/src/vs/platform/prompts/common/config.ts @@ -3,58 +3,19 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { ContextKeyExpr } from '../../contextkey/common/contextkey.js'; import { IConfigurationService } from '../../configuration/common/configuration.js'; /** * Configuration helper for the `reusable prompts` feature. - * @see {@link CONFIG_KEY}. + * @see {@link CONFIG_KEY} and {@link LOCATIONS_CONFIG_KEY}. * * ### Functions * - * - {@link getValue} allows to current read configuration value * - {@link enabled} allows to check if the feature is enabled + * - {@link getLocationsValue} allows to current read configuration value * - {@link promptSourceFolders} gets list of source folders for prompt files * - * ### Configuration Examples - * - * Enable the feature using the default `'.github/prompts'` folder as a source of prompt files: - * ```json - * { - * "chat.promptFiles": {}, - * } - * ``` - * - * Enable the feature, providing multiple source folder paths for prompt files, - * in addition to the default `'.github/prompts'` one: - * ```json - * { - * "chat.promptFiles": { - * ".copilot/prompts" : false, - * "/Users/legomushroom/repos/prompts" : true, - * }, - * } - * ``` - * - * See the next section for details on how we treat the config value. - * - * ### Possible Values - * - * - `undefined`/`null`: feature is disabledx - * - `object`: - * - expects the { "string": `boolean` } pairs, where the `string` is a path and the `boolean` - * is a flag that defines if this additional source folder is enabled or disabled; - * enabled source folders are used in addition to the default {@link DEFAULT_SOURCE_FOLDER} path; - * you can explicitly disable the default source folder by setting it to `false` in the object - * - value of a record in the object can also be a `string`: - * - if the string can be clearly mapped to a `boolean` (e.g., `"true"`, `"FALSE", "TrUe"`, etc.), - * it is treated as `boolean` value - * - any other string value is treated as `false` and is effectively ignored - * - if the record `key` is an `empty` string, it is ignored - * - if the resulting object is empty, the feature is considered `enabled`, prompt files source - * folders fallback to the default {@link DEFAULT_SOURCE_FOLDER} path - * - if the resulting object is not empty, and the default {@link DEFAULT_SOURCE_FOLDER} path - * is not explicitly disabled, it is added to the list of prompt files source folders - * * ### File Paths Resolution * * We resolve only `*.prompt.md` files inside the resulting source folders. Relative paths are resolved @@ -67,23 +28,46 @@ import { IConfigurationService } from '../../configuration/common/configuration. */ export namespace PromptsConfig { /** - * Configuration key for the `prompt files` feature (also - * known as `prompt files`, `prompt instructions`, etc.). + * Configuration key for the `reusable prompts` feature + * (also known as `prompt files`, `prompt instructions`, etc.). */ export const CONFIG_KEY: string = 'chat.promptFiles'; + /** + * Configuration key for the locations of reusable prompt files. + */ + export const LOCATIONS_CONFIG_KEY: string = 'chat.promptFilesLocations'; + /** * Default reusable prompt files source folder. */ export const DEFAULT_SOURCE_FOLDER = '.github/prompts'; /** - * Get value of the `prompt files` configuration setting. + * Checks if the feature is enabled. + * @see {@link CONFIG_KEY}. + */ + export const enabled = ( + configService: IConfigurationService, + ): boolean => { + const enabledValue = configService.getValue(CONFIG_KEY); + + return asBoolean(enabledValue) ?? false; + }; + + /** + * Context key expression for the `reusable prompts` feature `enabled` status. + */ + export const enabledCtx = ContextKeyExpr.equals(`config.${CONFIG_KEY}`, true); + + /** + * Get value of the `reusable prompt locations` configuration setting. + * @see {@link LOCATIONS_CONFIG_KEY}. */ - export const getValue = ( + export const getLocationsValue = ( configService: IConfigurationService, ): Record | undefined => { - const configValue = configService.getValue(CONFIG_KEY); + const configValue = configService.getValue(LOCATIONS_CONFIG_KEY); if (configValue === undefined || configValue === null || Array.isArray(configValue)) { return undefined; @@ -111,17 +95,6 @@ export namespace PromptsConfig { return undefined; }; - /** - * Checks if the feature is enabled. - */ - export const enabled = ( - configService: IConfigurationService, - ): boolean => { - const value = getValue(configService); - - return value !== undefined; - }; - /** * Gets list of source folders for prompt files. * Defaults to {@link DEFAULT_SOURCE_FOLDER}. @@ -129,7 +102,7 @@ export namespace PromptsConfig { export const promptSourceFolders = ( configService: IConfigurationService, ): string[] => { - const value = getValue(configService); + const value = getLocationsValue(configService); // note! the `value &&` part handles the `undefined`, `null`, and `false` cases if (value && (typeof value === 'object')) { diff --git a/src/vs/platform/prompts/test/common/config.test.ts b/src/vs/platform/prompts/test/common/config.test.ts index 672ae2c74c0e..d79145e6588f 100644 --- a/src/vs/platform/prompts/test/common/config.test.ts +++ b/src/vs/platform/prompts/test/common/config.test.ts @@ -16,10 +16,14 @@ import { IConfigurationOverrides, IConfigurationService } from '../../../configu const createMock = (value: T): IConfigurationService => { return mockService({ getValue(key?: string | IConfigurationOverrides) { - assert.strictEqual( - key, - PromptsConfig.CONFIG_KEY, - `Mocked service supports only one configuration key: '${PromptsConfig.CONFIG_KEY}'.`, + assert( + typeof key === 'string', + `Expected string configuration key, got '${typeof key}'.`, + ); + + assert( + [PromptsConfig.CONFIG_KEY, PromptsConfig.LOCATIONS_CONFIG_KEY].includes(key), + `Unsupported configuration key '${key}'.`, ); return value; @@ -30,12 +34,12 @@ const createMock = (value: T): IConfigurationService => { suite('PromptsConfig', () => { ensureNoDisposablesAreLeakedInTestSuite(); - suite('• getValue', () => { + suite('• getLocationsValue', () => { test('• undefined', () => { const configService = createMock(undefined); assert.strictEqual( - PromptsConfig.getValue(configService), + PromptsConfig.getLocationsValue(configService), undefined, 'Must read correct value.', ); @@ -45,7 +49,7 @@ suite('PromptsConfig', () => { const configService = createMock(null); assert.strictEqual( - PromptsConfig.getValue(configService), + PromptsConfig.getLocationsValue(configService), undefined, 'Must read correct value.', ); @@ -54,7 +58,7 @@ suite('PromptsConfig', () => { suite('• object', () => { test('• empty', () => { assert.deepStrictEqual( - PromptsConfig.getValue(createMock({})), + PromptsConfig.getLocationsValue(createMock({})), {}, 'Must read correct value.', ); @@ -62,7 +66,7 @@ suite('PromptsConfig', () => { test('• only valid strings', () => { assert.deepStrictEqual( - PromptsConfig.getValue(createMock({ + PromptsConfig.getLocationsValue(createMock({ '/root/.bashrc': true, '../../folder/.hidden-folder/config.xml': true, '/srv/www/Public_html/.htaccess': true, @@ -96,7 +100,7 @@ suite('PromptsConfig', () => { test('• filters out non valid entries', () => { assert.deepStrictEqual( - PromptsConfig.getValue(createMock({ + PromptsConfig.getLocationsValue(createMock({ '/etc/hosts.backup': '\t\n\t', './run.tests.sh': '\v', '../assets/img/logo.v2.png': true, @@ -135,7 +139,7 @@ suite('PromptsConfig', () => { test('• only invalid or false values', () => { assert.deepStrictEqual( - PromptsConfig.getValue(createMock({ + PromptsConfig.getLocationsValue(createMock({ '/etc/hosts.backup': '\t\n\t', './run.tests.sh': '\v', '../assets/IMG/logo.v2.png': '', diff --git a/src/vs/platform/protocol/electron-main/protocolMainService.ts b/src/vs/platform/protocol/electron-main/protocolMainService.ts index 86e0a616af8a..2f0c61b8a81d 100644 --- a/src/vs/platform/protocol/electron-main/protocolMainService.ts +++ b/src/vs/platform/protocol/electron-main/protocolMainService.ts @@ -5,7 +5,7 @@ import { session } from 'electron'; import { Disposable, IDisposable, toDisposable } from '../../../base/common/lifecycle.js'; -import { COI, FileAccess, Schemas } from '../../../base/common/network.js'; +import { COI, FileAccess, Schemas, CacheControlheaders } from '../../../base/common/network.js'; import { basename, extname, normalize } from '../../../base/common/path.js'; import { isLinux } from '../../../base/common/platform.js'; import { TernarySearchTree } from '../../../base/common/ternarySearchTree.js'; @@ -104,6 +104,15 @@ export class ProtocolMainService extends Disposable implements IProtocolMainServ } } + // In OSS, evict resources from the memory cache in the renderer process + // Refs https://github.com/microsoft/vscode/issues/148541#issuecomment-2670891511 + if (!this.environmentService.isBuilt) { + headers = { + ...headers, + ...CacheControlheaders + }; + } + // first check by validRoots if (this.validRoots.findSubstr(path)) { return callback({ path, headers }); @@ -111,7 +120,7 @@ export class ProtocolMainService extends Disposable implements IProtocolMainServ // then check by validExtensions if (this.validExtensions.has(extname(path).toLowerCase())) { - return callback({ path }); + return callback({ path, headers }); } // finally block to load the resource diff --git a/src/vs/platform/quickinput/browser/quickInput.ts b/src/vs/platform/quickinput/browser/quickInput.ts index 1ea9c37ab5fc..2324a1e3a761 100644 --- a/src/vs/platform/quickinput/browser/quickInput.ts +++ b/src/vs/platform/quickinput/browser/quickInput.ts @@ -1296,7 +1296,7 @@ export class QuickInputHoverDelegate extends WorkbenchHoverDelegate { @IConfigurationService configurationService: IConfigurationService, @IHoverService hoverService: IHoverService ) { - super('element', false, (options) => this.getOverrideOptions(options), configurationService, hoverService); + super('element', undefined, (options) => this.getOverrideOptions(options), configurationService, hoverService); } private getOverrideOptions(options: IHoverDelegateOptions): Partial { diff --git a/src/vs/platform/quickinput/browser/quickInputController.ts b/src/vs/platform/quickinput/browser/quickInputController.ts index afed745b58e2..47b007a41931 100644 --- a/src/vs/platform/quickinput/browser/quickInputController.ts +++ b/src/vs/platform/quickinput/browser/quickInputController.ts @@ -17,21 +17,21 @@ import { Disposable, DisposableStore, dispose } from '../../../base/common/lifec import Severity from '../../../base/common/severity.js'; import { isString } from '../../../base/common/types.js'; import { localize } from '../../../nls.js'; -import { IInputBox, IInputOptions, IKeyMods, IPickOptions, IQuickInput, IQuickInputButton, IQuickNavigateConfiguration, IQuickPick, IQuickPickItem, IQuickWidget, QuickInputHideReason, QuickPickInput, QuickPickFocus } from '../common/quickInput.js'; +import { IInputBox, IInputOptions, IKeyMods, IPickOptions, IQuickInput, IQuickInputButton, IQuickNavigateConfiguration, IQuickPick, IQuickPickItem, IQuickWidget, QuickInputHideReason, QuickPickInput, QuickPickFocus, QuickInputType } from '../common/quickInput.js'; import { QuickInputBox } from './quickInputBox.js'; import { QuickInputUI, Writeable, IQuickInputStyles, IQuickInputOptions, QuickPick, backButton, InputBox, Visibilities, QuickWidget, InQuickInputContextKey, QuickInputTypeContextKey, EndOfQuickInputBoxContextKey, QuickInputAlignmentContextKey } from './quickInput.js'; import { ILayoutService } from '../../layout/browser/layoutService.js'; import { mainWindow } from '../../../base/browser/window.js'; import { IInstantiationService } from '../../instantiation/common/instantiation.js'; import { QuickInputTree } from './quickInputTree.js'; -import { IContextKeyService } from '../../contextkey/common/contextkey.js'; +import { IContextKey, IContextKeyService } from '../../contextkey/common/contextkey.js'; import './quickInputActions.js'; import { autorun, observableValue } from '../../../base/common/observable.js'; import { StandardMouseEvent } from '../../../base/browser/mouseEvent.js'; import { IStorageService, StorageScope, StorageTarget } from '../../storage/common/storage.js'; import { IConfigurationService } from '../../configuration/common/configuration.js'; import { Platform, platform } from '../../../base/common/platform.js'; -import { getTitleBarStyle, TitlebarStyle } from '../../window/common/window.js'; +import { getWindowControlsStyle, WindowControlsStyle } from '../../window/common/window.js'; import { getZoomFactor } from '../../../base/browser/browser.js'; const $ = dom.$; @@ -75,18 +75,23 @@ export class QuickInputController extends Disposable { private viewState: QuickInputViewState | undefined; private dndController: QuickInputDragAndDropController | undefined; - private readonly inQuickInputContext = InQuickInputContextKey.bindTo(this.contextKeyService); - private readonly quickInputTypeContext = QuickInputTypeContextKey.bindTo(this.contextKeyService); - private readonly endOfQuickInputBoxContext = EndOfQuickInputBoxContextKey.bindTo(this.contextKeyService); + private readonly inQuickInputContext: IContextKey; + private readonly quickInputTypeContext: IContextKey; + private readonly endOfQuickInputBoxContext: IContextKey; constructor( private options: IQuickInputOptions, @ILayoutService private readonly layoutService: ILayoutService, @IInstantiationService private readonly instantiationService: IInstantiationService, - @IContextKeyService private readonly contextKeyService: IContextKeyService, + @IContextKeyService contextKeyService: IContextKeyService, @IStorageService private readonly storageService: IStorageService ) { super(); + + this.inQuickInputContext = InQuickInputContextKey.bindTo(contextKeyService); + this.quickInputTypeContext = QuickInputTypeContextKey.bindTo(contextKeyService); + this.endOfQuickInputBoxContext = EndOfQuickInputBoxContextKey.bindTo(contextKeyService); + this.idPrefix = options.idPrefix; this._container = options.container; this.styles = options.styles; @@ -911,7 +916,7 @@ class QuickInputDragAndDropController extends Disposable { private readonly _controlsOnLeft: boolean; private readonly _controlsOnRight: boolean; - private _quickInputAlignmentContext = QuickInputAlignmentContextKey.bindTo(this._contextKeyService); + private _quickInputAlignmentContext: IContextKey<'center' | 'top' | undefined>; constructor( private _container: HTMLElement, @@ -919,16 +924,17 @@ class QuickInputDragAndDropController extends Disposable { private _quickInputDragAreas: { node: HTMLElement; includeChildren: boolean }[], initialViewState: QuickInputViewState | undefined, @ILayoutService private readonly _layoutService: ILayoutService, - @IContextKeyService private readonly _contextKeyService: IContextKeyService, + @IContextKeyService contextKeyService: IContextKeyService, @IConfigurationService private readonly configurationService: IConfigurationService ) { super(); - const customTitleBar = getTitleBarStyle(this.configurationService) === TitlebarStyle.CUSTOM; + this._quickInputAlignmentContext = QuickInputAlignmentContextKey.bindTo(contextKeyService); + const customWindowControls = getWindowControlsStyle(this.configurationService) === WindowControlsStyle.CUSTOM; // Do not allow the widget to overflow or underflow window controls. // Use CSS calculations to avoid having to force layout with `.clientWidth` - this._controlsOnLeft = customTitleBar && platform === Platform.Mac; - this._controlsOnRight = customTitleBar && (platform === Platform.Windows || platform === Platform.Linux); + this._controlsOnLeft = customWindowControls && platform === Platform.Mac; + this._controlsOnRight = customWindowControls && (platform === Platform.Windows || platform === Platform.Linux); this._registerLayoutListener(); this.registerMouseListeners(); this.dndViewState.set({ ...initialViewState, done: true }, undefined); diff --git a/src/vs/platform/quickinput/browser/quickInputTree.ts b/src/vs/platform/quickinput/browser/quickInputTree.ts index 18dbc087992b..94f0488fc4ea 100644 --- a/src/vs/platform/quickinput/browser/quickInputTree.ts +++ b/src/vs/platform/quickinput/browser/quickInputTree.ts @@ -929,7 +929,7 @@ export class QuickInputTree extends Disposable { } private _registerHoverListeners() { - const delayer = this._register(new ThrottledDelayer(this.hoverDelegate.delay)); + const delayer = this._register(new ThrottledDelayer(typeof this.hoverDelegate.delay === 'function' ? this.hoverDelegate.delay() : this.hoverDelegate.delay)); this._register(this._tree.onMouseOver(async e => { // If we hover over an anchor element, we don't want to show the hover because // the anchor may have a tooltip that we want to show instead. diff --git a/src/vs/platform/remote/common/remote.ts b/src/vs/platform/remote/common/remote.ts new file mode 100644 index 000000000000..eba717a7eb1d --- /dev/null +++ b/src/vs/platform/remote/common/remote.ts @@ -0,0 +1,6 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export const REMOTE_DEFAULT_IF_LOCAL_EXTENSIONS = 'remote.defaultExtensionsIfInstalledLocally'; diff --git a/src/vs/platform/secrets/test/common/testSecretStorageService.ts b/src/vs/platform/secrets/test/common/testSecretStorageService.ts new file mode 100644 index 000000000000..ec7482e72bb2 --- /dev/null +++ b/src/vs/platform/secrets/test/common/testSecretStorageService.ts @@ -0,0 +1,36 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Emitter } from '../../../../base/common/event.js'; +import { ISecretStorageService } from '../../common/secrets.js'; + +export class TestSecretStorageService implements ISecretStorageService { + declare readonly _serviceBrand: undefined; + + private readonly _storage = new Map(); + private readonly _onDidChangeSecretEmitter = new Emitter(); + readonly onDidChangeSecret = this._onDidChangeSecretEmitter.event; + + type = 'in-memory' as const; + + async get(key: string): Promise { + return this._storage.get(key); + } + + async set(key: string, value: string): Promise { + this._storage.set(key, value); + this._onDidChangeSecretEmitter.fire(key); + } + + async delete(key: string): Promise { + this._storage.delete(key); + this._onDidChangeSecretEmitter.fire(key); + } + + // Helper method for tests to clear all secrets + clear(): void { + this._storage.clear(); + } +} diff --git a/src/vs/platform/shell/node/shellEnv.ts b/src/vs/platform/shell/node/shellEnv.ts index 0fde4b76e6e2..8f4fbf6c4950 100644 --- a/src/vs/platform/shell/node/shellEnv.ts +++ b/src/vs/platform/shell/node/shellEnv.ts @@ -129,7 +129,7 @@ async function doResolveUnixShellEnv(logService: ILogService, token: Cancellatio const name = basename(systemShellUnix); let command: string, shellArgs: Array; const extraArgs = ''; - if (/^(?:pwsh(?:-preview)?|powershell)$/.test(name)) { + if (/^(?:pwsh|powershell)(?:-preview)?$/.test(name)) { // Older versions of PowerShell removes double quotes sometimes so we use "double single quotes" which is how // you escape single quotes inside of a single quoted string. command = `& '${process.execPath}' ${extraArgs} -p '''${mark}'' + JSON.stringify(process.env) + ''${mark}'''`; diff --git a/src/vs/platform/state/node/stateService.ts b/src/vs/platform/state/node/stateService.ts index 77f7c45bc118..ef71d504bf68 100644 --- a/src/vs/platform/state/node/stateService.ts +++ b/src/vs/platform/state/node/stateService.ts @@ -25,18 +25,20 @@ export class FileStorage extends Disposable { private storage: StorageDatabase = Object.create(null); private lastSavedStorageContents = ''; - private readonly flushDelayer = this._register(new ThrottledDelayer(this.saveStrategy === SaveStrategy.IMMEDIATE ? 0 : 100 /* buffer saves over a short time */)); + private readonly flushDelayer: ThrottledDelayer; private initializing: Promise | undefined = undefined; private closing: Promise | undefined = undefined; constructor( private readonly storagePath: URI, - private readonly saveStrategy: SaveStrategy, + saveStrategy: SaveStrategy, private readonly logService: ILogService, private readonly fileService: IFileService, ) { super(); + + this.flushDelayer = this._register(new ThrottledDelayer(saveStrategy === SaveStrategy.IMMEDIATE ? 0 : 100 /* buffer saves over a short time */)); } init(): Promise { diff --git a/src/vs/platform/storage/common/storage.ts b/src/vs/platform/storage/common/storage.ts index b120f38a2963..d67cdc476805 100644 --- a/src/vs/platform/storage/common/storage.ts +++ b/src/vs/platform/storage/common/storage.ts @@ -328,11 +328,13 @@ export abstract class AbstractStorageService extends Disposable implements IStor private initializationPromise: Promise | undefined; - private readonly flushWhenIdleScheduler = this._register(new RunOnceScheduler(() => this.doFlushWhenIdle(), this.options.flushInterval)); + private readonly flushWhenIdleScheduler: RunOnceScheduler; private readonly runFlushWhenIdle = this._register(new MutableDisposable()); - constructor(private readonly options: IStorageServiceOptions = { flushInterval: AbstractStorageService.DEFAULT_FLUSH_INTERVAL }) { + constructor(options: IStorageServiceOptions = { flushInterval: AbstractStorageService.DEFAULT_FLUSH_INTERVAL }) { super(); + + this.flushWhenIdleScheduler = this._register(new RunOnceScheduler(() => this.doFlushWhenIdle(), options.flushInterval)); } onDidChangeValue(scope: StorageScope.WORKSPACE, key: string | undefined, disposable: DisposableStore): Event; diff --git a/src/vs/platform/storage/common/storageService.ts b/src/vs/platform/storage/common/storageService.ts index c7181a897057..1b6d567fe3e0 100644 --- a/src/vs/platform/storage/common/storageService.ts +++ b/src/vs/platform/storage/common/storageService.ts @@ -17,24 +17,33 @@ import { IAnyWorkspaceIdentifier } from '../../workspace/common/workspace.js'; export class RemoteStorageService extends AbstractStorageService { - private readonly applicationStorageProfile = this.initialProfiles.defaultProfile; - private readonly applicationStorage = this.createApplicationStorage(); + private readonly applicationStorageProfile: IUserDataProfile; + private readonly applicationStorage: IStorage; - private profileStorageProfile = this.initialProfiles.currentProfile; + private profileStorageProfile: IUserDataProfile; private readonly profileStorageDisposables = this._register(new DisposableStore()); - private profileStorage = this.createProfileStorage(this.profileStorageProfile); + private profileStorage: IStorage; - private workspaceStorageId = this.initialWorkspace?.id; + private workspaceStorageId: string | undefined; private readonly workspaceStorageDisposables = this._register(new DisposableStore()); - private workspaceStorage = this.createWorkspaceStorage(this.initialWorkspace); + private workspaceStorage: IStorage | undefined; constructor( - private readonly initialWorkspace: IAnyWorkspaceIdentifier | undefined, - private readonly initialProfiles: { defaultProfile: IUserDataProfile; currentProfile: IUserDataProfile }, + initialWorkspace: IAnyWorkspaceIdentifier | undefined, + initialProfiles: { defaultProfile: IUserDataProfile; currentProfile: IUserDataProfile }, private readonly remoteService: IRemoteService, private readonly environmentService: IEnvironmentService ) { super(); + + this.applicationStorageProfile = initialProfiles.defaultProfile; + this.applicationStorage = this.createApplicationStorage(); + + this.profileStorageProfile = initialProfiles.currentProfile; + this.profileStorage = this.createProfileStorage(this.profileStorageProfile); + + this.workspaceStorageId = initialWorkspace?.id; + this.workspaceStorage = this.createWorkspaceStorage(initialWorkspace); } private createApplicationStorage(): IStorage { diff --git a/src/vs/platform/storage/electron-main/storageMainService.ts b/src/vs/platform/storage/electron-main/storageMainService.ts index 94664c7eaef5..d46df4368623 100644 --- a/src/vs/platform/storage/electron-main/storageMainService.ts +++ b/src/vs/platform/storage/electron-main/storageMainService.ts @@ -91,6 +91,8 @@ export class StorageMainService extends Disposable implements IStorageMainServic ) { super(); + this.applicationStorage = this._register(this.createApplicationStorage()); + this.registerListeners(); } @@ -163,7 +165,7 @@ export class StorageMainService extends Disposable implements IStorageMainServic //#region Application Storage - readonly applicationStorage = this._register(this.createApplicationStorage()); + readonly applicationStorage: IStorageMain; private createApplicationStorage(): IStorageMain { this.logService.trace(`StorageMainService: creating application storage`); @@ -332,13 +334,15 @@ export class ApplicationStorageMainService extends AbstractStorageService implem declare readonly _serviceBrand: undefined; - readonly whenReady = this.storageMainService.applicationStorage.whenInit; + readonly whenReady: Promise; constructor( @IUserDataProfilesService private readonly userDataProfilesService: IUserDataProfilesService, @IStorageMainService private readonly storageMainService: IStorageMainService ) { super(); + + this.whenReady = this.storageMainService.applicationStorage.whenInit; } protected doInitialize(): Promise { diff --git a/src/vs/platform/telemetry/common/telemetryService.ts b/src/vs/platform/telemetry/common/telemetryService.ts index 51fbb8fa7539..a41dd8814715 100644 --- a/src/vs/platform/telemetry/common/telemetryService.ts +++ b/src/vs/platform/telemetry/common/telemetryService.ts @@ -176,11 +176,11 @@ function getTelemetryLevelSettingDescription(): string { const telemetryTableDescription = localize('telemetry.telemetryLevel.tableDescription', "The following table outlines the data sent with each setting:"); const telemetryTable = ` | | ${crashReportsHeader} | ${errorsHeader} | ${usageHeader} | -|:------|:---------------------:|:---------------:|:--------------:| -| all | ✓ | ✓ | ✓ | -| error | ✓ | ✓ | - | -| crash | ✓ | - | - | -| off | - | - | - | +|:------|:-------------:|:---------------:|:----------:| +| all | ✓ | ✓ | ✓ | +| error | ✓ | ✓ | - | +| crash | ✓ | - | - | +| off | - | - | - | `; const deprecatedSettingNote = localize('telemetry.telemetryLevel.deprecated', "****Note:*** If this setting is 'off', no telemetry will be sent regardless of other telemetry settings. If this setting is set to anything except 'off' and telemetry is disabled with deprecated settings, no telemetry will be sent.*"); @@ -200,7 +200,8 @@ ${deprecatedSettingNote} return telemetryDescription; } -Registry.as(Extensions.Configuration).registerConfiguration({ +const configurationRegistry = Registry.as(Extensions.Configuration); +configurationRegistry.registerConfiguration({ 'id': TELEMETRY_SECTION_ID, 'order': 1, 'type': 'object', @@ -219,18 +220,23 @@ Registry.as(Extensions.Configuration).registerConfigurat 'default': TelemetryConfiguration.ON, 'restricted': true, 'scope': ConfigurationScope.APPLICATION, - 'tags': ['usesOnlineServices', 'telemetry'] - } - } -}); - -// Deprecated telemetry setting -Registry.as(Extensions.Configuration).registerConfiguration({ - 'id': TELEMETRY_SECTION_ID, - 'order': 110, - 'type': 'object', - 'title': localize('telemetryConfigurationTitle', "Telemetry"), - 'properties': { + 'tags': ['usesOnlineServices', 'telemetry'], + 'policy': { + name: 'TelemetryLevel', + minimumVersion: '1.99', + description: localize('telemetry.telemetryLevel.policyDescription', "Controls the level of telemetry."), + } + }, + 'telemetry.disableFeedback': { + type: 'boolean', + default: false, + description: localize('telemetry.disableFeedback', "Disable feedback options."), + policy: { + name: 'DisableFeedback', + minimumVersion: '1.99', + } + }, + // Deprecated telemetry setting [TELEMETRY_OLD_SETTING_ID]: { 'type': 'boolean', 'markdownDescription': @@ -243,6 +249,5 @@ Registry.as(Extensions.Configuration).registerConfigurat 'scope': ConfigurationScope.APPLICATION, 'tags': ['usesOnlineServices', 'telemetry'] } - } + }, }); - diff --git a/src/vs/platform/terminal/common/capabilities/capabilities.ts b/src/vs/platform/terminal/common/capabilities/capabilities.ts index c3924a37fb99..17b2429ff8fa 100644 --- a/src/vs/platform/terminal/common/capabilities/capabilities.ts +++ b/src/vs/platform/terminal/common/capabilities/capabilities.ts @@ -152,14 +152,39 @@ export interface ICwdDetectionCapability { export interface IShellEnvDetectionCapability { readonly type: TerminalCapability.ShellEnvDetection; - readonly onDidChangeEnv: Event>; - get env(): Map; + readonly onDidChangeEnv: Event; + get env(): TerminalShellIntegrationEnvironment; setEnvironment(envs: { [key: string]: string | undefined } | undefined, isTrusted: boolean): void; - startEnvironmentSingleVar(isTrusted: boolean): void; + startEnvironmentSingleVar(clear: boolean, isTrusted: boolean): void; setEnvironmentSingleVar(key: string, value: string | undefined, isTrusted: boolean): void; deleteEnvironmentSingleVar(key: string, value: string | undefined, isTrusted: boolean): void; endEnvironmentSingleVar(isTrusted: boolean): void; - applyEnvironmentDiff(env: Map, isTrusted: boolean): void; +} + +export interface TerminalShellIntegrationEnvironment { + /** + * The dictionary of environment variables. + */ + value: { [key: string]: string | undefined } | undefined; + + /** + * Whether the environment came from a trusted source and is therefore safe to use its + * values in a manner that could lead to execution of arbitrary code. If this value is + * `false`, {@link value} should either not be used for something that could lead to arbitrary + * code execution, or the user should be warned beforehand. + * + * This is `true` only when the environment was reported explicitly and it used a nonce for + * verification. + */ + isTrusted: boolean; +} + +export interface TerminalShellIntegration { + /** + * The environment of the shell process. This is undefined if the shell integration script + * does not send the environment. + */ + readonly env: TerminalShellIntegrationEnvironment; } export const enum CommandInvalidationReason { @@ -189,12 +214,14 @@ export interface ICommandDetectionCapability { readonly executingCommandConfidence: 'low' | 'medium' | 'high' | undefined; /** The current cwd at the cursor's position. */ readonly cwd: string | undefined; + readonly hasRichCommandDetection: boolean; readonly currentCommand: ICurrentPartialCommand | undefined; readonly onCommandStarted: Event; readonly onCommandFinished: Event; readonly onCommandExecuted: Event; readonly onCommandInvalidated: Event; readonly onCurrentCommandInvalidated: Event; + readonly onSetRichCommandDetection: Event; setContinuationPrompt(value: string): void; setPromptTerminator(value: string, lastPromptLine: string): void; setCwd(value: string): void; @@ -214,6 +241,7 @@ export interface ICommandDetectionCapability { handleCommandStart(options?: IHandleCommandOptions): void; handleCommandExecuted(options?: IHandleCommandOptions): void; handleCommandFinished(exitCode?: number, options?: IHandleCommandOptions): void; + setHasRichCommandDetection(value: boolean): void; /** * Set the command line explicitly. * @param commandLine The command line being set. @@ -320,6 +348,7 @@ export interface IMarkProperties { } export interface ISerializedCommandDetectionCapability { isWindowsPty: boolean; + hasRichCommandDetection: boolean; commands: ISerializedTerminalCommand[]; promptInputModel: ISerializedPromptInputModel | undefined; } diff --git a/src/vs/platform/terminal/common/capabilities/commandDetection/promptInputModel.ts b/src/vs/platform/terminal/common/capabilities/commandDetection/promptInputModel.ts index e90a476fa677..b04874d672de 100644 --- a/src/vs/platform/terminal/common/capabilities/commandDetection/promptInputModel.ts +++ b/src/vs/platform/terminal/common/capabilities/commandDetection/promptInputModel.ts @@ -105,6 +105,7 @@ export class PromptInputModel extends Disposable implements IPromptInputModel { constructor( private readonly _xterm: Terminal, onCommandStart: Event, + onCommandStartChanged: Event, onCommandExecuted: Event, @ILogService private readonly _logService: ILogService ) { @@ -118,6 +119,7 @@ export class PromptInputModel extends Disposable implements IPromptInputModel { this._register(this._xterm.onData(e => this._handleUserInput(e))); this._register(onCommandStart(e => this._handleCommandStart(e as { marker: IMarker }))); + this._register(onCommandStartChanged(() => this._handleCommandStartChanged())); this._register(onCommandExecuted(() => this._handleCommandExecuted())); this._register(this.onDidStartInput(() => this._logCombinedStringIfTrace('PromptInputModel#onDidStartInput'))); @@ -215,6 +217,16 @@ export class PromptInputModel extends Disposable implements IPromptInputModel { } } + private _handleCommandStartChanged() { + if (this._state !== PromptInputState.Input) { + return; + } + + this._commandStartX = this._xterm.buffer.active.cursorX; + this._onDidChangeInput.fire(this._createStateObject()); + this._sync(); + } + private _handleCommandExecuted() { if (this._state === PromptInputState.Execute) { return; diff --git a/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts b/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts index d2413b5897ea..7237d1270d09 100644 --- a/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts +++ b/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts @@ -33,8 +33,8 @@ export class CommandDetectionCapability extends Disposable implements ICommandDe private _dimensions: ITerminalDimensions; private __isCommandStorageDisabled: boolean = false; private _handleCommandStartOptions?: IHandleCommandOptions; - - private _commitCommandFinished?: RunOnceScheduler; + private _hasRichCommandDetection: boolean = false; + get hasRichCommandDetection() { return this._hasRichCommandDetection; } private _ptyHeuristicsHooks: ICommandDetectionHeuristicsHooks; private readonly _ptyHeuristics: MandatoryMutableDisposable; @@ -61,6 +61,8 @@ export class CommandDetectionCapability extends Disposable implements ICommandDe private readonly _onCommandStarted = this._register(new Emitter()); readonly onCommandStarted = this._onCommandStarted.event; + private readonly _onCommandStartChanged = this._register(new Emitter()); + readonly onCommandStartChanged = this._onCommandStartChanged.event; private readonly _onBeforeCommandFinished = this._register(new Emitter()); readonly onBeforeCommandFinished = this._onBeforeCommandFinished.event; private readonly _onCommandFinished = this._register(new Emitter()); @@ -71,6 +73,8 @@ export class CommandDetectionCapability extends Disposable implements ICommandDe readonly onCommandInvalidated = this._onCommandInvalidated.event; private readonly _onCurrentCommandInvalidated = this._register(new Emitter()); readonly onCurrentCommandInvalidated = this._onCurrentCommandInvalidated.event; + private readonly _onSetRichCommandDetection = this._register(new Emitter()); + readonly onSetRichCommandDetection = this._onSetRichCommandDetection.event; constructor( private readonly _terminal: Terminal, @@ -78,7 +82,7 @@ export class CommandDetectionCapability extends Disposable implements ICommandDe ) { super(); - this._promptInputModel = this._register(new PromptInputModel(this._terminal, this.onCommandStarted, this.onCommandExecuted, this._logService)); + this._promptInputModel = this._register(new PromptInputModel(this._terminal, this.onCommandStarted, this.onCommandStartChanged, this.onCommandExecuted, this._logService)); // Pull command line from the buffer if it was not set explicitly this._register(this.onCommandExecuted(command => { @@ -128,10 +132,6 @@ export class CommandDetectionCapability extends Disposable implements ICommandDe get commandMarkers() { return that._commandMarkers; } set commandMarkers(value) { that._commandMarkers = value; } get clearCommandsInViewport() { return that._clearCommandsInViewport.bind(that); } - commitCommandFinished() { - that._commitCommandFinished?.flush(); - that._commitCommandFinished = undefined; - } }; this._ptyHeuristics = this._register(new MandatoryMutableDisposable(new UnixPtyHeuristics(this._terminal, this, this._ptyHeuristicsHooks, this._logService))); @@ -219,10 +219,6 @@ export class CommandDetectionCapability extends Disposable implements ICommandDe get commandMarkers() { return that._commandMarkers; } set commandMarkers(value) { that._commandMarkers = value; } get clearCommandsInViewport() { return that._clearCommandsInViewport.bind(that); } - commitCommandFinished() { - that._commitCommandFinished?.flush(); - that._commitCommandFinished = undefined; - } }, this._logService ); @@ -231,6 +227,11 @@ export class CommandDetectionCapability extends Disposable implements ICommandDe } } + setHasRichCommandDetection(value: boolean): void { + this._hasRichCommandDetection = value; + this._onSetRichCommandDetection.fire(value); + } + setIsCommandStorageDisabled(): void { this.__isCommandStorageDisabled = true; } @@ -330,6 +331,7 @@ export class CommandDetectionCapability extends Disposable implements ICommandDe this._currentCommand.commandStartMarker = options?.marker || this._currentCommand.commandStartMarker; if (this._currentCommand.commandStartMarker?.line === this._terminal.buffer.active.cursorY) { this._currentCommand.commandStartX = this._terminal.buffer.active.cursorX; + this._onCommandStartChanged.fire(); this._logService.debug('CommandDetectionCapability#handleCommandStart', this._currentCommand.commandStartX, this._currentCommand.commandStartMarker?.line); return; } @@ -381,14 +383,11 @@ export class CommandDetectionCapability extends Disposable implements ICommandDe if (newCommand) { this._commands.push(newCommand); - this._commitCommandFinished = new RunOnceScheduler(() => { - this._onBeforeCommandFinished.fire(newCommand); - if (!this._currentCommand.isInvalid) { - this._logService.debug('CommandDetectionCapability#onCommandFinished', newCommand); - this._onCommandFinished.fire(newCommand); - } - }, 50); - this._commitCommandFinished.schedule(); + this._onBeforeCommandFinished.fire(newCommand); + if (!this._currentCommand.isInvalid) { + this._logService.debug('CommandDetectionCapability#onCommandFinished', newCommand); + this._onCommandFinished.fire(newCommand); + } } this._currentCommand = new PartialTerminalCommand(this._terminal); this._handleCommandStartOptions = undefined; @@ -413,6 +412,7 @@ export class CommandDetectionCapability extends Disposable implements ICommandDe } return { isWindowsPty: this._ptyHeuristics.value instanceof WindowsPtyHeuristics, + hasRichCommandDetection: this._hasRichCommandDetection, commands, promptInputModel: this._promptInputModel.serialize(), }; @@ -422,6 +422,9 @@ export class CommandDetectionCapability extends Disposable implements ICommandDe if (serialized.isWindowsPty) { this.setIsWindowsPty(serialized.isWindowsPty); } + if (serialized.hasRichCommandDetection) { + this.setHasRichCommandDetection(serialized.hasRichCommandDetection); + } const buffer = this._terminal.buffer.normal; for (const e of serialized.commands) { // Partial command @@ -470,7 +473,6 @@ interface ICommandDetectionHeuristicsHooks { commandMarkers: IMarker[]; clearCommandsInViewport(): void; - commitCommandFinished(): void; } type IPtyHeuristics = ( @@ -502,8 +504,6 @@ class UnixPtyHeuristics extends Disposable { } handleCommandStart(options?: IHandleCommandOptions) { - this._hooks.commitCommandFinished(); - const currentCommand = this._capability.currentCommand; currentCommand.commandStartX = this._terminal.buffer.active.cursorX; currentCommand.commandStartMarker = options?.marker || this._terminal.registerMarker(0); @@ -758,8 +758,6 @@ class WindowsPtyHeuristics extends Disposable { this._tryAdjustCommandStartMarkerScheduler = undefined; } - this._hooks.commitCommandFinished(); - if (!this._capability.currentCommand.commandExecutedMarker) { this._onCursorMoveListener.value = this._terminal.onCursorMove(() => { if (this._hooks.commandMarkers.length === 0 || this._hooks.commandMarkers[this._hooks.commandMarkers.length - 1].line !== this._terminal.buffer.active.cursorY) { diff --git a/src/vs/platform/terminal/common/capabilities/shellEnvDetectionCapability.ts b/src/vs/platform/terminal/common/capabilities/shellEnvDetectionCapability.ts index d27e2d3eec33..d701509efb4c 100644 --- a/src/vs/platform/terminal/common/capabilities/shellEnvDetectionCapability.ts +++ b/src/vs/platform/terminal/common/capabilities/shellEnvDetectionCapability.ts @@ -4,100 +4,101 @@ *--------------------------------------------------------------------------------------------*/ import { Disposable } from '../../../../base/common/lifecycle.js'; -import { IShellEnvDetectionCapability, TerminalCapability } from './capabilities.js'; +import { IShellEnvDetectionCapability, TerminalCapability, TerminalShellIntegrationEnvironment } from './capabilities.js'; import { Emitter } from '../../../../base/common/event.js'; import { equals } from '../../../../base/common/objects.js'; +import { mapsStrictEqualIgnoreOrder } from '../../../../base/common/map.js'; + +export interface IShellEnv { + value: Map; + isTrusted: boolean; +} export class ShellEnvDetectionCapability extends Disposable implements IShellEnvDetectionCapability { readonly type = TerminalCapability.ShellEnvDetection; - private _pendingEnv: Map | undefined; - private _env: Map = new Map(); - get env(): Map { return this._env; } + private _pendingEnv: IShellEnv | undefined; + private _env: IShellEnv = { value: new Map(), isTrusted: true }; + + get env(): TerminalShellIntegrationEnvironment { + return this._createStateObject(); + } - private readonly _onDidChangeEnv = this._register(new Emitter>()); + private readonly _onDidChangeEnv = this._register(new Emitter()); readonly onDidChangeEnv = this._onDidChangeEnv.event; setEnvironment(env: { [key: string]: string | undefined }, isTrusted: boolean): void { - if (!isTrusted) { - return; - } - - if (equals(this._env, env)) { + if (equals(this.env.value, env)) { return; } - this._env.clear(); + this._env.value.clear(); for (const [key, value] of Object.entries(env)) { if (value !== undefined) { - this._env.set(key, value); + this._env.value.set(key, value); } } + this._env.isTrusted = isTrusted; - // Convert to event and fire event - this._onDidChangeEnv.fire(this._env); + this._fireEnvChange(); } - startEnvironmentSingleVar(isTrusted: boolean): void { - if (!isTrusted) { - return; + startEnvironmentSingleVar(clear: boolean, isTrusted: boolean): void { + if (clear) { + this._pendingEnv = { + value: new Map(), + isTrusted + }; + } else { + this._pendingEnv = { + value: new Map(this._env.value), + isTrusted: this._env.isTrusted && isTrusted + }; } - this._pendingEnv = new Map(); + } + setEnvironmentSingleVar(key: string, value: string | undefined, isTrusted: boolean): void { - if (!isTrusted) { + if (!this._pendingEnv) { return; } if (key !== undefined && value !== undefined) { - this._pendingEnv?.set(key, value); + this._pendingEnv.value.set(key, value); + this._pendingEnv.isTrusted &&= isTrusted; } } + endEnvironmentSingleVar(isTrusted: boolean): void { - if (!isTrusted) { - return; - } if (!this._pendingEnv) { return; } - this.applyEnvironmentDiff(this._pendingEnv, isTrusted); + this._pendingEnv.isTrusted &&= isTrusted; + const envDiffers = !mapsStrictEqualIgnoreOrder(this._env.value, this._pendingEnv.value); + if (envDiffers) { + this._env = this._pendingEnv; + this._fireEnvChange(); + } this._pendingEnv = undefined; } deleteEnvironmentSingleVar(key: string, value: string | undefined, isTrusted: boolean): void { - if (!isTrusted) { + if (!this._pendingEnv) { return; } if (key !== undefined && value !== undefined) { - this._env.delete(key); - this._pendingEnv?.delete(key); - this._onDidChangeEnv.fire(this._env); + this._pendingEnv.value.delete(key); + this._pendingEnv.isTrusted &&= isTrusted; } - return; } - // Make sure to update this.env to the latest, fire event if there is a diff - applyEnvironmentDiff(env: Map, isTrusted: boolean): void { - if (!isTrusted) { - return; - } - - let envDiffers: boolean = false; - - for (const [key, value] of env.entries()) { - if (this._env.has(key) && this._env.get(key) === value) { - // Do nothing - } else if (this.env.has(key) && value !== this.env.get(key)) { - this._env.set(key, value); - envDiffers = true; - } else if (!this.env.has(key)) { - this._env.set(key, value); - envDiffers = true; - } - } + private _fireEnvChange(): void { + this._onDidChangeEnv.fire(this._createStateObject()); + } - if (envDiffers) { - this._onDidChangeEnv.fire(this._env); - return; - } + private _createStateObject(): TerminalShellIntegrationEnvironment { + return { + value: Object.fromEntries(this._env.value), + isTrusted: this._env.isTrusted + }; } } diff --git a/src/vs/platform/terminal/common/terminal.ts b/src/vs/platform/terminal/common/terminal.ts index 2b985ab369e7..3f326233dc79 100644 --- a/src/vs/platform/terminal/common/terminal.ts +++ b/src/vs/platform/terminal/common/terminal.ts @@ -949,9 +949,11 @@ export type ITerminalProfileObject = ITerminalExecutable | ITerminalProfileSourc export interface IShellIntegration { readonly capabilities: ITerminalCapabilityStore; + readonly seenSequences: ReadonlySet; readonly status: ShellIntegrationStatus; readonly onDidChangeStatus: Event; + readonly onDidChangeSeenSequences: Event>; deserialize(serialized: ISerializedCommandDetectionCapability): void; } diff --git a/src/vs/platform/terminal/common/terminalRecorder.ts b/src/vs/platform/terminal/common/terminalRecorder.ts index 18026ca1ad76..1f924f3192ab 100644 --- a/src/vs/platform/terminal/common/terminalRecorder.ts +++ b/src/vs/platform/terminal/common/terminalRecorder.ts @@ -91,6 +91,7 @@ export class TerminalRecorder { // No command restoration is needed when relaunching terminals commands: { isWindowsPty: false, + hasRichCommandDetection: false, commands: [], promptInputModel: undefined, } diff --git a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts index 8fdf1a4b2d6c..f17dbb5be08e 100644 --- a/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts +++ b/src/vs/platform/terminal/common/xterm/shellIntegrationAddon.ts @@ -131,8 +131,10 @@ const enum VSCodeOscPt { CommandExecuted = 'C', /** - * Sent just after a command has finished. The exit code is optional, when not specified it - * means no command was run (ie. enter on empty prompt or ctrl+c). + * Sent just after a command has finished. This should generally be used on the new line + * following the end of a command's output, just before {@link PromptStart}. The exit code is + * optional, when not specified it means no command was run (ie. enter on empty prompt or + * ctrl+c). * * Format: `OSC 633 ; D [; ] ST` * @@ -204,11 +206,15 @@ const enum VSCodeOscPt { * Known properties: * * - `Cwd` - Reports the current working directory to the terminal. - * - `IsWindows` - Indicates whether the terminal is using a Windows backend like winpty or - * conpty. This may be used to enable additional heuristics as the positioning of the shell + * - `IsWindows` - Reports whether the shell is using a Windows backend like winpty or conpty. + * This may be used to enable additional heuristics as the positioning of the shell * integration sequences are not guaranteed to be correct. Valid values: `True`, `False`. * - `ContinuationPrompt` - Reports the continuation prompt that is printed at the start of * multi-line inputs. + * - `HasRichCommandDetection` - Reports whether the shell has rich command line detection, + * meaning that sequences A, B, C, D and E are exactly where they're meant to be. In + * particular, {@link CommandLine} must happen immediately before {@link CommandExecuted} so + * VS Code knows the command line when the execution begins. * * WARNING: Any other properties may be changed and are not guaranteed to work in the future. */ @@ -233,7 +239,8 @@ const enum VSCodeOscPt { * * - `Environment` - A stringified JSON object containing the shell's complete environment. The * variables and values use the same encoding rules as the {@link CommandLine} sequence. - * - `Nonce` - An _mandatory_ nonce to ensure the sequence is not malicious. + * - `Nonce` - An _mandatory_ nonce can be provided which may be required by the terminal in order + * to enable some features. This helps ensure no malicious command injection has occurred. * * WARNING: This sequence is unfinalized, DO NOT use this in your shell integration script. */ @@ -242,7 +249,10 @@ const enum VSCodeOscPt { /** * Delete a single environment variable from cached environment. * - * Format: `OSC 633 ; EnvSingleDelete ; ; ; ` + * Format: `OSC 633 ; EnvSingleDelete ; ; [; ]` + * + * - `Nonce` - An optional nonce can be provided which may be required by the terminal in order + * to enable some features. This helps ensure no malicious command injection has occurred. * * WARNING: This sequence is unfinalized, DO NOT use this in your shell integration script. */ @@ -250,9 +260,12 @@ const enum VSCodeOscPt { /** * The start of the collecting user's environment variables individually. - * Clears any environment residuals in previous sessions. * - * Format: `OSC 633 ; EnvSingleStart ; ` + * Format: `OSC 633 ; EnvSingleStart ; [; ]` + * + * - `Clear` - An _mandatory_ flag indicating any cached environment variables will be cleared. + * - `Nonce` - An optional nonce can be provided which may be required by the terminal in order + * to enable some features. This helps ensure no malicious command injection has occurred. * * WARNING: This sequence is unfinalized, DO NOT use this in your shell integration script. */ @@ -261,7 +274,10 @@ const enum VSCodeOscPt { /** * Sets an entry of single environment variable to transactional pending map of environment variables. * - * Format: `OSC 633 ; EnvSingleEntry ; ; ; ` + * Format: `OSC 633 ; EnvSingleEntry ; ; [; ]` + * + * - `Nonce` - An optional nonce can be provided which may be required by the terminal in order + * to enable some features. This helps ensure no malicious command injection has occurred. * * WARNING: This sequence is unfinalized, DO NOT use this in your shell integration script. */ @@ -271,7 +287,10 @@ const enum VSCodeOscPt { * The end of the collecting user's environment variables individually. * Clears any pending environment variables and fires an event that contains user's environment. * - * Format: `OSC 633 ; EnvSingleEnd ; ` + * Format: `OSC 633 ; EnvSingleEnd [; ]` + * + * - `Nonce` - An optional nonce can be provided which may be required by the terminal in order + * to enable some features. This helps ensure no malicious command injection has occurred. * * WARNING: This sequence is unfinalized, DO NOT use this in your shell integration script. */ @@ -308,12 +327,17 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati private _hasUpdatedTelemetry: boolean = false; private _activationTimeout: any; private _commonProtocolDisposables: IDisposable[] = []; - private _status: ShellIntegrationStatus = ShellIntegrationStatus.Off; + private _seenSequences: Set = new Set(); + get seenSequences(): ReadonlySet { return this._seenSequences; } + + private _status: ShellIntegrationStatus = ShellIntegrationStatus.Off; get status(): ShellIntegrationStatus { return this._status; } private readonly _onDidChangeStatus = new Emitter(); readonly onDidChangeStatus = this._onDidChangeStatus.event; + private readonly _onDidChangeSeenSequences = new Emitter>(); + readonly onDidChangeSeenSequences = this._onDidChangeSeenSequences.event; constructor( private _nonce: string, @@ -350,6 +374,13 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati this._createOrGetBufferMarkDetection(terminal).getMark(vscodeMarkerId); } + private _markSequenceSeen(sequence: string) { + if (!this._seenSequences.has(sequence)) { + this._seenSequences.add(sequence); + this._onDidChangeSeenSequences.fire(this._seenSequences); + } + } + private _handleFinalTermSequence(data: string): boolean { const didHandle = this._doHandleFinalTermSequence(data); if (this._status === ShellIntegrationStatus.Off) { @@ -370,6 +401,7 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati // when instant prompt is enabled though. If this does end up being a problem we could pass // a type flag through the capability calls const [command, ...args] = data.split(';'); + this._markSequenceSeen(command); switch (command) { case FinalTermOscPt.PromptStart: this._createOrGetCommandDetection(this._terminal).handlePromptStart(); @@ -410,7 +442,7 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati } this._activationTimeout = setTimeout(() => { if (!this.capabilities.get(TerminalCapability.CommandDetection) && !this.capabilities.get(TerminalCapability.CwdDetection)) { - this._telemetryService?.publicLog2<{ classification: 'SystemMetaData'; purpose: 'FeatureInsight' }>('terminal/shellIntegrationActivationTimeout'); + this._telemetryService?.publicLog2<{}, { owner: 'meganrogge'; comment: 'Indicates shell integration activation timeout' }>('terminal/shellIntegrationActivationTimeout'); this._logService.warn('Shell integration failed to add capabilities within 10 seconds'); } this._hasUpdatedTelemetry = true; @@ -431,10 +463,11 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati // Pass the sequence along to the capability const argsIndex = data.indexOf(';'); - const sequenceCommand = argsIndex === -1 ? data : data.substring(0, argsIndex); + const command = argsIndex === -1 ? data : data.substring(0, argsIndex); + this._markSequenceSeen(command); // Cast to strict checked index access const args: (string | undefined)[] = argsIndex === -1 ? [] : data.substring(argsIndex + 1).split(';'); - switch (sequenceCommand) { + switch (command) { case VSCodeOscPt.PromptStart: this._createOrGetCommandDetection(this._terminal).handlePromptStart(); return true; @@ -484,7 +517,7 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati return true; } case VSCodeOscPt.EnvSingleStart: { - this._createOrGetShellEnvDetection().startEnvironmentSingleVar(args[0] === this._nonce); + this._createOrGetShellEnvDetection().startEnvironmentSingleVar(args[0] === '1', args[1] === this._nonce); return true; } case VSCodeOscPt.EnvSingleDelete: { @@ -540,6 +573,10 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati this._createOrGetCommandDetection(this._terminal).setIsWindowsPty(value === 'True' ? true : false); return true; } + case 'HasRichCommandDetection': { + this._createOrGetCommandDetection(this._terminal).setHasRichCommandDetection(value === 'True' ? true : false); + return true; + } case 'Prompt': { // Remove escape sequences from the user's prompt const sanitizedValue = value.replace(/\x1b\[[0-9;]*m/g, ''); @@ -594,6 +631,7 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati } const [command] = data.split(';'); + this._markSequenceSeen(`${ShellIntegrationOscPs.ITerm};${command}`); switch (command) { case ITermOscPt.SetMark: { this._createOrGetBufferMarkDetection(this._terminal).addMark(); @@ -628,6 +666,7 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati } const [command, ...args] = data.split(';'); + this._markSequenceSeen(`${ShellIntegrationOscPs.SetWindowsFriendlyCwd};${command}`); switch (command) { case '9': // Encountered `OSC 9 ; 9 ; ST` @@ -650,6 +689,7 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati } const [command] = data.split(';'); + this._markSequenceSeen(`${ShellIntegrationOscPs.SetCwd};${command}`); if (command.match(/^file:\/\/.*\//)) { const uri = URI.parse(command); @@ -667,6 +707,7 @@ export class ShellIntegrationAddon extends Disposable implements IShellIntegrati if (!this._terminal || !this.capabilities.has(TerminalCapability.CommandDetection)) { return { isWindowsPty: false, + hasRichCommandDetection: false, commands: [], promptInputModel: undefined, }; diff --git a/src/vs/platform/terminal/node/ptyService.ts b/src/vs/platform/terminal/node/ptyService.ts index cb01a77585de..840334c8d4cc 100644 --- a/src/vs/platform/terminal/node/ptyService.ts +++ b/src/vs/platform/terminal/node/ptyService.ts @@ -234,6 +234,21 @@ export class PtyService extends Disposable implements IPtyService { private async _reviveTerminalProcess(workspaceId: string, terminal: ISerializedTerminalState): Promise { const restoreMessage = localize('terminal-history-restored', "History restored"); + + // Conpty v1.22+ uses passthrough and doesn't reprint the buffer often, this means that when + // the terminal is revived, the cursor would be at the bottom of the buffer then when + // PSReadLine requests `GetConsoleCursorInfo` it will be handled by conpty itself by design. + // This causes the cursor to move to the top into the replayed terminal contents. To avoid + // this, the post restore message will print new lines to get a clear viewport and put the + // cursor back at to top left. + let postRestoreMessage = ''; + if (isWindows) { + const lastReplayEvent = terminal.replayEvent.events.length > 0 ? terminal.replayEvent.events.at(-1) : undefined; + if (lastReplayEvent) { + postRestoreMessage += '\r\n'.repeat(lastReplayEvent.rows - 1) + `\x1b[H`; + } + } + // TODO: We may at some point want to show date information in a hover via a custom sequence: // new Date(terminal.timestamp).toLocaleDateString(dateTimeFormatLocale) // new Date(terminal.timestamp).toLocaleTimeString(dateTimeFormatLocale) @@ -244,7 +259,7 @@ export class PtyService extends Disposable implements IPtyService { color: terminal.processDetails.color, icon: terminal.processDetails.icon, name: terminal.processDetails.titleSource === TitleEventSource.Api ? terminal.processDetails.title : undefined, - initialText: terminal.replayEvent.events[0].data + formatMessageForTerminal(restoreMessage, { loudFormatting: true }) + initialText: terminal.replayEvent.events[0].data + formatMessageForTerminal(restoreMessage, { loudFormatting: true }) + postRestoreMessage }, terminal.processDetails.cwd, terminal.replayEvent.events[0].cols, diff --git a/src/vs/platform/terminal/node/terminalEnvironment.ts b/src/vs/platform/terminal/node/terminalEnvironment.ts index bacf1a64a740..b1ff7cc5a754 100644 --- a/src/vs/platform/terminal/node/terminalEnvironment.ts +++ b/src/vs/platform/terminal/node/terminalEnvironment.ts @@ -233,6 +233,7 @@ export function getShellIntegrationInjection( source: path.join(appRoot, 'out/vs/workbench/contrib/terminal/common/scripts/shellIntegration-login.zsh'), dest: path.join(zdotdir, '.zlogin') }); + envMixin['VSCODE_STABLE'] = productService.quality === 'stable' ? '1' : '0'; return { newArgs, envMixin, filesToCopy }; } } diff --git a/src/vs/platform/terminal/node/terminalProcess.ts b/src/vs/platform/terminal/node/terminalProcess.ts index 1fff426753ac..e33d5beccb36 100644 --- a/src/vs/platform/terminal/node/terminalProcess.ts +++ b/src/vs/platform/terminal/node/terminalProcess.ts @@ -78,6 +78,7 @@ const posixShellTypeMap = new Map([ const generalShellTypeMap = new Map([ ['pwsh', GeneralShellType.PowerShell], + ['powershell', GeneralShellType.PowerShell], ['python', GeneralShellType.Python], ['julia', GeneralShellType.Julia], ['nu', GeneralShellType.NuShell], @@ -418,7 +419,12 @@ export class TerminalProcess extends Disposable implements ITerminalChildProcess this._currentTitle = (ptyProcess.process ?? ''); this._onDidChangeProperty.fire({ type: ProcessPropertyType.Title, value: this._currentTitle }); // If fig is installed it may change the title of the process - const sanitizedTitle = this.currentTitle.replace(/ \(figterm\)$/g, ''); + let sanitizedTitle = this.currentTitle.replace(/ \(figterm\)$/g, ''); + // Ensure any prefixed path is removed so that the executable name since we use this to + // detect the shell type + if (!isWindows) { + sanitizedTitle = path.basename(sanitizedTitle); + } if (sanitizedTitle.toLowerCase().startsWith('python')) { this._onDidChangeProperty.fire({ type: ProcessPropertyType.ShellType, value: GeneralShellType.Python }); diff --git a/src/vs/platform/terminal/test/common/capabilities/commandDetection/promptInputModel.test.ts b/src/vs/platform/terminal/test/common/capabilities/commandDetection/promptInputModel.test.ts index 32934ec84986..dc34f387e61a 100644 --- a/src/vs/platform/terminal/test/common/capabilities/commandDetection/promptInputModel.test.ts +++ b/src/vs/platform/terminal/test/common/capabilities/commandDetection/promptInputModel.test.ts @@ -19,6 +19,7 @@ suite('PromptInputModel', () => { let promptInputModel: PromptInputModel; let xterm: Terminal; let onCommandStart: Emitter; + let onCommandStartChanged: Emitter; let onCommandExecuted: Emitter; async function writePromise(data: string) { @@ -62,8 +63,9 @@ suite('PromptInputModel', () => { const TerminalCtor = (await importAMDNodeModule('@xterm/xterm', 'lib/xterm.js')).Terminal; xterm = store.add(new TerminalCtor({ allowProposedApi: true })); onCommandStart = store.add(new Emitter()); + onCommandStartChanged = store.add(new Emitter()); onCommandExecuted = store.add(new Emitter()); - promptInputModel = store.add(new PromptInputModel(xterm, onCommandStart.event, onCommandExecuted.event, new NullLogService)); + promptInputModel = store.add(new PromptInputModel(xterm, onCommandStart.event, onCommandStartChanged.event, onCommandExecuted.event, new NullLogService)); }); test('basic input and execute', async () => { diff --git a/src/vs/platform/terminal/test/node/terminalEnvironment.test.ts b/src/vs/platform/terminal/test/node/terminalEnvironment.test.ts index 6248f83003d4..0829f838db63 100644 --- a/src/vs/platform/terminal/test/node/terminalEnvironment.test.ts +++ b/src/vs/platform/terminal/test/node/terminalEnvironment.test.ts @@ -132,7 +132,6 @@ suite('platform - terminalEnvironment', () => { /.+\/out\/vs\/workbench\/contrib\/terminal\/common\/scripts\/shellIntegration-login.zsh/ ]; function assertIsEnabled(result: IShellIntegrationConfigInjection, globalZdotdir = homedir()) { - strictEqual(Object.keys(result.envMixin!).length, 3); ok(result.envMixin!['ZDOTDIR']?.match(expectedDir)); strictEqual(result.envMixin!['USER_ZDOTDIR'], globalZdotdir); ok(result.envMixin!['VSCODE_INJECTION']?.match('1')); diff --git a/src/vs/platform/theme/common/colors/baseColors.ts b/src/vs/platform/theme/common/colors/baseColors.ts index 9c61daf2dffe..514fdc8a4e99 100644 --- a/src/vs/platform/theme/common/colors/baseColors.ts +++ b/src/vs/platform/theme/common/colors/baseColors.ts @@ -65,11 +65,11 @@ export const textSeparatorForeground = registerColor('textSeparator.foreground', // ------ text preformat export const textPreformatForeground = registerColor('textPreformat.foreground', - { light: '#A31515', dark: '#D7BA7D', hcDark: Color.white, hcLight: '#292929' }, + { light: '#A31515', dark: '#D7BA7D', hcDark: '#000000', hcLight: '#FFFFFF' }, nls.localize('textPreformatForeground', "Foreground color for preformatted text segments.")); export const textPreformatBackground = registerColor('textPreformat.background', - { light: '#0000001A', dark: '#FFFFFF1A', hcDark: Color.black, hcLight: '#F2F2F2' }, + { light: '#0000001A', dark: '#FFFFFF1A', hcDark: '#FFFFFF', hcLight: '#09345f' }, nls.localize('textPreformatBackground', "Background color for preformatted text segments.")); diff --git a/src/vs/platform/theme/common/themeService.ts b/src/vs/platform/theme/common/themeService.ts index 7911e0221da9..1f54a38197a5 100644 --- a/src/vs/platform/theme/common/themeService.ts +++ b/src/vs/platform/theme/common/themeService.ts @@ -100,12 +100,14 @@ export interface IThemingParticipant { (theme: IColorTheme, collector: ICssStyleCollector, environment: IEnvironmentService): void; } +export type IThemeChangeEvent = { theme: IColorTheme }; + export interface IThemeService { readonly _serviceBrand: undefined; getColorTheme(): IColorTheme; - readonly onDidColorThemeChange: Event; + readonly onDidColorThemeChange: Event; getFileIconTheme(): IFileIconTheme; @@ -182,7 +184,7 @@ export class Themable extends Disposable { this.theme = themeService.getColorTheme(); // Hook up to theme changes - this._register(this.themeService.onDidColorThemeChange(theme => this.onThemeChange(theme))); + this._register(this.themeService.onDidColorThemeChange(e => this.onThemeChange(e.theme))); } protected onThemeChange(theme: IColorTheme): void { diff --git a/src/vs/platform/theme/test/common/testThemeService.ts b/src/vs/platform/theme/test/common/testThemeService.ts index 8ee388d4dbd3..0e6c72cadcc0 100644 --- a/src/vs/platform/theme/test/common/testThemeService.ts +++ b/src/vs/platform/theme/test/common/testThemeService.ts @@ -7,7 +7,7 @@ import { Color } from '../../../../base/common/color.js'; import { Emitter, Event } from '../../../../base/common/event.js'; import { IconContribution } from '../../common/iconRegistry.js'; import { ColorScheme } from '../../common/theme.js'; -import { IColorTheme, IFileIconTheme, IProductIconTheme, IThemeService, ITokenStyle } from '../../common/themeService.js'; +import { IColorTheme, IFileIconTheme, IProductIconTheme, IThemeChangeEvent, IThemeService, ITokenStyle } from '../../common/themeService.js'; export class TestColorTheme implements IColorTheme { @@ -58,7 +58,7 @@ export class TestThemeService implements IThemeService { _colorTheme: IColorTheme; _fileIconTheme: IFileIconTheme; _productIconTheme: IProductIconTheme; - _onThemeChange = new Emitter(); + _onThemeChange = new Emitter(); _onFileIconThemeChange = new Emitter(); _onProductIconThemeChange = new Emitter(); @@ -78,10 +78,10 @@ export class TestThemeService implements IThemeService { } fireThemeChange() { - this._onThemeChange.fire(this._colorTheme); + this._onThemeChange.fire({ theme: this._colorTheme }); } - public get onDidColorThemeChange(): Event { + public get onDidColorThemeChange(): Event { return this._onThemeChange.event; } diff --git a/src/vs/platform/update/common/update.config.contribution.ts b/src/vs/platform/update/common/update.config.contribution.ts index d96926b5578a..ee290e42780d 100644 --- a/src/vs/platform/update/common/update.config.contribution.ts +++ b/src/vs/platform/update/common/update.config.contribution.ts @@ -17,7 +17,7 @@ configurationRegistry.registerConfiguration({ properties: { 'update.mode': { type: 'string', - enum: ['none', 'manual', 'start', 'default'], + enum: ['none', 'manual', 'start', 'block', 'default'], default: 'default', scope: ConfigurationScope.APPLICATION, description: localize('updateMode', "Configure whether you receive automatic updates. Requires a restart after change. The updates are fetched from a Microsoft online service."), @@ -26,6 +26,7 @@ configurationRegistry.registerConfiguration({ localize('none', "Disable updates."), localize('manual', "Disable automatic background update checks. Updates will be available if you manually check for updates."), localize('start', "Check for updates only on startup. Disable automatic background update checks."), + localize('block', "Check for updates on startup and install if available before attempting to open any windows."), localize('default', "Enable automatic update checks. Code will check for updates automatically and periodically.") ], policy: { diff --git a/src/vs/platform/update/common/update.ts b/src/vs/platform/update/common/update.ts index 199f433a462d..bd72b5be3b8e 100644 --- a/src/vs/platform/update/common/update.ts +++ b/src/vs/platform/update/common/update.ts @@ -100,10 +100,12 @@ export interface IUpdateService { readonly onStateChange: Event; readonly state: State; + initialize(): Promise; + checkForUpdates(explicit: boolean): Promise; downloadUpdate(): Promise; applyUpdate(): Promise; - quitAndInstall(): Promise; + quitAndInstall(force?: boolean): Promise; isLatestVersion(): Promise; _applySpecificUpdate(packagePath: string): Promise; diff --git a/src/vs/platform/update/common/updateIpc.ts b/src/vs/platform/update/common/updateIpc.ts index d30463039185..f6467c16c416 100644 --- a/src/vs/platform/update/common/updateIpc.ts +++ b/src/vs/platform/update/common/updateIpc.ts @@ -22,6 +22,7 @@ export class UpdateChannel implements IServerChannel { call(_: unknown, command: string, arg?: any): Promise { switch (command) { + case 'initialize': return this.service.initialize(); case 'checkForUpdates': return this.service.checkForUpdates(arg); case 'downloadUpdate': return this.service.downloadUpdate(); case 'applyUpdate': return this.service.applyUpdate(); @@ -55,6 +56,10 @@ export class UpdateChannelClient implements IUpdateService { this.channel.call('_getInitialState').then(state => this.state = state); } + initialize(): Promise { + return this.channel.call('initialize'); + } + checkForUpdates(explicit: boolean): Promise { return this.channel.call('checkForUpdates', explicit); } diff --git a/src/vs/platform/update/electron-main/abstractUpdateService.ts b/src/vs/platform/update/electron-main/abstractUpdateService.ts index 48638aa12f73..755533e9f73b 100644 --- a/src/vs/platform/update/electron-main/abstractUpdateService.ts +++ b/src/vs/platform/update/electron-main/abstractUpdateService.ts @@ -18,12 +18,6 @@ export function createUpdateURL(platform: string, quality: string, productServic return `${productService.updateUrl}/api/update/${platform}/${quality}/${productService.commit}`; } -export type UpdateNotAvailableClassification = { - owner: 'joaomoreno'; - explicit: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the user has manually checked for updates, or this was an automatic check.' }; - comment: 'This is used to understand how often VS Code pings the update server for an update and there\'s none available.'; -}; - export type UpdateErrorClassification = { owner: 'joaomoreno'; messageHash: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The hash of the error message.' }; @@ -68,7 +62,7 @@ export abstract class AbstractUpdateService implements IUpdateService { * optimization, to avoid using extra CPU cycles before first window open. * https://github.com/microsoft/vscode/issues/89784 */ - protected async initialize(): Promise { + async initialize(): Promise { if (!this.environmentMainService.isBuilt) { this.setState(State.Disabled(DisablementReason.NotBuilt)); return; // updates are never enabled when running out of sources @@ -86,7 +80,7 @@ export abstract class AbstractUpdateService implements IUpdateService { return; } - const updateMode = this.configurationService.getValue<'none' | 'manual' | 'start' | 'default'>('update.mode'); + const updateMode = this.configurationService.getValue<'none' | 'manual' | 'start' | 'default' | 'block'>('update.mode'); const quality = this.getProductQuality(updateMode); if (!quality) { @@ -178,22 +172,28 @@ export abstract class AbstractUpdateService implements IUpdateService { // noop } - quitAndInstall(): Promise { - this.logService.trace('update#quitAndInstall, state = ', this.state.type); + quitAndInstall(force?: boolean): Promise { + this.logService.info('update#quitAndInstall, state = ', this.state.type); if (this.state.type !== StateType.Ready) { return Promise.resolve(undefined); } - this.logService.trace('update#quitAndInstall(): before lifecycle quit()'); + if (force) { + this.logService.info('update#quitAndInstall(): running raw#quitAndInstall()'); + this.doQuitAndInstall(); + return Promise.resolve(undefined); + } + + this.logService.info('update#quitAndInstall(): before lifecycle quit()'); this.lifecycleMainService.quit(true /* will restart */).then(vetod => { - this.logService.trace(`update#quitAndInstall(): after lifecycle quit() with veto: ${vetod}`); + this.logService.info(`update#quitAndInstall(): after lifecycle quit() with veto: ${vetod}`); if (vetod) { return; } - this.logService.trace('update#quitAndInstall(): running raw#quitAndInstall()'); + this.logService.info('update#quitAndInstall(): running raw#quitAndInstall()'); this.doQuitAndInstall(); }); @@ -205,7 +205,7 @@ export abstract class AbstractUpdateService implements IUpdateService { return undefined; } - const mode = this.configurationService.getValue<'none' | 'manual' | 'start' | 'default'>('update.mode'); + const mode = this.configurationService.getValue<'none' | 'manual' | 'start' | 'default' | 'block'>('update.mode'); if (mode === 'none') { return false; diff --git a/src/vs/platform/update/electron-main/updateService.darwin.ts b/src/vs/platform/update/electron-main/updateService.darwin.ts index d3f27d3711e5..fe2c6dd24539 100644 --- a/src/vs/platform/update/electron-main/updateService.darwin.ts +++ b/src/vs/platform/update/electron-main/updateService.darwin.ts @@ -16,7 +16,7 @@ import { IProductService } from '../../product/common/productService.js'; import { IRequestService } from '../../request/common/request.js'; import { ITelemetryService } from '../../telemetry/common/telemetry.js'; import { IUpdate, State, StateType, UpdateType } from '../common/update.js'; -import { AbstractUpdateService, createUpdateURL, UpdateErrorClassification, UpdateNotAvailableClassification } from './abstractUpdateService.js'; +import { AbstractUpdateService, createUpdateURL, UpdateErrorClassification } from './abstractUpdateService.js'; export class DarwinUpdateService extends AbstractUpdateService implements IRelaunchHandler { @@ -56,7 +56,7 @@ export class DarwinUpdateService extends AbstractUpdateService implements IRelau return true; } - protected override async initialize(): Promise { + override async initialize(): Promise { await super.initialize(); this.onRawError(this.onError, this, this.disposables); this.onRawUpdateAvailable(this.onUpdateAvailable, this, this.disposables); @@ -113,10 +113,10 @@ export class DarwinUpdateService extends AbstractUpdateService implements IRelau type UpdateDownloadedClassification = { owner: 'joaomoreno'; - version: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The version number of the new VS Code that has been downloaded.' }; + newVersion: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The version number of the new VS Code that has been downloaded.' }; comment: 'This is used to know how often VS Code has successfully downloaded the update.'; }; - this.telemetryService.publicLog2<{ version: String }, UpdateDownloadedClassification>('update:downloaded', { version: update.version }); + this.telemetryService.publicLog2<{ newVersion: String }, UpdateDownloadedClassification>('update:downloaded', { newVersion: update.version }); this.setState(State.Ready(update)); } @@ -125,13 +125,12 @@ export class DarwinUpdateService extends AbstractUpdateService implements IRelau if (this.state.type !== StateType.CheckingForUpdates) { return; } - this.telemetryService.publicLog2<{ explicit: boolean }, UpdateNotAvailableClassification>('update:notAvailable', { explicit: this.state.explicit }); this.setState(State.Idle(UpdateType.Archive)); } protected override doQuitAndInstall(): void { - this.logService.trace('update#quitAndInstall(): running raw#quitAndInstall()'); + this.logService.info('update#quitAndInstall(): running raw#quitAndInstall()'); electron.autoUpdater.quitAndInstall(); } diff --git a/src/vs/platform/update/electron-main/updateService.linux.ts b/src/vs/platform/update/electron-main/updateService.linux.ts index 6e076c72ed80..dd18900547dd 100644 --- a/src/vs/platform/update/electron-main/updateService.linux.ts +++ b/src/vs/platform/update/electron-main/updateService.linux.ts @@ -11,16 +11,14 @@ import { ILogService } from '../../log/common/log.js'; import { INativeHostMainService } from '../../native/electron-main/nativeHostMainService.js'; import { IProductService } from '../../product/common/productService.js'; import { asJson, IRequestService } from '../../request/common/request.js'; -import { ITelemetryService } from '../../telemetry/common/telemetry.js'; import { AvailableForDownload, IUpdate, State, UpdateType } from '../common/update.js'; -import { AbstractUpdateService, createUpdateURL, UpdateNotAvailableClassification } from './abstractUpdateService.js'; +import { AbstractUpdateService, createUpdateURL } from './abstractUpdateService.js'; export class LinuxUpdateService extends AbstractUpdateService { constructor( @ILifecycleMainService lifecycleMainService: ILifecycleMainService, @IConfigurationService configurationService: IConfigurationService, - @ITelemetryService private readonly telemetryService: ITelemetryService, @IEnvironmentMainService environmentMainService: IEnvironmentMainService, @IRequestService requestService: IRequestService, @ILogService logService: ILogService, @@ -44,8 +42,6 @@ export class LinuxUpdateService extends AbstractUpdateService { .then(asJson) .then(update => { if (!update || !update.url || !update.version || !update.productVersion) { - this.telemetryService.publicLog2<{ explicit: boolean }, UpdateNotAvailableClassification>('update:notAvailable', { explicit: !!context }); - this.setState(State.Idle(UpdateType.Archive)); } else { this.setState(State.AvailableForDownload(update)); diff --git a/src/vs/platform/update/electron-main/updateService.snap.ts b/src/vs/platform/update/electron-main/updateService.snap.ts index 78bcf9d7a456..d591a8ef9922 100644 --- a/src/vs/platform/update/electron-main/updateService.snap.ts +++ b/src/vs/platform/update/electron-main/updateService.snap.ts @@ -11,9 +11,7 @@ import * as path from '../../../base/common/path.js'; import { IEnvironmentMainService } from '../../environment/electron-main/environmentMainService.js'; import { ILifecycleMainService } from '../../lifecycle/electron-main/lifecycleMainService.js'; import { ILogService } from '../../log/common/log.js'; -import { ITelemetryService } from '../../telemetry/common/telemetry.js'; import { AvailableForDownload, IUpdateService, State, StateType, UpdateType } from '../common/update.js'; -import { UpdateNotAvailableClassification } from './abstractUpdateService.js'; abstract class AbstractUpdateService implements IUpdateService { @@ -50,6 +48,8 @@ abstract class AbstractUpdateService implements IUpdateService { this.scheduleCheckForUpdates(30 * 1000).then(undefined, err => this.logService.error(err)); } + async initialize(): Promise { } + private scheduleCheckForUpdates(delay = 60 * 60 * 1000): Promise { return timeout(delay) .then(() => this.checkForUpdates(false)) @@ -145,7 +145,6 @@ export class SnapUpdateService extends AbstractUpdateService { @ILifecycleMainService lifecycleMainService: ILifecycleMainService, @IEnvironmentMainService environmentMainService: IEnvironmentMainService, @ILogService logService: ILogService, - @ITelemetryService private readonly telemetryService: ITelemetryService ) { super(lifecycleMainService, environmentMainService, logService); @@ -167,13 +166,10 @@ export class SnapUpdateService extends AbstractUpdateService { if (result) { this.setState(State.Ready({ version: 'something' })); } else { - this.telemetryService.publicLog2<{ explicit: boolean }, UpdateNotAvailableClassification>('update:notAvailable', { explicit: false }); - this.setState(State.Idle(UpdateType.Snap)); } }, err => { this.logService.error(err); - this.telemetryService.publicLog2<{ explicit: boolean }, UpdateNotAvailableClassification>('update:notAvailable', { explicit: false }); this.setState(State.Idle(UpdateType.Snap, err.message || err)); }); } diff --git a/src/vs/platform/update/electron-main/updateService.win32.ts b/src/vs/platform/update/electron-main/updateService.win32.ts index 61109e547417..9272c047ab7f 100644 --- a/src/vs/platform/update/electron-main/updateService.win32.ts +++ b/src/vs/platform/update/electron-main/updateService.win32.ts @@ -24,7 +24,7 @@ import { IProductService } from '../../product/common/productService.js'; import { asJson, IRequestService } from '../../request/common/request.js'; import { ITelemetryService } from '../../telemetry/common/telemetry.js'; import { AvailableForDownload, DisablementReason, IUpdate, State, StateType, UpdateType } from '../common/update.js'; -import { AbstractUpdateService, createUpdateURL, UpdateErrorClassification, UpdateNotAvailableClassification } from './abstractUpdateService.js'; +import { AbstractUpdateService, createUpdateURL, UpdateErrorClassification } from './abstractUpdateService.js'; async function pollUntil(fn: () => boolean, millis = 1000): Promise { while (!fn()) { @@ -89,7 +89,7 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun return true; } - protected override async initialize(): Promise { + override async initialize(): Promise { if (this.productService.target === 'user' && await this.nativeHostMainService.isAdmin(undefined)) { this.setState(State.Disabled(DisablementReason.RunningAsAdmin)); this.logService.info('update#ctor - updates are disabled due to running as Admin in user setup'); @@ -124,8 +124,6 @@ export class Win32UpdateService extends AbstractUpdateService implements IRelaun const updateType = getUpdateType(); if (!update || !update.url || !update.version || !update.productVersion) { - this.telemetryService.publicLog2<{ explicit: boolean }, UpdateNotAvailableClassification>('update:notAvailable', { explicit: !!context }); - this.setState(State.Idle(updateType)); return Promise.resolve(null); } diff --git a/src/vs/platform/userData/common/fileUserDataProvider.ts b/src/vs/platform/userData/common/fileUserDataProvider.ts index 1cfd07ec02b2..efa390b0ad2c 100644 --- a/src/vs/platform/userData/common/fileUserDataProvider.ts +++ b/src/vs/platform/userData/common/fileUserDataProvider.ts @@ -2,7 +2,7 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Emitter } from '../../../base/common/event.js'; +import { Emitter, Event } from '../../../base/common/event.js'; import { Disposable, IDisposable, toDisposable } from '../../../base/common/lifecycle.js'; import { IFileSystemProviderWithFileReadWriteCapability, IFileChange, IWatchOptions, IStat, IFileOverwriteOptions, FileType, IFileWriteOptions, IFileDeleteOptions, FileSystemProviderCapabilities, IFileSystemProviderWithFileReadStreamCapability, IFileReadStreamOptions, IFileSystemProviderWithFileAtomicReadCapability, hasFileFolderCopyCapability, IFileSystemProviderWithOpenReadWriteCloseCapability, IFileOpenOptions, IFileSystemProviderWithFileAtomicWriteCapability, IFileSystemProviderWithFileAtomicDeleteCapability, IFileSystemProviderWithFileFolderCopyCapability, IFileSystemProviderWithFileCloneCapability, hasFileCloneCapability, IFileAtomicReadOptions, IFileAtomicOptions } from '../../files/common/files.js'; import { URI } from '../../../base/common/uri.js'; @@ -29,14 +29,14 @@ export class FileUserDataProvider extends Disposable implements IFileSystemProviderWithFileAtomicDeleteCapability, IFileSystemProviderWithFileCloneCapability { - readonly capabilities = this.fileSystemProvider.capabilities; - readonly onDidChangeCapabilities = this.fileSystemProvider.onDidChangeCapabilities; + readonly capabilities: FileSystemProviderCapabilities; + readonly onDidChangeCapabilities: Event; - private readonly _onDidChangeFile = this._register(new Emitter()); - readonly onDidChangeFile = this._onDidChangeFile.event; + private readonly _onDidChangeFile: Emitter; + readonly onDidChangeFile: Event; - private readonly watchResources = TernarySearchTree.forUris(() => !(this.capabilities & FileSystemProviderCapabilities.PathCaseSensitive)); - private readonly atomicReadWriteResources = new ResourceSet((uri) => this.uriIdentityService.extUri.getComparisonKey(this.toFileSystemResource(uri))); + private readonly watchResources: TernarySearchTree; + private readonly atomicReadWriteResources: ResourceSet; constructor( private readonly fileSystemScheme: string, @@ -47,6 +47,12 @@ export class FileUserDataProvider extends Disposable implements private readonly logService: ILogService, ) { super(); + this.capabilities = this.fileSystemProvider.capabilities; + this.onDidChangeCapabilities = this.fileSystemProvider.onDidChangeCapabilities; + this._onDidChangeFile = this._register(new Emitter()); + this.onDidChangeFile = this._onDidChangeFile.event; + this.watchResources = TernarySearchTree.forUris(() => !(this.capabilities & 1024 /* FileSystemProviderCapabilities.PathCaseSensitive */)); + this.atomicReadWriteResources = new ResourceSet((uri) => this.uriIdentityService.extUri.getComparisonKey(this.toFileSystemResource(uri))); this.updateAtomicReadWritesResources(); this._register(userDataProfilesService.onDidChangeProfiles(() => this.updateAtomicReadWritesResources())); this._register(this.fileSystemProvider.onDidChangeFile(e => this.handleFileChanges(e))); diff --git a/src/vs/platform/userDataSync/common/promptsSync/promptsSync.ts b/src/vs/platform/userDataSync/common/promptsSync/promptsSync.ts index 918e1cf6eb85..8c4324017771 100644 --- a/src/vs/platform/userDataSync/common/promptsSync/promptsSync.ts +++ b/src/vs/platform/userDataSync/common/promptsSync/promptsSync.ts @@ -34,7 +34,7 @@ export function parsePrompts(syncData: ISyncData): IStringDictionary { } /** - * Synchronizer class for the global prompt files. + * Synchronizer class for the "user" prompt files. * Adopted from {@link SnippetsSynchroniser}. */ export class PromptsSynchronizer extends AbstractSynchroniser implements IUserDataSynchroniser { diff --git a/src/vs/platform/userDataSync/common/userDataSync.ts b/src/vs/platform/userDataSync/common/userDataSync.ts index 51b6957d2f03..b8f7b5c506e6 100644 --- a/src/vs/platform/userDataSync/common/userDataSync.ts +++ b/src/vs/platform/userDataSync/common/userDataSync.ts @@ -532,7 +532,7 @@ export interface IUserDataSyncEnablementService { setEnablement(enabled: boolean): void; readonly onDidChangeResourceEnablement: Event<[SyncResource, boolean]>; - isResourceEnabled(resource: SyncResource): boolean; + isResourceEnabled(resource: SyncResource, defaultValue?: boolean): boolean; setResourceEnablement(resource: SyncResource, enabled: boolean): void; getResourceSyncStateVersion(resource: SyncResource): string | undefined; diff --git a/src/vs/platform/userDataSync/common/userDataSyncEnablementService.ts b/src/vs/platform/userDataSync/common/userDataSyncEnablementService.ts index 163f805c7309..978ef5416f96 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncEnablementService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncEnablementService.ts @@ -52,8 +52,10 @@ export class UserDataSyncEnablementService extends Disposable implements IUserDa this.storageService.store(enablementKey, enabled, StorageScope.APPLICATION, StorageTarget.MACHINE); } - isResourceEnabled(resource: SyncResource): boolean { - return this.storageService.getBoolean(getEnablementKey(resource), StorageScope.APPLICATION, true); + isResourceEnabled(resource: SyncResource, defaultValue?: boolean): boolean { + const storedValue = this.storageService.getBoolean(getEnablementKey(resource), StorageScope.APPLICATION); + defaultValue = defaultValue ?? resource !== SyncResource.Prompts; + return storedValue ?? defaultValue; } setResourceEnablement(resource: SyncResource, enabled: boolean): void { diff --git a/src/vs/platform/userDataSync/common/userDataSyncService.ts b/src/vs/platform/userDataSync/common/userDataSyncService.ts index d6d22ed4a3de..cc0e93e20d64 100644 --- a/src/vs/platform/userDataSync/common/userDataSyncService.ts +++ b/src/vs/platform/userDataSync/common/userDataSyncService.ts @@ -865,9 +865,9 @@ class ProfileSynchronizer extends Disposable { case SyncResource.Tasks: return 3; case SyncResource.GlobalState: return 4; case SyncResource.Extensions: return 5; - case SyncResource.Profiles: return 6; - case SyncResource.WorkspaceState: return 7; - case SyncResource.Prompts: return 8; + case SyncResource.Prompts: return 6; + case SyncResource.Profiles: return 7; + case SyncResource.WorkspaceState: return 8; } } } diff --git a/src/vs/platform/userDataSync/test/common/userDataAutoSyncService.test.ts b/src/vs/platform/userDataSync/test/common/userDataAutoSyncService.test.ts index 950d974455a3..559d2bf4179a 100644 --- a/src/vs/platform/userDataSync/test/common/userDataAutoSyncService.test.ts +++ b/src/vs/platform/userDataSync/test/common/userDataAutoSyncService.test.ts @@ -164,11 +164,11 @@ suite('UserDataAutoSyncService', () => { { type: 'POST', url: `${target.url}/v1/resource/globalState`, headers: { 'If-Match': '0' } }, // Extensions { type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} }, - // Profiles - { type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: {} }, // Prompts { type: 'GET', url: `${target.url}/v1/resource/prompts/latest`, headers: {} }, { type: 'POST', url: `${target.url}/v1/resource/prompts`, headers: { 'If-Match': '0' } }, + // Profiles + { type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: {} }, // Manifest { type: 'GET', url: `${target.url}/v1/manifest`, headers: {} }, // Machines diff --git a/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts index 7d2891cea1d5..2cf0fb06ade0 100644 --- a/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts +++ b/src/vs/platform/userDataSync/test/common/userDataSyncClient.ts @@ -141,6 +141,11 @@ export class UserDataSyncClient extends Disposable { await fileService.writeFile(environmentService.argvResource, VSBuffer.fromString(JSON.stringify({ 'locale': 'en' }))); } await configurationService.reloadConfiguration(); + + // `prompts` resource is disabled by default, so enable it for tests + this.instantiationService + .get(IUserDataSyncEnablementService) + .setResourceEnablement(SyncResource.Prompts, true); } async sync(): Promise { diff --git a/src/vs/platform/userDataSync/test/common/userDataSyncService.test.ts b/src/vs/platform/userDataSync/test/common/userDataSyncService.test.ts index 62624c494e79..3048a8dd304c 100644 --- a/src/vs/platform/userDataSync/test/common/userDataSyncService.test.ts +++ b/src/vs/platform/userDataSync/test/common/userDataSyncService.test.ts @@ -47,11 +47,11 @@ suite('UserDataSyncService', () => { { type: 'POST', url: `${target.url}/v1/resource/globalState`, headers: { 'If-Match': '0' } }, // Extensions { type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} }, - // Profiles - { type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: {} }, // Prompts { type: 'GET', url: `${target.url}/v1/resource/prompts/latest`, headers: {} }, { type: 'POST', url: `${target.url}/v1/resource/prompts`, headers: { 'If-Match': '0' } }, + // Profiles + { type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: {} }, ]); }); @@ -84,11 +84,11 @@ suite('UserDataSyncService', () => { { type: 'POST', url: `${target.url}/v1/resource/globalState`, headers: { 'If-Match': '0' } }, // Extensions { type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} }, - // Profiles - { type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: {} }, // Prompts { type: 'GET', url: `${target.url}/v1/resource/prompts/latest`, headers: {} }, { type: 'POST', url: `${target.url}/v1/resource/prompts`, headers: { 'If-Match': '0' } }, + // Profiles + { type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: {} }, ]); }); @@ -117,12 +117,11 @@ suite('UserDataSyncService', () => { { type: 'GET', url: `${target.url}/v1/resource/globalState/latest`, headers: {} }, // Extensions { type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} }, - // Profiles - { type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: {} }, // Prompts { type: 'GET', url: `${target.url}/v1/resource/prompts/latest`, headers: {} }, + // Profiles + { type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: {} }, ]); - }); test('test first time sync from the client with no changes - merge', async () => { @@ -150,10 +149,9 @@ suite('UserDataSyncService', () => { { type: 'GET', url: `${target.url}/v1/resource/tasks/latest`, headers: {} }, { type: 'GET', url: `${target.url}/v1/resource/globalState/latest`, headers: {} }, { type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} }, - { type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: {} }, { type: 'GET', url: `${target.url}/v1/resource/prompts/latest`, headers: {} }, + { type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: {} }, ]); - }); test('test first time sync from the client with changes - merge', async () => { @@ -193,9 +191,9 @@ suite('UserDataSyncService', () => { { type: 'GET', url: `${target.url}/v1/resource/tasks/latest`, headers: {} }, { type: 'GET', url: `${target.url}/v1/resource/globalState/latest`, headers: {} }, { type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} }, - { type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: {} }, { type: 'GET', url: `${target.url}/v1/resource/prompts/latest`, headers: {} }, { type: 'POST', url: `${target.url}/v1/resource/prompts`, headers: { 'If-Match': '1' } }, + { type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: {} }, ]); }); @@ -238,11 +236,11 @@ suite('UserDataSyncService', () => { { type: 'GET', url: `${target.url}/v1/resource/tasks/latest`, headers: {} }, { type: 'GET', url: `${target.url}/v1/resource/globalState/latest`, headers: {} }, { type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} }, + { type: 'GET', url: `${target.url}/v1/resource/prompts/latest`, headers: {} }, + { type: 'POST', url: `${target.url}/v1/resource/prompts`, headers: { 'If-Match': '1' } }, { type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: {} }, { type: 'POST', url: `${target.url}/v1/collection`, headers: {} }, { type: 'POST', url: `${target.url}/v1/resource/profiles`, headers: { 'If-Match': '0' } }, - { type: 'GET', url: `${target.url}/v1/resource/prompts/latest`, headers: {} }, - { type: 'POST', url: `${target.url}/v1/resource/prompts`, headers: { 'If-Match': '1' } }, { type: 'GET', url: `${target.url}/v1/collection/1/resource/settings/latest`, headers: {} }, { type: 'GET', url: `${target.url}/v1/collection/1/resource/keybindings/latest`, headers: {} }, { type: 'GET', url: `${target.url}/v1/collection/1/resource/snippets/latest`, headers: {} }, @@ -347,11 +345,11 @@ suite('UserDataSyncService', () => { { type: 'POST', url: `${target.url}/v1/resource/snippets`, headers: { 'If-Match': '1' } }, // Global state { type: 'POST', url: `${target.url}/v1/resource/globalState`, headers: { 'If-Match': '1' } }, + // Prompts + { type: 'POST', url: `${target.url}/v1/resource/prompts`, headers: { 'If-Match': '1' } }, // Profiles { type: 'POST', url: `${target.url}/v1/collection`, headers: {} }, { type: 'POST', url: `${target.url}/v1/resource/profiles`, headers: { 'If-Match': '0' } }, - // Prompts - { type: 'POST', url: `${target.url}/v1/resource/prompts`, headers: { 'If-Match': '1' } }, { type: 'GET', url: `${target.url}/v1/collection/1/resource/settings/latest`, headers: {} }, { type: 'GET', url: `${target.url}/v1/collection/1/resource/keybindings/latest`, headers: {} }, { type: 'GET', url: `${target.url}/v1/collection/1/resource/snippets/latest`, headers: {} }, @@ -486,10 +484,10 @@ suite('UserDataSyncService', () => { { type: 'GET', url: `${target.url}/v1/resource/snippets/latest`, headers: { 'If-None-Match': '1' } }, // Global state { type: 'GET', url: `${target.url}/v1/resource/globalState/latest`, headers: { 'If-None-Match': '1' } }, - // Profiles - { type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: { 'If-None-Match': '0' } }, // Prompts { type: 'GET', url: `${target.url}/v1/resource/prompts/latest`, headers: { 'If-None-Match': '1' } }, + // Profiles + { type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: { 'If-None-Match': '0' } }, { type: 'GET', url: `${target.url}/v1/collection/1/resource/settings/latest`, headers: {} }, { type: 'GET', url: `${target.url}/v1/collection/1/resource/keybindings/latest`, headers: {} }, { type: 'GET', url: `${target.url}/v1/collection/1/resource/snippets/latest`, headers: {} }, @@ -558,11 +556,11 @@ suite('UserDataSyncService', () => { { type: 'POST', url: `${target.url}/v1/resource/globalState`, headers: { 'If-Match': '0' } }, // Extensions { type: 'GET', url: `${target.url}/v1/resource/extensions/latest`, headers: {} }, - // Profiles - { type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: {} }, // Prompts { type: 'GET', url: `${target.url}/v1/resource/prompts/latest`, headers: {} }, { type: 'POST', url: `${target.url}/v1/resource/prompts`, headers: { 'If-Match': '0' } }, + // Profiles + { type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: {} }, ]); }); @@ -749,11 +747,11 @@ suite('UserDataSyncService', () => { { type: 'POST', url: `${target.url}/v1/resource/snippets`, headers: { 'If-Match': '1' } }, // Global state { type: 'POST', url: `${target.url}/v1/resource/globalState`, headers: { 'If-Match': '1' } }, + // Prompts + { type: 'POST', url: `${target.url}/v1/resource/prompts`, headers: { 'If-Match': '1' } }, // Profiles { type: 'POST', url: `${target.url}/v1/collection`, headers: {} }, { type: 'POST', url: `${target.url}/v1/resource/profiles`, headers: { 'If-Match': '0' } }, - // Prompts - { type: 'POST', url: `${target.url}/v1/resource/prompts`, headers: { 'If-Match': '1' } }, { type: 'GET', url: `${target.url}/v1/collection/1/resource/keybindings/latest`, headers: {} }, { type: 'GET', url: `${target.url}/v1/collection/1/resource/snippets/latest`, headers: {} }, { type: 'GET', url: `${target.url}/v1/collection/1/resource/tasks/latest`, headers: {} }, @@ -804,10 +802,10 @@ suite('UserDataSyncService', () => { { type: 'GET', url: `${target.url}/v1/resource/snippets/latest`, headers: { 'If-None-Match': '1' } }, // Global state { type: 'GET', url: `${target.url}/v1/resource/globalState/latest`, headers: { 'If-None-Match': '1' } }, - // Profiles - { type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: { 'If-None-Match': '0' } }, // Prompts { type: 'GET', url: `${target.url}/v1/resource/prompts/latest`, headers: { 'If-None-Match': '1' } }, + // Profiles + { type: 'GET', url: `${target.url}/v1/resource/profiles/latest`, headers: { 'If-None-Match': '0' } }, { type: 'GET', url: `${target.url}/v1/collection/1/resource/settings/latest`, headers: {} }, { type: 'GET', url: `${target.url}/v1/collection/1/resource/snippets/latest`, headers: {} }, { type: 'GET', url: `${target.url}/v1/collection/1/resource/tasks/latest`, headers: {} }, @@ -817,5 +815,4 @@ suite('UserDataSyncService', () => { ]); }); - }); diff --git a/src/vs/platform/utilityProcess/electron-main/utilityProcess.ts b/src/vs/platform/utilityProcess/electron-main/utilityProcess.ts index e2f88fbf8c0f..6378c6d4c0c6 100644 --- a/src/vs/platform/utilityProcess/electron-main/utilityProcess.ts +++ b/src/vs/platform/utilityProcess/electron-main/utilityProcess.ts @@ -244,7 +244,7 @@ export class UtilityProcess extends Disposable { this.process = utilityProcess.fork(modulePath, args, { serviceName, env, - execArgv, + execArgv, // !!! Add `--trace-warnings` for node.js tracing !!! allowLoadingUnsignedLibraries, respondToAuthRequestsFromMainProcess, stdio diff --git a/src/vs/platform/utilityProcess/electron-main/utilityProcessWorkerMainService.ts b/src/vs/platform/utilityProcess/electron-main/utilityProcessWorkerMainService.ts index 9ab01b9be15d..1ce3d5a18a55 100644 --- a/src/vs/platform/utilityProcess/electron-main/utilityProcessWorkerMainService.ts +++ b/src/vs/platform/utilityProcess/electron-main/utilityProcessWorkerMainService.ts @@ -99,17 +99,19 @@ class UtilityProcessWorker extends Disposable { private readonly _onDidTerminate = this._register(new Emitter()); readonly onDidTerminate = this._onDidTerminate.event; - private readonly utilityProcess = this._register(new WindowUtilityProcess(this.logService, this.windowsMainService, this.telemetryService, this.lifecycleMainService)); + private readonly utilityProcess: WindowUtilityProcess; constructor( - @ILogService private readonly logService: ILogService, + @ILogService logService: ILogService, @IWindowsMainService private readonly windowsMainService: IWindowsMainService, - @ITelemetryService private readonly telemetryService: ITelemetryService, - @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, + @ITelemetryService telemetryService: ITelemetryService, + @ILifecycleMainService lifecycleMainService: ILifecycleMainService, private readonly configuration: IUtilityProcessWorkerCreateConfiguration ) { super(); + this.utilityProcess = this._register(new WindowUtilityProcess(logService, windowsMainService, telemetryService, lifecycleMainService)); + this.registerListeners(); } diff --git a/src/vs/platform/webContentExtractor/common/webContentExtractor.ts b/src/vs/platform/webContentExtractor/common/webContentExtractor.ts new file mode 100644 index 000000000000..5c9ab27ed41f --- /dev/null +++ b/src/vs/platform/webContentExtractor/common/webContentExtractor.ts @@ -0,0 +1,27 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { URI } from '../../../base/common/uri.js'; +import { createDecorator } from '../../instantiation/common/instantiation.js'; + +export const IWebContentExtractorService = createDecorator('IWebContentExtractorService'); + +export interface IWebContentExtractorService { + _serviceBrand: undefined; + extract(uri: URI[]): Promise; +} + +/** + * A service that extracts web content from a given URI. + * This is a placeholder implementation that does not perform any actual extraction. + * It's intended to be used on platforms where web content extraction is not supported such as in the browser. + */ +export class NullWebContentExtractorService implements IWebContentExtractorService { + _serviceBrand: undefined; + + extract(_uri: URI[]): Promise { + throw new Error('Not implemented'); + } +} diff --git a/src/vs/platform/webContentExtractor/electron-main/cdpAccessibilityDomain.ts b/src/vs/platform/webContentExtractor/electron-main/cdpAccessibilityDomain.ts new file mode 100644 index 000000000000..9e7a7c87b17b --- /dev/null +++ b/src/vs/platform/webContentExtractor/electron-main/cdpAccessibilityDomain.ts @@ -0,0 +1,199 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/** + * Contains types from https://chromedevtools.github.io/devtools-protocol/tot/Accessibility/ + */ + +export interface AXProperty { + name: string; + value: AXValue; +} + +export interface AXValue { + type: string; + value: any; +} + +export interface AXNode { + nodeId: string; + ignored?: boolean; + ignoredReasons?: AXProperty[]; + role?: AXValue; + chromeRole?: AXValue; + name?: AXValue; + description?: AXValue; + value?: AXValue; + properties?: AXProperty[]; + parentId?: string; + childIds?: string[]; + backendDOMNodeId?: number; + frameId?: string; +} + +/** + * Converts an array of AXNode objects to a readable format. + * It processes the nodes to extract their text content, ignoring navigation elements and + * formatting them in a structured way. + * + * @remarks We can do more here, but this is a good start. + * @param axNodes - The array of AXNode objects to be converted to a readable format. + * @returns string + */ +export function convertToReadibleFormat(axNodes: AXNode[]): string { + if (!axNodes.length) { + return ''; + } + + const nodeMap = new Map(); + const processedNodes = new Set(); + const rootNodes: AXNode[] = []; + + // Build node map and identify root nodes + for (const node of axNodes) { + nodeMap.set(node.nodeId, node); + if (!node.parentId || !axNodes.some(n => n.nodeId === node.parentId)) { + rootNodes.push(node); + } + } + + function isNavigationElement(node: AXNode): boolean { + // Skip navigation and UI elements that don't contribute to content + const skipRoles = [ + 'navigation', + 'banner', + 'complementary', + 'toolbar', + 'menu', + 'menuitem', + 'tab', + 'tablist' + ]; + const skipTexts = [ + 'Skip to main content', + 'Toggle navigation', + 'Previous', + 'Next', + 'Copy', + 'Direct link to', + 'On this page', + 'Edit this page', + 'Search', + 'Command+K' + ]; + + const text = getNodeText(node); + const role = node.role?.value?.toString().toLowerCase() || ''; + // allow-any-unicode-next-line + return skipRoles.includes(role) || + skipTexts.some(skipText => text.includes(skipText)) || + text.startsWith('Direct link to') || + text.startsWith('\xAB ') || // Left-pointing double angle quotation mark + text.endsWith(' \xBB') || // Right-pointing double angle quotation mark + /^#\s*$/.test(text) || // Skip standalone # characters + text === '\u200B'; // Zero-width space character + } + + function getNodeText(node: AXNode): string { + const parts: string[] = []; + + // Add name if available + if (node.name?.value) { + parts.push(String(node.name.value)); + } + + // Add value if available and different from name + if (node.value?.value && node.value.value !== node.name?.value) { + parts.push(String(node.value.value)); + } + + // Add description if available and different from name and value + if (node.description?.value && + node.description.value !== node.name?.value && + node.description.value !== node.value?.value) { + parts.push(String(node.description.value)); + } + + return parts.join(' ').trim(); + } + + function isCodeBlock(node: AXNode): boolean { + return node.role?.value === 'code' || + (node.properties || []).some(p => p.name === 'code-block' || p.name === 'pre'); + } + + function processNode(node: AXNode, depth: number = 0, parentContext: { inCodeBlock: boolean; codeText: string[] } = { inCodeBlock: false, codeText: [] }): string[] { + if (!node || node.ignored || processedNodes.has(node.nodeId)) { + return []; + } + + if (isNavigationElement(node)) { + return []; + } + + processedNodes.add(node.nodeId); + const lines: string[] = []; + const text = getNodeText(node); + const currentIsCode = isCodeBlock(node); + const context = currentIsCode ? { inCodeBlock: true, codeText: [] } : parentContext; + + if (text) { + const indent = ' '.repeat(depth); + if (currentIsCode || context.inCodeBlock) { + // For code blocks, collect text without adding newlines + context.codeText.push(text.trim()); + } else { + lines.push(indent + text); + } + } + + // Process children + if (node.childIds) { + for (const childId of node.childIds) { + const child = nodeMap.get(childId); + if (child) { + const childLines = processNode(child, depth + 1, context); + lines.push(...childLines); + } + } + } + + // If this is the root code block node, join all collected code text + if (currentIsCode && context.codeText.length > 0) { + const indent = ' '.repeat(depth); + lines.push(indent + context.codeText.join(' ')); + } + + return lines; + } + + // Process all nodes starting from roots + const allLines: string[] = []; + for (const node of rootNodes) { + const nodeLines = processNode(node); + if (nodeLines.length > 0) { + allLines.push(...nodeLines); + } + } + + // Process any remaining unprocessed nodes + for (const node of axNodes) { + if (!processedNodes.has(node.nodeId)) { + const nodeLines = processNode(node); + if (nodeLines.length > 0) { + allLines.push(...nodeLines); + } + } + } + + // Clean up empty lines and trim + return allLines + .filter((line, index, array) => { + // Keep the line if it's not empty or if it's not adjacent to another empty line + return line.trim() || (index > 0 && array[index - 1].trim()); + }) + .join('\n') + .trim(); +} diff --git a/src/vs/platform/webContentExtractor/electron-main/webContentExtractorService.ts b/src/vs/platform/webContentExtractor/electron-main/webContentExtractorService.ts new file mode 100644 index 000000000000..0cbfd73e6d52 --- /dev/null +++ b/src/vs/platform/webContentExtractor/electron-main/webContentExtractorService.ts @@ -0,0 +1,73 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { BrowserWindow } from 'electron'; +import { IWebContentExtractorService } from '../common/webContentExtractor.js'; +import { URI } from '../../../base/common/uri.js'; +import { AXNode, convertToReadibleFormat } from './cdpAccessibilityDomain.js'; +import { Limiter } from '../../../base/common/async.js'; +import { ResourceMap } from '../../../base/common/map.js'; + +interface CacheEntry { + content: string; + timestamp: number; +} + +export class NativeWebContentExtractorService implements IWebContentExtractorService { + _serviceBrand: undefined; + + // Only allow 3 windows to be opened at a time + // to avoid overwhelming the system with too many processes. + private _limiter = new Limiter(3); + private _webContentsCache = new ResourceMap(); + private readonly _cacheDuration = 24 * 60 * 60 * 1000; // 1 day in milliseconds + + private isExpired(entry: CacheEntry): boolean { + return Date.now() - entry.timestamp > this._cacheDuration; + } + + extract(uris: URI[]): Promise { + if (uris.length === 0) { + return Promise.resolve([]); + } + return Promise.all(uris.map((uri) => this._limiter.queue(() => this.doExtract(uri)))); + } + + async doExtract(uri: URI): Promise { + const cached = this._webContentsCache.get(uri); + if (cached) { + if (this.isExpired(cached)) { + this._webContentsCache.delete(uri); + } else { + return cached.content; + } + } + + const win = new BrowserWindow({ + width: 800, + height: 600, + show: false, + webPreferences: { + javascript: false, + offscreen: true, + sandbox: true, + webgl: false + } + }); + try { + await win.loadURL(uri.toString(true)); + win.webContents.debugger.attach('1.1'); + const result: { nodes: AXNode[] } = await win.webContents.debugger.sendCommand('Accessibility.getFullAXTree'); + const str = convertToReadibleFormat(result.nodes); + this._webContentsCache.set(uri, { content: str, timestamp: Date.now() }); + return str; + } catch (err) { + console.log(err); + } finally { + win.destroy(); + } + return ''; + } +} diff --git a/src/vs/platform/webContentExtractor/electron-sandbox/webContentExtractorService.ts b/src/vs/platform/webContentExtractor/electron-sandbox/webContentExtractorService.ts new file mode 100644 index 000000000000..2f6d99645767 --- /dev/null +++ b/src/vs/platform/webContentExtractor/electron-sandbox/webContentExtractorService.ts @@ -0,0 +1,9 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { registerMainProcessRemoteService } from '../../ipc/electron-sandbox/services.js'; +import { IWebContentExtractorService } from '../common/webContentExtractor.js'; + +registerMainProcessRemoteService(IWebContentExtractorService, 'webContentExtractor'); diff --git a/src/vs/platform/window/common/window.ts b/src/vs/platform/window/common/window.ts index 56cbfacd20d2..ddf6d46f7db6 100644 --- a/src/vs/platform/window/common/window.ts +++ b/src/vs/platform/window/common/window.ts @@ -6,7 +6,7 @@ import { VSBuffer } from '../../../base/common/buffer.js'; import { IStringDictionary } from '../../../base/common/collections.js'; import { PerformanceMark } from '../../../base/common/performance.js'; -import { isLinux, isMacintosh, isNative, isWeb } from '../../../base/common/platform.js'; +import { isMacintosh, isNative, isWeb } from '../../../base/common/platform.js'; import { URI, UriComponents, UriDto } from '../../../base/common/uri.js'; import { ISandboxConfiguration } from '../../../base/parts/sandbox/common/sandboxTypes.js'; import { IConfigurationService } from '../../configuration/common/configuration.js'; @@ -15,7 +15,6 @@ import { NativeParsedArgs } from '../../environment/common/argv.js'; import { FileType } from '../../files/common/files.js'; import { ILoggerResource, LogLevel } from '../../log/common/log.js'; import { PolicyDefinition, PolicyValue } from '../../policy/common/policy.js'; -import product from '../../product/common/product.js'; import { IPartsSplash } from '../../theme/common/themeService.js'; import { IUserDataProfile } from '../../userDataProfile/common/userDataProfile.js'; import { IAnyWorkspaceIdentifier, ISingleFolderWorkspaceIdentifier, IWorkspaceIdentifier } from '../../workspace/common/workspace.js'; @@ -153,6 +152,7 @@ export interface IWindowSettings { readonly restoreFullscreen: boolean; readonly zoomLevel: number; readonly titleBarStyle: TitlebarStyle; + readonly controlsStyle: WindowControlsStyle; readonly autoDetectHighContrast: boolean; readonly autoDetectColorScheme: boolean; readonly menuBarVisibility: MenuBarVisibility; @@ -180,17 +180,18 @@ export const enum TitlebarStyle { CUSTOM = 'custom', } +export const enum WindowControlsStyle { + NATIVE = 'native', + CUSTOM = 'custom', + HIDDEN = 'hidden' +} + export const enum CustomTitleBarVisibility { AUTO = 'auto', WINDOWED = 'windowed', NEVER = 'never', } -export let titlebarStyleDefaultOverride: 'custom' | undefined = undefined; -export function overrideDefaultTitlebarStyle(style: 'custom'): void { - titlebarStyleDefaultOverride = style; -} - export function hasCustomTitlebar(configurationService: IConfigurationService, titleBarStyle?: TitlebarStyle): boolean { // Returns if it possible to have a custom title bar in the curren session // Does not imply that the title bar is visible @@ -228,11 +229,21 @@ export function getTitleBarStyle(configurationService: IConfigurationService): T } } - if (titlebarStyleDefaultOverride === 'custom') { - return TitlebarStyle.CUSTOM; + return TitlebarStyle.CUSTOM; // default to custom on all OS +} + +export function getWindowControlsStyle(configurationService: IConfigurationService): WindowControlsStyle { + if (isWeb || isMacintosh || getTitleBarStyle(configurationService) === TitlebarStyle.NATIVE) { + return WindowControlsStyle.NATIVE; // only supported on Windows/Linux desktop with custom titlebar + } + + const configuration = configurationService.getValue('window'); + const style = configuration?.controlsStyle; + if (style === WindowControlsStyle.CUSTOM || style === WindowControlsStyle.HIDDEN) { + return style; } - return isLinux && product.quality === 'stable' ? TitlebarStyle.NATIVE : TitlebarStyle.CUSTOM; // default to custom on all OS except Linux stable (for now) + return WindowControlsStyle.NATIVE; // default to native on all OS } export const DEFAULT_CUSTOM_TITLEBAR_HEIGHT = 35; // includes space for command center @@ -246,6 +257,13 @@ export function useWindowControlsOverlay(configurationService: IConfigurationSer return false; // only supported when title bar is custom } + if (!isMacintosh) { + const setting = getWindowControlsStyle(configurationService); + if (setting === WindowControlsStyle.CUSTOM || setting === WindowControlsStyle.HIDDEN) { + return false; // explicitly disabled by choice + } + } + return true; // default } @@ -392,7 +410,6 @@ export interface INativeWindowConfiguration extends IWindowConfiguration, Native autoDetectHighContrast?: boolean; autoDetectColorScheme?: boolean; isCustomZoomLevel?: boolean; - overrideDefaultTitlebarStyle?: 'custom'; perfMarks: PerformanceMark[]; diff --git a/src/vs/platform/windows/electron-main/windowImpl.ts b/src/vs/platform/windows/electron-main/windowImpl.ts index 7ea47d522f8e..d7c6a41d986d 100644 --- a/src/vs/platform/windows/electron-main/windowImpl.ts +++ b/src/vs/platform/windows/electron-main/windowImpl.ts @@ -26,7 +26,7 @@ import { IFileService } from '../../files/common/files.js'; import { ILifecycleMainService } from '../../lifecycle/electron-main/lifecycleMainService.js'; import { ILogService } from '../../log/common/log.js'; import { IProductService } from '../../product/common/productService.js'; -import { IProtocolMainService } from '../../protocol/electron-main/protocol.js'; +import { IIPCObjectUrl, IProtocolMainService } from '../../protocol/electron-main/protocol.js'; import { resolveMarketplaceHeaders } from '../../externalServices/common/marketplace.js'; import { IApplicationStorageMainService, IStorageMainService } from '../../storage/electron-main/storageMainService.js'; import { ITelemetryService } from '../../telemetry/common/telemetry.js'; @@ -536,7 +536,7 @@ export class CodeWindow extends BaseWindow implements ICodeWindow { private customZoomLevel: number | undefined = undefined; - private readonly configObjectUrl = this._register(this.protocolMainService.createIPCObjectUrl()); + private readonly configObjectUrl: IIPCObjectUrl; private pendingLoadConfig: INativeWindowConfiguration | undefined; private wasLoaded = false; @@ -558,7 +558,7 @@ export class CodeWindow extends BaseWindow implements ICodeWindow { @IDialogMainService private readonly dialogMainService: IDialogMainService, @ILifecycleMainService private readonly lifecycleMainService: ILifecycleMainService, @IProductService private readonly productService: IProductService, - @IProtocolMainService private readonly protocolMainService: IProtocolMainService, + @IProtocolMainService protocolMainService: IProtocolMainService, @IWindowsMainService private readonly windowsMainService: IWindowsMainService, @IStateService stateService: IStateService, @IInstantiationService instantiationService: IInstantiationService @@ -567,6 +567,8 @@ export class CodeWindow extends BaseWindow implements ICodeWindow { //#region create browser window { + this.configObjectUrl = this._register(protocolMainService.createIPCObjectUrl()); + // Load window state const [state, hasMultipleDisplays] = this.restoreWindowState(config.state); this.windowState = state; diff --git a/src/vs/platform/windows/electron-main/windows.ts b/src/vs/platform/windows/electron-main/windows.ts index d8720996b919..99f145c92367 100644 --- a/src/vs/platform/windows/electron-main/windows.ts +++ b/src/vs/platform/windows/electron-main/windows.ts @@ -192,21 +192,23 @@ export function defaultBrowserWindowOptions(accessor: ServicesAccessor, windowSt } if (useWindowControlsOverlay(configurationService)) { - - // This logic will not perfectly guess the right colors - // to use on initialization, but prefer to keep things - // simple as it is temporary and not noticeable - // On macOS, only the presence of `titleBarOverlay` is - // considered, the properties are ignored. - - const titleBarColor = themeMainService.getWindowSplash(undefined)?.colorInfo.titleBarBackground ?? themeMainService.getBackgroundColor(); - const symbolColor = Color.fromHex(titleBarColor).isDarker() ? '#FFFFFF' : '#000000'; - - options.titleBarOverlay = { - height: 29, // the smallest size of the title bar on windows accounting for the border on windows 11 - color: titleBarColor, - symbolColor - }; + if (isMacintosh) { + options.titleBarOverlay = true; + } else { + + // This logic will not perfectly guess the right colors + // to use on initialization, but prefer to keep things + // simple as it is temporary and not noticeable + + const titleBarColor = themeMainService.getWindowSplash(undefined)?.colorInfo.titleBarBackground ?? themeMainService.getBackgroundColor(); + const symbolColor = Color.fromHex(titleBarColor).isDarker() ? '#FFFFFF' : '#000000'; + + options.titleBarOverlay = { + height: 29, // the smallest size of the title bar on windows accounting for the border on windows 11 + color: titleBarColor, + symbolColor + }; + } } } diff --git a/src/vs/platform/windows/electron-main/windowsMainService.ts b/src/vs/platform/windows/electron-main/windowsMainService.ts index 4690b9702df0..594b83643530 100644 --- a/src/vs/platform/windows/electron-main/windowsMainService.ts +++ b/src/vs/platform/windows/electron-main/windowsMainService.ts @@ -37,7 +37,7 @@ import product from '../../product/common/product.js'; import { IProtocolMainService } from '../../protocol/electron-main/protocol.js'; import { getRemoteAuthority } from '../../remote/common/remoteHosts.js'; import { IStateService } from '../../state/node/state.js'; -import { IAddRemoveFoldersRequest, INativeOpenFileRequest, INativeWindowConfiguration, IOpenEmptyWindowOptions, IPath, IPathsToWaitFor, isFileToOpen, isFolderToOpen, isWorkspaceToOpen, IWindowOpenable, IWindowSettings, titlebarStyleDefaultOverride } from '../../window/common/window.js'; +import { IAddRemoveFoldersRequest, INativeOpenFileRequest, INativeWindowConfiguration, IOpenEmptyWindowOptions, IPath, IPathsToWaitFor, isFileToOpen, isFolderToOpen, isWorkspaceToOpen, IWindowOpenable, IWindowSettings } from '../../window/common/window.js'; import { CodeWindow } from './windowImpl.js'; import { IOpenConfiguration, IOpenEmptyConfiguration, IWindowsCountChangedEvent, IWindowsMainService, OpenContext, getLastFocused } from './windows.js'; import { findWindowOnExtensionDevelopmentPath, findWindowOnFile, findWindowOnWorkspaceOrFolder } from './windowsFinder.js'; @@ -210,7 +210,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic private readonly windows = new Map(); - private readonly windowsStateHandler = this._register(new WindowsStateHandler(this, this.stateService, this.lifecycleMainService, this.logService, this.configurationService)); + private readonly windowsStateHandler: WindowsStateHandler; constructor( private readonly machineId: string, @@ -219,7 +219,7 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic private readonly initialUserEnv: IProcessEnvironment, @ILogService private readonly logService: ILogService, @ILoggerMainService private readonly loggerService: ILoggerMainService, - @IStateService private readonly stateService: IStateService, + @IStateService stateService: IStateService, @IPolicyService private readonly policyService: IPolicyService, @IEnvironmentMainService private readonly environmentMainService: IEnvironmentMainService, @IUserDataProfilesMainService private readonly userDataProfilesMainService: IUserDataProfilesMainService, @@ -238,6 +238,8 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic ) { super(); + this.windowsStateHandler = this._register(new WindowsStateHandler(this, stateService, this.lifecycleMainService, this.logService, this.configurationService)); + this.registerListeners(); } @@ -1506,7 +1508,6 @@ export class WindowsMainService extends Disposable implements IWindowsMainServic autoDetectHighContrast: windowConfig?.autoDetectHighContrast ?? true, autoDetectColorScheme: windowConfig?.autoDetectColorScheme ?? false, - overrideDefaultTitlebarStyle: titlebarStyleDefaultOverride, accessibilitySupport: app.accessibilitySupportEnabled, colorScheme: this.themeMainService.getColorScheme(), policiesData: this.policyService.serialize(), diff --git a/src/vs/platform/windows/electron-main/windowsStateHandler.ts b/src/vs/platform/windows/electron-main/windowsStateHandler.ts index bd14cdbe3f4c..1008f8217e59 100644 --- a/src/vs/platform/windows/electron-main/windowsStateHandler.ts +++ b/src/vs/platform/windows/electron-main/windowsStateHandler.ts @@ -55,7 +55,7 @@ export class WindowsStateHandler extends Disposable { private static readonly windowsStateStorageKey = 'windowsState'; get state() { return this._state; } - private readonly _state = restoreWindowsState(this.stateService.getItem(WindowsStateHandler.windowsStateStorageKey)); + private readonly _state: IWindowsState; private lastClosedState: IWindowState | undefined = undefined; @@ -70,6 +70,8 @@ export class WindowsStateHandler extends Disposable { ) { super(); + this._state = restoreWindowsState(this.stateService.getItem(WindowsStateHandler.windowsStateStorageKey)); + this.registerListeners(); } diff --git a/src/vs/platform/workspace/common/workspace.ts b/src/vs/platform/workspace/common/workspace.ts index c6e7245ba7f8..85c36ef5649f 100644 --- a/src/vs/platform/workspace/common/workspace.ts +++ b/src/vs/platform/workspace/common/workspace.ts @@ -329,16 +329,23 @@ export function isWorkspaceFolder(thing: unknown): thing is IWorkspaceFolder { export class Workspace implements IWorkspace { - private _foldersMap: TernarySearchTree = TernarySearchTree.forUris(this._ignorePathCasing, () => true); + private foldersMap: TernarySearchTree; + private _folders!: WorkspaceFolder[]; + get folders(): WorkspaceFolder[] { return this._folders; } + set folders(folders: WorkspaceFolder[]) { + this._folders = folders; + this.updateFoldersMap(); + } constructor( private _id: string, folders: WorkspaceFolder[], private _transient: boolean, private _configuration: URI | null, - private _ignorePathCasing: (key: URI) => boolean, + private ignorePathCasing: (key: URI) => boolean, ) { + this.foldersMap = TernarySearchTree.forUris(this.ignorePathCasing, () => true); this.folders = folders; } @@ -346,19 +353,10 @@ export class Workspace implements IWorkspace { this._id = workspace.id; this._configuration = workspace.configuration; this._transient = workspace.transient; - this._ignorePathCasing = workspace._ignorePathCasing; + this.ignorePathCasing = workspace.ignorePathCasing; this.folders = workspace.folders; } - get folders(): WorkspaceFolder[] { - return this._folders; - } - - set folders(folders: WorkspaceFolder[]) { - this._folders = folders; - this.updateFoldersMap(); - } - get id(): string { return this._id; } @@ -380,13 +378,13 @@ export class Workspace implements IWorkspace { return null; } - return this._foldersMap.findSubstr(resource) || null; + return this.foldersMap.findSubstr(resource) || null; } private updateFoldersMap(): void { - this._foldersMap = TernarySearchTree.forUris(this._ignorePathCasing, () => true); + this.foldersMap = TernarySearchTree.forUris(this.ignorePathCasing, () => true); for (const folder of this.folders) { - this._foldersMap.set(folder.uri, folder); + this.foldersMap.set(folder.uri, folder); } } @@ -440,23 +438,41 @@ export function toWorkspaceFolder(resource: URI): WorkspaceFolder { return new WorkspaceFolder({ uri: resource, index: 0, name: basenameOrAuthority(resource) }, { uri: resource.toString() }); } -export const WORKSPACE_EXTENSION = 'code-workspace'; -export const WORKSPACE_SUFFIX = `.${WORKSPACE_EXTENSION}`; -export const WORKSPACE_FILTER = [{ name: localize('codeWorkspace', "Code Workspace"), extensions: [WORKSPACE_EXTENSION] }]; -export const UNTITLED_WORKSPACE_NAME = 'workspace.json'; - -export function isUntitledWorkspace(path: URI, environmentService: IEnvironmentService): boolean { - return extUriBiasedIgnorePathCase.isEqualOrParent(path, environmentService.untitledWorkspacesHome); -} - -export function isTemporaryWorkspace(workspace: IWorkspace): boolean; -export function isTemporaryWorkspace(path: URI): boolean; -export function isTemporaryWorkspace(arg1: IWorkspace | URI): boolean { - let path: URI | null | undefined; - if (URI.isUri(arg1)) { - path = arg1; - } else { - path = arg1.configuration; +export function toWorkspaceFolders(configuredFolders: IStoredWorkspaceFolder[], workspaceConfigFile: URI): WorkspaceFolder[] { + let result: WorkspaceFolder[] = []; + let seen: Set = new Set(); + + const extUri = resources.extUriBiasedIgnorePathCase; // To be replaced by the UriIdentityService as parameter: #108793 + + const relativeTo = extUri.dirname(workspaceConfigFile); + for (let configuredFolder of configuredFolders) { + let uri: URI | null = null; + if (isRawFileWorkspaceFolder(configuredFolder)) { + if (configuredFolder.path) { + uri = extUri.resolvePath(relativeTo, configuredFolder.path); + } + } else if (isRawUriWorkspaceFolder(configuredFolder)) { + try { + uri = URI.parse(configuredFolder.uri); + // this makes sure all workspace folder are absolute + if (uri.path[0] !== '/') { + uri = uri.with({ path: '/' + uri.path }); + } + } catch (e) { + console.warn(e); + // ignore + } + } + if (uri) { + // remove duplicates + let comparisonKey = extUri.getComparisonKey(uri); + if (!seen.has(comparisonKey)) { + seen.add(comparisonKey); + + const name = configuredFolder.name || extUri.basenameOrAuthority(uri); + result.push(new WorkspaceFolder({ uri, name, index: result.length }, configuredFolder)); + } + } } return path?.scheme === Schemas.tmp; diff --git a/src/vs/platform/workspaces/common/workspaces.ts b/src/vs/platform/workspaces/common/workspaces.ts index 7dddb7f3764c..c9818f14c925 100644 --- a/src/vs/platform/workspaces/common/workspaces.ts +++ b/src/vs/platform/workspaces/common/workspaces.ts @@ -3,22 +3,27 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Event } from '../../../base/common/event.js'; -import { isUNC, toSlashes } from '../../../base/common/extpath.js'; -import * as json from '../../../base/common/json.js'; -import * as jsonEdit from '../../../base/common/jsonEdit.js'; -import { FormattingOptions } from '../../../base/common/jsonFormatter.js'; -import { normalizeDriveLetter } from '../../../base/common/labels.js'; -import { Schemas } from '../../../base/common/network.js'; -import { isAbsolute, posix } from '../../../base/common/path.js'; -import { isLinux, isMacintosh, isWindows } from '../../../base/common/platform.js'; -import { IExtUri, isEqualAuthority } from '../../../base/common/resources.js'; -import { URI } from '../../../base/common/uri.js'; -import { IWorkspaceBackupInfo, IFolderBackupInfo } from '../../backup/common/backup.js'; -import { createDecorator } from '../../instantiation/common/instantiation.js'; -import { ILogService } from '../../log/common/log.js'; -import { getRemoteAuthority } from '../../remote/common/remoteHosts.js'; -import { IBaseWorkspace, IRawFileWorkspaceFolder, IRawUriWorkspaceFolder, IWorkspaceIdentifier, WorkspaceFolder } from '../../workspace/common/workspace.js'; +import { createDecorator } from 'vs/platform/instantiation/common/instantiation'; +import { localize } from 'vs/nls'; +import { IWorkspaceFolder, IWorkspace } from 'vs/platform/workspace/common/workspace'; +import { URI, UriComponents } from 'vs/base/common/uri'; +import { isWindows, isLinux, isMacintosh } from 'vs/base/common/platform'; +import { extname, isAbsolute } from 'vs/base/common/path'; +import { isEqualAuthority, extname as resourceExtname, extUriBiasedIgnorePathCase } from 'vs/base/common/resources'; +import * as jsonEdit from 'vs/base/common/jsonEdit'; +import * as json from 'vs/base/common/json'; +import { Schemas } from 'vs/base/common/network'; +import { normalizeDriveLetter } from 'vs/base/common/labels'; +import { toSlashes } from 'vs/base/common/extpath'; +import { FormattingOptions } from 'vs/base/common/jsonFormatter'; +import { getRemoteAuthority } from 'vs/platform/remote/common/remoteHosts'; +import { ILogService } from 'vs/platform/log/common/log'; +import { Event } from 'vs/base/common/event'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; + +export const WORKSPACE_EXTENSION = 'code-workspace'; +export const WORKSPACE_FILTER = [{ name: localize('codeWorkspace', "Code Workspace"), extensions: [WORKSPACE_EXTENSION] }]; +export const UNTITLED_WORKSPACE_NAME = 'workspace.json'; export const IWorkspacesService = createDecorator('workspacesService'); @@ -141,9 +146,8 @@ export function getStoredWorkspaceFolder(folderURI: URI, forceAbsolute: boolean, return { name: folderName, uri: folderURI.toString(true) }; } - // Always prefer a relative path if possible unless - // prevented to make the workspace file shareable - // with other users + const extUri = extUriBiasedIgnorePathCase; // To be replaced by the UriIdentityService as parameter: #108793 + let folderPath = !forceAbsolute ? extUri.relativePath(targetConfigFolderURI, folderURI) : undefined; if (folderPath !== undefined) { if (folderPath.length === 0) { @@ -240,6 +244,8 @@ export function toWorkspaceFolders(configuredFolders: IStoredWorkspaceFolder[], export function rewriteWorkspaceFileForNewLocation(rawWorkspaceContents: string, configPathURI: URI, isFromUntitledWorkspace: boolean, targetConfigPathURI: URI, extUri: IExtUri) { const storedWorkspace = doParseStoredWorkspace(configPathURI, rawWorkspaceContents); + const extUri = extUriBiasedIgnorePathCase; // To be replaced by the UriIdentityService as parameter: #108793 + const sourceConfigFolder = extUri.dirname(configPathURI); const targetConfigFolder = extUri.dirname(targetConfigPathURI); diff --git a/src/vs/platform/workspaces/electron-main/workspacesMainService.ts b/src/vs/platform/workspaces/electron-main/workspacesMainService.ts index b42bd3ad6792..17374c47894b 100644 --- a/src/vs/platform/workspaces/electron-main/workspacesMainService.ts +++ b/src/vs/platform/workspaces/electron-main/workspacesMainService.ts @@ -12,6 +12,7 @@ import { IWorkspaceIdentifier } from '../../workspace/common/workspace.js'; import { IWorkspacesHistoryMainService } from './workspacesHistoryMainService.js'; import { IWorkspacesManagementMainService } from './workspacesManagementMainService.js'; import { IWorkspaceBackupInfo, IFolderBackupInfo } from '../../backup/common/backup.js'; +import { Event } from '../../../base/common/event.js'; export class WorkspacesMainService implements AddFirstParameterToFunctions /* only methods, not events */, number /* window ID */> { @@ -23,6 +24,7 @@ export class WorkspacesMainService implements AddFirstParameterToFunctions; getRecentlyOpened(windowId: number): Promise { return this.workspacesHistoryMainService.getRecentlyOpened(); diff --git a/src/vs/platform/workspaces/electron-main/workspacesManagementMainService.ts b/src/vs/platform/workspaces/electron-main/workspacesManagementMainService.ts index 33a585bd9822..64835d8b0ebc 100644 --- a/src/vs/platform/workspaces/electron-main/workspacesManagementMainService.ts +++ b/src/vs/platform/workspaces/electron-main/workspacesManagementMainService.ts @@ -64,7 +64,7 @@ export class WorkspacesManagementMainService extends Disposable implements IWork private readonly _onDidEnterWorkspace = this._register(new Emitter()); readonly onDidEnterWorkspace: Event = this._onDidEnterWorkspace.event; - private readonly untitledWorkspacesHome = this.environmentMainService.untitledWorkspacesHome; // local URI that contains all untitled workspaces + private readonly untitledWorkspacesHome: URI; // local URI that contains all untitled workspaces private untitledWorkspaces: IUntitledWorkspaceInfo[] = []; @@ -76,6 +76,8 @@ export class WorkspacesManagementMainService extends Disposable implements IWork @IDialogMainService private readonly dialogMainService: IDialogMainService ) { super(); + + this.untitledWorkspacesHome = this.environmentMainService.untitledWorkspacesHome; } async initialize(): Promise { diff --git a/src/vs/server/node/remoteAgentEnvironmentImpl.ts b/src/vs/server/node/remoteAgentEnvironmentImpl.ts index e050ae1f4f5f..3b4aa7cbe3ae 100644 --- a/src/vs/server/node/remoteAgentEnvironmentImpl.ts +++ b/src/vs/server/node/remoteAgentEnvironmentImpl.ts @@ -103,7 +103,7 @@ export class RemoteAgentEnvironmentChannel implements IServerChannel { if (process.platform === 'linux') { const glibcVersion = (process as ProcessWithGlibc).glibcVersion; const minorVersion = glibcVersion ? parseInt(glibcVersion.split('.')[1]) : 28; - isUnsupportedGlibc = (minorVersion <= 27); + isUnsupportedGlibc = (minorVersion <= 27) || !!process.env['VSCODE_SERVER_CUSTOM_GLIBC_LINKER']; } return { pid: process.pid, diff --git a/src/vs/server/node/remoteExtensionHostAgentCli.ts b/src/vs/server/node/remoteExtensionHostAgentCli.ts index df33289a964c..066f8f3a9aab 100644 --- a/src/vs/server/node/remoteExtensionHostAgentCli.ts +++ b/src/vs/server/node/remoteExtensionHostAgentCli.ts @@ -51,6 +51,8 @@ import { LoggerService } from '../../platform/log/node/loggerService.js'; import { localize } from '../../nls.js'; import { addUNCHostToAllowlist, disableUNCAccessRestrictions } from '../../base/node/unc.js'; import { AllowedExtensionsService } from '../../platform/extensionManagement/common/allowedExtensionsService.js'; +import { IExtensionGalleryManifestService } from '../../platform/extensionManagement/common/extensionGalleryManifest.js'; +import { ExtensionGalleryManifestService } from '../../platform/extensionManagement/common/extensionGalleryManifestService.js'; class CliMain extends Disposable { @@ -132,6 +134,7 @@ class CliMain extends Disposable { services.set(IRequestService, new SyncDescriptor(RequestService, ['remote'])); services.set(IDownloadService, new SyncDescriptor(DownloadService)); services.set(ITelemetryService, NullTelemetryService); + services.set(IExtensionGalleryManifestService, new SyncDescriptor(ExtensionGalleryManifestService)); services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryServiceWithNoStorageService)); services.set(IExtensionsProfileScannerService, new SyncDescriptor(ExtensionsProfileScannerService)); services.set(IExtensionsScannerService, new SyncDescriptor(ExtensionsScannerService)); diff --git a/src/vs/server/node/remoteExtensionHostAgentServer.ts b/src/vs/server/node/remoteExtensionHostAgentServer.ts index 8323d0f13d0a..18bc307d31e9 100644 --- a/src/vs/server/node/remoteExtensionHostAgentServer.ts +++ b/src/vs/server/node/remoteExtensionHostAgentServer.ts @@ -64,7 +64,7 @@ class RemoteExtensionHostAgentServer extends Disposable implements IServerAPI { private readonly _managementConnections: { [reconnectionToken: string]: ManagementConnection }; private readonly _allReconnectionTokens: Set; private readonly _webClientServer: WebClientServer | null; - private readonly _webEndpointOriginChecker = WebEndpointOriginChecker.create(this._productService); + private readonly _webEndpointOriginChecker: WebEndpointOriginChecker; private readonly _serverBasePath: string | undefined; private readonly _serverProductPath: string; @@ -83,6 +83,7 @@ class RemoteExtensionHostAgentServer extends Disposable implements IServerAPI { @IInstantiationService private readonly _instantiationService: IInstantiationService, ) { super(); + this._webEndpointOriginChecker = WebEndpointOriginChecker.create(this._productService); if (serverBasePath !== undefined && serverBasePath.charCodeAt(serverBasePath.length - 1) === CharCode.Slash) { // Remove trailing slash from base path diff --git a/src/vs/server/node/remoteExtensionsScanner.ts b/src/vs/server/node/remoteExtensionsScanner.ts index a9712d781f5a..1bd6cf68c1dc 100644 --- a/src/vs/server/node/remoteExtensionsScanner.ts +++ b/src/vs/server/node/remoteExtensionsScanner.ts @@ -88,10 +88,12 @@ export class RemoteExtensionsScannerService implements IRemoteExtensionsScannerS } } - if (failed.length) { - _logService.info(`Reporting the following extensions as failed to install: ${failed.map(f => f.id).join(', ')}`); + if (!failed.length) { + _logService.trace(`No extensions to report as failed`); + return { failed: [] }; } + _logService.info(`Relaying the following extensions to install later: ${failed.map(f => f.id).join(', ')}`); return { failed }; }); } diff --git a/src/vs/server/node/remoteTerminalChannel.ts b/src/vs/server/node/remoteTerminalChannel.ts index 279c5113240e..325e8db1c517 100644 --- a/src/vs/server/node/remoteTerminalChannel.ts +++ b/src/vs/server/node/remoteTerminalChannel.ts @@ -201,7 +201,8 @@ export class RemoteTerminalChannel extends Disposable implements IServerChannel< reconnectionProperties: args.shellLaunchConfig.reconnectionProperties, type: args.shellLaunchConfig.type, isFeatureTerminal: args.shellLaunchConfig.isFeatureTerminal, - tabActions: args.shellLaunchConfig.tabActions + tabActions: args.shellLaunchConfig.tabActions, + shellIntegrationEnvironmentReporting: args.shellLaunchConfig.shellIntegrationEnvironmentReporting, }; diff --git a/src/vs/server/node/serverServices.ts b/src/vs/server/node/serverServices.ts index d66209b4ad63..a0848a8bfc9c 100644 --- a/src/vs/server/node/serverServices.ts +++ b/src/vs/server/node/serverServices.ts @@ -80,6 +80,11 @@ import { NodePtyHostStarter } from '../../platform/terminal/node/nodePtyHostStar import { CSSDevelopmentService, ICSSDevelopmentService } from '../../platform/cssDev/node/cssDevService.js'; import { AllowedExtensionsService } from '../../platform/extensionManagement/common/allowedExtensionsService.js'; import { TelemetryLogAppender } from '../../platform/telemetry/common/telemetryLogAppender.js'; +import { INativeMcpDiscoveryHelperService, NativeMcpDiscoveryHelperChannelName } from '../../platform/mcp/common/nativeMcpDiscoveryHelper.js'; +import { NativeMcpDiscoveryHelperChannel } from '../../platform/mcp/node/nativeMcpDiscoveryHelperChannel.js'; +import { NativeMcpDiscoveryHelperService } from '../../platform/mcp/node/nativeMcpDiscoveryHelperService.js'; +import { IExtensionGalleryManifestService } from '../../platform/extensionManagement/common/extensionGalleryManifest.js'; +import { ExtensionGalleryManifestIPCService } from '../../platform/extensionManagement/common/extensionGalleryManifestServiceIpc.js'; const eventPrefix = 'monacoworkbench'; @@ -183,6 +188,7 @@ export async function setupServerServices(connectionToken: ServerConnectionToken services.set(IServerTelemetryService, ServerNullTelemetryService); } + services.set(IExtensionGalleryManifestService, new ExtensionGalleryManifestIPCService(socketServer, productService)); services.set(IExtensionGalleryService, new SyncDescriptor(ExtensionGalleryServiceWithNoStorageService)); const downloadChannel = socketServer.getChannel('download', router); @@ -193,6 +199,7 @@ export async function setupServerServices(connectionToken: ServerConnectionToken services.set(IExtensionSignatureVerificationService, new SyncDescriptor(ExtensionSignatureVerificationService)); services.set(IAllowedExtensionsService, new SyncDescriptor(AllowedExtensionsService)); services.set(INativeServerExtensionManagementService, new SyncDescriptor(ExtensionManagementService)); + services.set(INativeMcpDiscoveryHelperService, new SyncDescriptor(NativeMcpDiscoveryHelperService)); const instantiationService: IInstantiationService = new InstantiationService(services); services.set(ILanguagePackService, instantiationService.createInstance(NativeLanguagePackService)); @@ -224,6 +231,8 @@ export async function setupServerServices(connectionToken: ServerConnectionToken const remoteExtensionsScanner = new RemoteExtensionsScannerService(instantiationService.createInstance(ExtensionManagementCLI, logService), environmentService, userDataProfilesService, extensionsScannerService, logService, extensionGalleryService, languagePackService, extensionManagementService); socketServer.registerChannel(RemoteExtensionsScannerChannelName, new RemoteExtensionsScannerChannel(remoteExtensionsScanner, (ctx: RemoteAgentConnectionContext) => getUriTransformer(ctx.remoteAuthority))); + socketServer.registerChannel(NativeMcpDiscoveryHelperChannelName, instantiationService.createInstance(NativeMcpDiscoveryHelperChannel, (ctx: RemoteAgentConnectionContext) => getUriTransformer(ctx.remoteAuthority))); + const remoteFileSystemChannel = disposables.add(new RemoteAgentFileSystemProviderChannel(logService, environmentService, configurationService)); socketServer.registerChannel(REMOTE_FILE_SYSTEM_CHANNEL_NAME, remoteFileSystemChannel); diff --git a/src/vs/workbench/api/browser/extensionHost.contribution.ts b/src/vs/workbench/api/browser/extensionHost.contribution.ts index 3e73acf1abd4..f0bf62c67ff5 100644 --- a/src/vs/workbench/api/browser/extensionHost.contribution.ts +++ b/src/vs/workbench/api/browser/extensionHost.contribution.ts @@ -88,6 +88,7 @@ import './mainThreadShare.js'; import './mainThreadProfileContentHandlers.js'; import './mainThreadAiRelatedInformation.js'; import './mainThreadAiEmbeddingVector.js'; +import './mainThreadMcp.js'; export class ExtensionPoints implements IWorkbenchContribution { diff --git a/src/vs/workbench/api/browser/mainThreadChatAgents2.ts b/src/vs/workbench/api/browser/mainThreadChatAgents2.ts index 1d4022618387..0da8d2a09f16 100644 --- a/src/vs/workbench/api/browser/mainThreadChatAgents2.ts +++ b/src/vs/workbench/api/browser/mainThreadChatAgents2.ts @@ -24,11 +24,12 @@ import { ILogService } from '../../../platform/log/common/log.js'; import { IChatWidgetService } from '../../contrib/chat/browser/chat.js'; import { ChatInputPart } from '../../contrib/chat/browser/chatInputPart.js'; import { AddDynamicVariableAction, IAddDynamicVariableContext } from '../../contrib/chat/browser/contrib/chatDynamicVariables.js'; -import { ChatAgentLocation, IChatAgentHistoryEntry, IChatAgentImplementation, IChatAgentRequest, IChatAgentService } from '../../contrib/chat/common/chatAgents.js'; +import { IChatAgentHistoryEntry, IChatAgentImplementation, IChatAgentRequest, IChatAgentService } from '../../contrib/chat/common/chatAgents.js'; import { IChatEditingService, IChatRelatedFileProviderMetadata } from '../../contrib/chat/common/chatEditingService.js'; import { ChatRequestAgentPart } from '../../contrib/chat/common/chatParserTypes.js'; import { ChatRequestParser } from '../../contrib/chat/common/chatRequestParser.js'; import { IChatContentInlineReference, IChatContentReference, IChatFollowup, IChatNotebookEdit, IChatProgress, IChatService, IChatTask, IChatWarningMessage } from '../../contrib/chat/common/chatService.js'; +import { ChatAgentLocation } from '../../contrib/chat/common/constants.js'; import { IExtHostContext, extHostNamedCustomer } from '../../services/extensions/common/extHostCustomers.js'; import { IExtensionService } from '../../services/extensions/common/extensions.js'; import { Dto } from '../../services/extensions/common/proxyIdentifier.js'; @@ -137,7 +138,9 @@ export class MainThreadChatAgents2 extends Disposable implements MainThreadChatA } const inputValue = widget?.inputEditor.getValue() ?? ''; - this._chatService.transferChatSession({ sessionId, inputValue }, URI.revive(toWorkspace)); + const location = widget.location; + const mode = widget.input.currentMode; + this._chatService.transferChatSession({ sessionId, inputValue, location, mode }, URI.revive(toWorkspace)); } async $registerAgent(handle: number, extension: ExtensionIdentifier, id: string, metadata: IExtensionChatAgentMetadata, dynamicProps: IDynamicChatAgentProps | undefined): Promise { @@ -172,9 +175,6 @@ export class MainThreadChatAgents2 extends Disposable implements MainThreadChatA return this._proxy.$provideFollowups(request, handle, result, { history }, token); }, - provideWelcomeMessage: (token: CancellationToken) => { - return this._proxy.$provideWelcomeMessage(handle, token); - }, provideChatTitle: (history, token) => { return this._proxy.$provideChatTitle(handle, history, token); }, diff --git a/src/vs/workbench/api/browser/mainThreadComments.ts b/src/vs/workbench/api/browser/mainThreadComments.ts index 5e7d48721cef..35799eeb566a 100644 --- a/src/vs/workbench/api/browser/mainThreadComments.ts +++ b/src/vs/workbench/api/browser/mainThreadComments.ts @@ -577,11 +577,14 @@ export class MainThreadComments extends Disposable implements MainThreadComments this._commentService.registerCommentController(providerId, provider); this._commentControllers.set(handle, provider); - const commentsPanelAlreadyConstructed = !!this._viewDescriptorService.getViewDescriptorById(COMMENTS_VIEW_ID); - if (!commentsPanelAlreadyConstructed) { - this.registerView(commentsPanelAlreadyConstructed); - } - this.registerViewListeners(commentsPanelAlreadyConstructed); + this._register(this._commentService.onResourceHasCommentingRanges(e => { + this.registerView(); + })); + + this._register(this._commentService.onDidUpdateCommentThreads(e => { + this.registerView(); + })); + this._commentService.setWorkspaceComments(String(handle), []); } @@ -693,8 +696,9 @@ export class MainThreadComments extends Disposable implements MainThreadComments thread.collapsibleState = languages.CommentThreadCollapsibleState.Collapsed; } - private registerView(commentsViewAlreadyRegistered: boolean) { - if (!commentsViewAlreadyRegistered) { + private registerView() { + const commentsPanelAlreadyConstructed = !!this._viewDescriptorService.getViewDescriptorById(COMMENTS_VIEW_ID); + if (!commentsPanelAlreadyConstructed) { const VIEW_CONTAINER: ViewContainer = Registry.as(ViewExtensions.ViewContainersRegistry).registerViewContainer({ id: COMMENTS_VIEW_ID, title: COMMENTS_VIEW_TITLE, @@ -717,6 +721,7 @@ export class MainThreadComments extends Disposable implements MainThreadComments } }], VIEW_CONTAINER); } + this.registerViewListeners(commentsPanelAlreadyConstructed); } private setComments() { @@ -737,7 +742,6 @@ export class MainThreadComments extends Disposable implements MainThreadComments this.setComments(); if (this._openViewListener) { this._openViewListener.dispose(); - this._openViewListener = null; } } }); diff --git a/src/vs/workbench/api/browser/mainThreadConfiguration.ts b/src/vs/workbench/api/browser/mainThreadConfiguration.ts index 86891475af3f..f83af92fe3d6 100644 --- a/src/vs/workbench/api/browser/mainThreadConfiguration.ts +++ b/src/vs/workbench/api/browser/mainThreadConfiguration.ts @@ -3,15 +3,16 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { URI } from '../../../base/common/uri.js'; -import { IDisposable } from '../../../base/common/lifecycle.js'; -import { Registry } from '../../../platform/registry/common/platform.js'; -import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope, getScopes } from '../../../platform/configuration/common/configurationRegistry.js'; -import { IWorkspaceContextService, WorkbenchState } from '../../../platform/workspace/common/workspace.js'; -import { MainThreadConfigurationShape, MainContext, ExtHostContext, IConfigurationInitData } from '../common/extHost.protocol.js'; -import { extHostNamedCustomer, IExtHostContext } from '../../services/extensions/common/extHostCustomers.js'; -import { ConfigurationTarget, IConfigurationService, IConfigurationOverrides } from '../../../platform/configuration/common/configuration.js'; -import { IEnvironmentService } from '../../../platform/environment/common/environment.js'; +import { URI } from 'vs/base/common/uri'; +import { IDisposable } from 'vs/base/common/lifecycle'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { IConfigurationRegistry, Extensions as ConfigurationExtensions, ConfigurationScope, getScopes } from 'vs/platform/configuration/common/configurationRegistry'; +import { IWorkspaceContextService, WorkbenchState } from 'vs/platform/workspace/common/workspace'; +import { MainThreadConfigurationShape, MainContext, ExtHostContext, IConfigurationInitData } from '../common/extHost.protocol'; +import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; +import { ConfigurationTarget, IConfigurationService, IConfigurationOverrides } from 'vs/platform/configuration/common/configuration'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { ErrorNoTelemetry } from 'vs/base/common/errors'; @extHostNamedCustomer(MainContext.MainThreadConfiguration) export class MainThreadConfiguration implements MainThreadConfigurationShape { @@ -72,12 +73,16 @@ export class MainThreadConfiguration implements MainThreadConfigurationShape { } } - private _updateValue(key: string, value: any, configurationTarget: ConfigurationTarget, overriddenValue: any | undefined, overrides: IConfigurationOverrides, scopeToLanguage: boolean | undefined): Promise { + private async _updateValue(key: string, value: any, configurationTarget: ConfigurationTarget, overriddenValue: any | undefined, overrides: IConfigurationOverrides, scopeToLanguage: boolean | undefined): Promise { overrides = scopeToLanguage === true ? overrides : scopeToLanguage === false ? { resource: overrides.resource } : overrides.overrideIdentifier && overriddenValue !== undefined ? overrides : { resource: overrides.resource }; - return this.configurationService.updateValue(key, value, overrides, configurationTarget, { donotNotifyError: true }); + try { + return await this.configurationService.updateValue(key, value, overrides, configurationTarget, true); + } catch (error) { + throw ErrorNoTelemetry.fromError(error); + } } private deriveConfigurationTarget(key: string, overrides: IConfigurationOverrides): ConfigurationTarget { diff --git a/src/vs/workbench/api/browser/mainThreadDebugService.ts b/src/vs/workbench/api/browser/mainThreadDebugService.ts index 69edcaa8ecd9..16cd23d554b6 100644 --- a/src/vs/workbench/api/browser/mainThreadDebugService.ts +++ b/src/vs/workbench/api/browser/mainThreadDebugService.ts @@ -116,12 +116,12 @@ export class MainThreadDebugService implements MainThreadDebugServiceShape, IDeb } $registerDebugVisualizerTree(treeId: string, canEdit: boolean): void { - this.visualizerService.registerTree(treeId, { + this._visualizerTreeHandles.set(treeId, this.visualizerService.registerTree(treeId, { disposeItem: id => this._proxy.$disposeVisualizedTree(id), getChildren: e => this._proxy.$getVisualizerTreeItemChildren(treeId, e), getTreeItem: e => this._proxy.$getVisualizerTreeItem(treeId, e), editItem: canEdit ? ((e, v) => this._proxy.$editVisualizerTreeItem(e, v)) : undefined - }); + })); } $unregisterDebugVisualizerTree(treeId: string): void { diff --git a/src/vs/workbench/api/browser/mainThreadDocuments.ts b/src/vs/workbench/api/browser/mainThreadDocuments.ts index a882ce0beed9..47925bd1c08d 100644 --- a/src/vs/workbench/api/browser/mainThreadDocuments.ts +++ b/src/vs/workbench/api/browser/mainThreadDocuments.ts @@ -12,7 +12,7 @@ import { IModelService } from '../../../editor/common/services/model.js'; import { ITextModelService } from '../../../editor/common/services/resolverService.js'; import { IFileService, FileOperation } from '../../../platform/files/common/files.js'; import { ExtHostContext, ExtHostDocumentsShape, MainThreadDocumentsShape } from '../common/extHost.protocol.js'; -import { ITextFileEditorModel, ITextFileService } from '../../services/textfile/common/textfiles.js'; +import { EncodingMode, ITextFileEditorModel, ITextFileService, TextFileResolveReason } from '../../services/textfile/common/textfiles.js'; import { IUntitledTextEditorModel } from '../../services/untitled/common/untitledTextEditorModel.js'; import { IWorkbenchEnvironmentService } from '../../services/environment/common/environmentService.js'; import { toLocalResource, extUri, IExtUri } from '../../../base/common/resources.js'; @@ -219,7 +219,7 @@ export class MainThreadDocuments extends Disposable implements MainThreadDocumen return Boolean(target); } - async $tryOpenDocument(uriData: UriComponents): Promise { + async $tryOpenDocument(uriData: UriComponents, options?: { encoding?: string }): Promise { const inputUri = URI.revive(uriData); if (!inputUri.scheme || !(inputUri.fsPath || inputUri.authority)) { throw new ErrorNoTelemetry(`Invalid uri. Scheme and authority or path must be set.`); @@ -230,11 +230,11 @@ export class MainThreadDocuments extends Disposable implements MainThreadDocumen let promise: Promise; switch (canonicalUri.scheme) { case Schemas.untitled: - promise = this._handleUntitledScheme(canonicalUri); + promise = this._handleUntitledScheme(canonicalUri, options); break; case Schemas.file: default: - promise = this._handleAsResourceInput(canonicalUri); + promise = this._handleAsResourceInput(canonicalUri, options); break; } @@ -255,31 +255,40 @@ export class MainThreadDocuments extends Disposable implements MainThreadDocumen } } - $tryCreateDocument(options?: { language?: string; content?: string }): Promise { - return this._doCreateUntitled(undefined, options ? options.language : undefined, options ? options.content : undefined); + $tryCreateDocument(options?: { language?: string; content?: string; encoding?: string }): Promise { + return this._doCreateUntitled(undefined, options); } - private async _handleAsResourceInput(uri: URI): Promise { + private async _handleAsResourceInput(uri: URI, options?: { encoding?: string }): Promise { + if (options?.encoding) { + const model = await this._textFileService.files.resolve(uri, { encoding: options.encoding, reason: TextFileResolveReason.REFERENCE }); + if (model.isDirty()) { + throw new ErrorNoTelemetry(`Cannot re-open a dirty text document with different encoding. Save it first.`); + } + await model.setEncoding(options.encoding, EncodingMode.Decode); + } + const ref = await this._textModelResolverService.createModelReference(uri); this._modelReferenceCollection.add(uri, ref, ref.object.textEditorModel.getValueLength()); return ref.object.textEditorModel.uri; } - private async _handleUntitledScheme(uri: URI): Promise { + private async _handleUntitledScheme(uri: URI, options?: { encoding?: string }): Promise { const asLocalUri = toLocalResource(uri, this._environmentService.remoteAuthority, this._pathService.defaultUriScheme); const exists = await this._fileService.exists(asLocalUri); if (exists) { // don't create a new file ontop of an existing file return Promise.reject(new Error('file already exists')); } - return await this._doCreateUntitled(Boolean(uri.path) ? uri : undefined); + return await this._doCreateUntitled(Boolean(uri.path) ? uri : undefined, options); } - private async _doCreateUntitled(associatedResource?: URI, languageId?: string, initialValue?: string): Promise { + private async _doCreateUntitled(associatedResource?: URI, options?: { language?: string; content?: string; encoding?: string }): Promise { const model = this._textFileService.untitled.create({ associatedResource, - languageId, - initialValue + languageId: options?.language, + initialValue: options?.content, + encoding: options?.encoding }); const resource = model.resource; const ref = await this._textModelResolverService.createModelReference(resource); diff --git a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index c744021cf31d..9aadd0d679ae 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -614,7 +614,7 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread this._registrations.set(handle, this._languageFeaturesService.completionProvider.register(selector, provider)); } - $registerInlineCompletionsSupport(handle: number, selector: IDocumentFilterDto[], supportsHandleEvents: boolean, extensionId: string, yieldsToExtensionIds: string[], debounceDelayMs: number | undefined): void { + $registerInlineCompletionsSupport(handle: number, selector: IDocumentFilterDto[], supportsHandleEvents: boolean, extensionId: string, yieldsToExtensionIds: string[], displayName: string | undefined, debounceDelayMs: number | undefined): void { const provider: languages.InlineCompletionsProvider = { provideInlineCompletions: async (model: ITextModel, position: EditorPosition, context: languages.InlineCompletionContext, token: CancellationToken): Promise => { return this._proxy.$provideInlineCompletions(handle, model.uri, position, context, token); @@ -635,9 +635,15 @@ export class MainThreadLanguageFeatures extends Disposable implements MainThread freeInlineCompletions: (completions: IdentifiableInlineCompletions): void => { this._proxy.$freeInlineCompletionsList(handle, completions.pid); }, + handleRejection: async (completions, item): Promise => { + if (supportsHandleEvents) { + await this._proxy.$handleInlineCompletionRejection(handle, completions.pid, item.idx); + } + }, groupId: extensionId, yieldsToGroupIds: yieldsToExtensionIds, debounceDelayMs, + displayName, toString() { return `InlineCompletionsProvider(${extensionId})`; }, @@ -1033,7 +1039,7 @@ class MainThreadPasteEditProvider implements languages.DocumentPasteEditProvider if (metadata.supportsCopy) { this.prepareDocumentPaste = async (model: ITextModel, selections: readonly IRange[], dataTransfer: IReadonlyVSDataTransfer, token: CancellationToken): Promise => { - const dataTransferDto = await typeConvert.DataTransfer.from(dataTransfer); + const dataTransferDto = await typeConvert.DataTransfer.fromList(dataTransfer); if (token.isCancellationRequested) { return undefined; } @@ -1045,7 +1051,7 @@ class MainThreadPasteEditProvider implements languages.DocumentPasteEditProvider const dataTransferOut = new VSDataTransfer(); for (const [type, item] of newDataTransfer.items) { - dataTransferOut.replace(type, createStringDataTransferItem(item.asString)); + dataTransferOut.replace(type, createStringDataTransferItem(item.asString, item.id)); } return dataTransferOut; }; @@ -1055,7 +1061,7 @@ class MainThreadPasteEditProvider implements languages.DocumentPasteEditProvider this.provideDocumentPasteEdits = async (model: ITextModel, selections: Selection[], dataTransfer: IReadonlyVSDataTransfer, context: languages.DocumentPasteContext, token: CancellationToken) => { const request = this.dataTransfers.add(dataTransfer); try { - const dataTransferDto = await typeConvert.DataTransfer.from(dataTransfer); + const dataTransferDto = await typeConvert.DataTransfer.fromList(dataTransfer); if (token.isCancellationRequested) { return; } @@ -1139,7 +1145,7 @@ class MainThreadDocumentOnDropEditProvider implements languages.DocumentDropEdit async provideDocumentDropEdits(model: ITextModel, position: IPosition, dataTransfer: IReadonlyVSDataTransfer, token: CancellationToken): Promise { const request = this.dataTransfers.add(dataTransfer); try { - const dataTransferDto = await typeConvert.DataTransfer.from(dataTransfer); + const dataTransferDto = await typeConvert.DataTransfer.fromList(dataTransfer); if (token.isCancellationRequested) { return; } diff --git a/src/vs/workbench/api/browser/mainThreadMcp.ts b/src/vs/workbench/api/browser/mainThreadMcp.ts new file mode 100644 index 000000000000..b18bebf211f0 --- /dev/null +++ b/src/vs/workbench/api/browser/mainThreadMcp.ts @@ -0,0 +1,139 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Emitter } from '../../../base/common/event.js'; +import { Disposable, DisposableMap } from '../../../base/common/lifecycle.js'; +import { ISettableObservable, observableValue } from '../../../base/common/observable.js'; +import { IMcpMessageTransport, IMcpRegistry } from '../../contrib/mcp/common/mcpRegistryTypes.js'; +import { McpCollectionDefinition, McpConnectionState, McpServerDefinition, McpServerTransportType } from '../../contrib/mcp/common/mcpTypes.js'; +import { MCP } from '../../contrib/mcp/common/modelContextProtocol.js'; +import { ExtensionHostKind } from '../../services/extensions/common/extensionHostKind.js'; +import { IExtHostContext, extHostNamedCustomer } from '../../services/extensions/common/extHostCustomers.js'; +import { ExtHostContext, MainContext, MainThreadMcpShape } from '../common/extHost.protocol.js'; + +@extHostNamedCustomer(MainContext.MainThreadMcp) +export class MainThreadMcp extends Disposable implements MainThreadMcpShape { + + private _serverIdCounter = 0; + + private readonly _servers: Map = new Map(); + private readonly _collectionDefinitions = this._register(new DisposableMap; + dispose(): void; + }>()); + + constructor( + private readonly _extHostContext: IExtHostContext, + @IMcpRegistry private readonly _mcpRegistry: IMcpRegistry, + ) { + super(); + const proxy = _extHostContext.getProxy(ExtHostContext.ExtHostMcp); + this._register(this._mcpRegistry.registerDelegate({ + waitForInitialProviderPromises() { + return proxy.$waitForInitialCollectionProviders(); + }, + canStart(collection, serverDefinition) { + // todo: SSE MPC servers without a remote authority could be served from the renderer + if (collection.remoteAuthority !== _extHostContext.remoteAuthority) { + return false; + } + if (serverDefinition.launch.type === McpServerTransportType.Stdio && _extHostContext.extensionHostKind === ExtensionHostKind.LocalWebWorker) { + return false; + } + return true; + }, + start: (collection, _serverDefiniton, resolveLaunch) => { + const id = ++this._serverIdCounter; + const launch = new ExtHostMcpServerLaunch( + () => proxy.$stopMcp(id), + msg => proxy.$sendMessage(id, JSON.stringify(msg)), + ); + + this._servers.set(id, launch); + proxy.$startMcp(id, resolveLaunch); + + return launch; + }, + })); + } + + $upsertMcpCollection(collection: McpCollectionDefinition.FromExtHost, serversDto: McpServerDefinition.Serialized[]): void { + const servers = serversDto.map(McpServerDefinition.fromSerialized); + const existing = this._collectionDefinitions.get(collection.id); + if (existing) { + existing.servers.set(servers, undefined); + } else { + const serverDefinitions = observableValue('mcpServers', servers); + const handle = this._mcpRegistry.registerCollection({ + ...collection, + remoteAuthority: this._extHostContext.remoteAuthority, + serverDefinitions, + }); + + this._collectionDefinitions.set(collection.id, { + fromExtHost: collection, + servers: serverDefinitions, + dispose: () => handle.dispose(), + }); + } + } + + $deleteMcpCollection(collectionId: string): void { + this._collectionDefinitions.deleteAndDispose(collectionId); + } + + $onDidChangeState(id: number, update: McpConnectionState): void { + this._servers.get(id)?.state.set(update, undefined); + + if (update.state === McpConnectionState.Kind.Stopped || update.state === McpConnectionState.Kind.Error) { + this._servers.delete(id); + } + } + + $onDidPublishLog(id: number, log: string): void { + this._servers.get(id)?.pushLog(log); + } + + $onDidReceiveMessage(id: number, message: string): void { + this._servers.get(id)?.pushMessage(message); + } +} + + +class ExtHostMcpServerLaunch extends Disposable implements IMcpMessageTransport { + public readonly state = observableValue('mcpServerState', { state: McpConnectionState.Kind.Starting }); + + private readonly _onDidLog = this._register(new Emitter()); + public readonly onDidLog = this._onDidLog.event; + + private readonly _onDidReceiveMessage = this._register(new Emitter()); + public readonly onDidReceiveMessage = this._onDidReceiveMessage.event; + + pushLog(log: string): void { + this._onDidLog.fire(log); + } + + pushMessage(message: string): void { + let parsed: MCP.JSONRPCMessage | undefined; + try { + parsed = JSON.parse(message); + } catch (e) { + this.pushLog(`Failed to parse message: ${JSON.stringify(message)}`); + } + + if (parsed) { + this._onDidReceiveMessage.fire(parsed); + } + } + + constructor( + public readonly stop: () => void, + public readonly send: (message: MCP.JSONRPCMessage) => void, + ) { + super(); + } + +} diff --git a/src/vs/workbench/api/browser/mainThreadNotebookDocuments.ts b/src/vs/workbench/api/browser/mainThreadNotebookDocuments.ts index c2d1234aa836..72630519cca6 100644 --- a/src/vs/workbench/api/browser/mainThreadNotebookDocuments.ts +++ b/src/vs/workbench/api/browser/mainThreadNotebookDocuments.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { Event } from '../../../base/common/event.js'; import { DisposableStore, dispose } from '../../../base/common/lifecycle.js'; import { ResourceMap } from '../../../base/common/map.js'; import { URI, UriComponents } from '../../../base/common/uri.js'; @@ -131,7 +132,7 @@ export class MainThreadNotebookDocuments implements MainThreadNotebookDocumentsS // untitled notebooks are disposed when they get saved. we should not hold a reference // to such a disposed notebook and therefore dispose the reference as well - ref.object.notebook.onWillDispose(() => { + Event.once(ref.object.notebook.onWillDispose)(() => { ref.dispose(); }); diff --git a/src/vs/workbench/api/browser/mainThreadOutputService.ts b/src/vs/workbench/api/browser/mainThreadOutputService.ts index 063ed1901e71..434fc88d497c 100644 --- a/src/vs/workbench/api/browser/mainThreadOutputService.ts +++ b/src/vs/workbench/api/browser/mainThreadOutputService.ts @@ -106,7 +106,7 @@ export class MainThreadOutputService extends Disposable implements MainThreadOut statusProperties, 'status.view.showQuietly', StatusbarAlignment.RIGHT, - { id: 'status.notifications', alignment: StatusbarAlignment.LEFT } + { location: { id: 'status.notifications', priority: Number.NEGATIVE_INFINITY }, alignment: StatusbarAlignment.LEFT } ); } else { this._outputStatusItem.value.update(statusProperties); diff --git a/src/vs/workbench/api/browser/mainThreadSCM.ts b/src/vs/workbench/api/browser/mainThreadSCM.ts index 2e33d6f89a25..a6c361a18c7d 100644 --- a/src/vs/workbench/api/browser/mainThreadSCM.ts +++ b/src/vs/workbench/api/browser/mainThreadSCM.ts @@ -3,36 +3,24 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Barrier } from '../../../base/common/async.js'; -import { isUriComponents, URI, UriComponents } from '../../../base/common/uri.js'; -import { Event, Emitter } from '../../../base/common/event.js'; -import { IObservable, observableValue, observableValueOpts, transaction } from '../../../base/common/observable.js'; -import { IDisposable, DisposableStore, combinedDisposable, dispose, Disposable } from '../../../base/common/lifecycle.js'; -import { ISCMService, ISCMRepository, ISCMProvider, ISCMResource, ISCMResourceGroup, ISCMResourceDecorations, IInputValidation, ISCMViewService, InputValidationType, ISCMActionButtonDescriptor } from '../../contrib/scm/common/scm.js'; -import { ExtHostContext, MainThreadSCMShape, ExtHostSCMShape, SCMProviderFeatures, SCMRawResourceSplices, SCMGroupFeatures, MainContext, SCMHistoryItemDto, SCMHistoryItemRefsChangeEventDto, SCMHistoryItemRefDto } from '../common/extHost.protocol.js'; -import { Command } from '../../../editor/common/languages.js'; -import { extHostNamedCustomer, IExtHostContext } from '../../services/extensions/common/extHostCustomers.js'; -import { CancellationToken } from '../../../base/common/cancellation.js'; -import { MarshalledId } from '../../../base/common/marshallingIds.js'; -import { ThemeIcon } from '../../../base/common/themables.js'; -import { IMarkdownString } from '../../../base/common/htmlContent.js'; -import { IQuickDiffService, QuickDiffProvider } from '../../contrib/scm/common/quickDiff.js'; -import { ISCMHistoryItem, ISCMHistoryItemChange, ISCMHistoryItemRef, ISCMHistoryItemRefsChangeEvent, ISCMHistoryOptions, ISCMHistoryProvider } from '../../contrib/scm/common/history.js'; -import { ResourceTree } from '../../../base/common/resourceTree.js'; -import { IUriIdentityService } from '../../../platform/uriIdentity/common/uriIdentity.js'; -import { IWorkspaceContextService } from '../../../platform/workspace/common/workspace.js'; -import { basename } from '../../../base/common/resources.js'; -import { ILanguageService } from '../../../editor/common/languages/language.js'; -import { IModelService } from '../../../editor/common/services/model.js'; -import { ITextModelContentProvider, ITextModelService } from '../../../editor/common/services/resolverService.js'; -import { Schemas } from '../../../base/common/network.js'; -import { ITextModel } from '../../../editor/common/model.js'; -import { structuralEquals } from '../../../base/common/equals.js'; -import { historyItemBaseRefColor, historyItemRefColor, historyItemRemoteRefColor } from '../../contrib/scm/browser/scmHistory.js'; -import { ColorIdentifier } from '../../../platform/theme/common/colorUtils.js'; - -function getIconFromIconDto(iconDto?: UriComponents | { light: UriComponents; dark: UriComponents } | ThemeIcon): URI | { light: URI; dark: URI } | ThemeIcon | undefined { - if (iconDto === undefined) { +import { URI, UriComponents } from 'vs/base/common/uri'; +import { Event, Emitter } from 'vs/base/common/event'; +import { IDisposable, DisposableStore, combinedDisposable, dispose } from 'vs/base/common/lifecycle'; +import { ISCMService, ISCMRepository, ISCMProvider, ISCMResource, ISCMResourceGroup, ISCMResourceDecorations, IInputValidation, ISCMViewService, InputValidationType, ISCMActionButtonDescriptor } from 'vs/workbench/contrib/scm/common/scm'; +import { ExtHostContext, MainThreadSCMShape, ExtHostSCMShape, SCMProviderFeatures, SCMRawResourceSplices, SCMGroupFeatures, MainContext, SCMHistoryItemDto, SCMActionButtonDto, SCMHistoryItemGroupDto, SCMInputActionButtonDto } from '../common/extHost.protocol'; +import { Command } from 'vs/editor/common/languages'; +import { extHostNamedCustomer, IExtHostContext } from 'vs/workbench/services/extensions/common/extHostCustomers'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { MarshalledId } from 'vs/base/common/marshallingIds'; +import { ThemeIcon } from 'vs/base/common/themables'; +import { IMarkdownString } from 'vs/base/common/htmlContent'; +import { IQuickDiffService, QuickDiffProvider } from 'vs/workbench/contrib/scm/common/quickDiff'; +import { ISCMHistoryItem, ISCMHistoryItemChange, ISCMHistoryItemGroup, ISCMHistoryOptions, ISCMHistoryProvider } from 'vs/workbench/contrib/scm/common/history'; +import { ResourceTree } from 'vs/base/common/resourceTree'; +import { IUriIdentityService } from 'vs/platform/uriIdentity/common/uriIdentity'; + +function getSCMInputBoxActionButtonIcon(actionButton: SCMInputActionButtonDto): URI | { light: URI; dark: URI } | ThemeIcon | undefined { + if (!actionButton.icon) { return undefined; } else if (ThemeIcon.isThemeIcon(iconDto)) { return iconDto; @@ -102,8 +90,6 @@ class MainThreadSCMResourceGroup implements ISCMResourceGroup { get hideWhenEmpty(): boolean { return !!this.features.hideWhenEmpty; } - get contextValue(): string | undefined { return this.features.contextValue; } - constructor( private readonly sourceControlHandle: number, private readonly handle: number, @@ -111,7 +97,6 @@ class MainThreadSCMResourceGroup implements ISCMResourceGroup { public features: SCMGroupFeatures, public label: string, public id: string, - public readonly multiDiffEditorEnableViewChanges: boolean, private readonly _uriIdentService: IUriIdentityService ) { } @@ -299,20 +284,10 @@ class MainThreadSCMProvider implements ISCMProvider, QuickDiffProvider { private readonly _providerId: string, private readonly _label: string, private readonly _rootUri: URI | undefined, - private readonly _inputBoxTextModel: ITextModel, + private readonly _inputBoxDocumentUri: URI, private readonly _quickDiffService: IQuickDiffService, - private readonly _uriIdentService: IUriIdentityService, - private readonly _workspaceContextService: IWorkspaceContextService - ) { - if (_rootUri) { - const folder = this._workspaceContextService.getWorkspaceFolder(_rootUri); - if (folder?.uri.toString() === _rootUri.toString()) { - this._name = folder.name; - } else if (_rootUri.path !== '/') { - this._name = basename(_rootUri); - } - } - } + private readonly _uriIdentService: IUriIdentityService + ) { } $updateSourceControl(features: SCMProviderFeatures): void { this.features = { ...this.features, ...features }; @@ -363,7 +338,6 @@ class MainThreadSCMProvider implements ISCMProvider, QuickDiffProvider { features, label, id, - multiDiffEditorEnableViewChanges, this._uriIdentService ); @@ -507,12 +481,8 @@ export class MainThreadSCM implements MainThreadSCMShape { extHostContext: IExtHostContext, @ISCMService private readonly scmService: ISCMService, @ISCMViewService private readonly scmViewService: ISCMViewService, - @ILanguageService private readonly languageService: ILanguageService, - @IModelService private readonly modelService: IModelService, - @ITextModelService private readonly textModelService: ITextModelService, @IQuickDiffService private readonly quickDiffService: IQuickDiffService, - @IUriIdentityService private readonly _uriIdentService: IUriIdentityService, - @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService + @IUriIdentityService private readonly _uriIdentService: IUriIdentityService ) { this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostSCM); @@ -529,11 +499,8 @@ export class MainThreadSCM implements MainThreadSCMShape { this._disposables.dispose(); } - async $registerSourceControl(handle: number, id: string, label: string, rootUri: UriComponents | undefined, inputBoxDocumentUri: UriComponents): Promise { - this._repositoryBarriers.set(handle, new Barrier()); - - const inputBoxTextModelRef = await this.textModelService.createModelReference(URI.revive(inputBoxDocumentUri)); - const provider = new MainThreadSCMProvider(this._proxy, handle, id, label, rootUri ? URI.revive(rootUri) : undefined, inputBoxTextModelRef.object.textEditorModel, this.quickDiffService, this._uriIdentService, this.workspaceContextService); + $registerSourceControl(handle: number, id: string, label: string, rootUri: UriComponents | undefined, inputBoxDocumentUri: UriComponents): void { + const provider = new MainThreadSCMProvider(this._proxy, handle, id, label, rootUri ? URI.revive(rootUri) : undefined, URI.revive(inputBoxDocumentUri), this.quickDiffService, this._uriIdentService); const repository = this.scmService.registerSCMProvider(provider); this._repositories.set(handle, repository); diff --git a/src/vs/workbench/api/browser/mainThreadTask.ts b/src/vs/workbench/api/browser/mainThreadTask.ts index 52e977fe25d9..c3a1c502e5f0 100644 --- a/src/vs/workbench/api/browser/mainThreadTask.ts +++ b/src/vs/workbench/api/browser/mainThreadTask.ts @@ -15,7 +15,7 @@ import { Disposable, IDisposable } from '../../../base/common/lifecycle.js'; import { IWorkspace, IWorkspaceContextService, IWorkspaceFolder } from '../../../platform/workspace/common/workspace.js'; import { - ContributedTask, ConfiguringTask, KeyedTaskIdentifier, ITaskExecution, Task, ITaskEvent, TaskEventKind, + ContributedTask, ConfiguringTask, KeyedTaskIdentifier, ITaskExecution, Task, ITaskEvent, IPresentationOptions, CommandOptions, ICommandConfiguration, RuntimeType, CustomTask, TaskScope, TaskSource, TaskSourceKind, IExtensionTaskSource, IRunOptions, ITaskSet, TaskGroup, TaskDefinition, PresentationOptions, RunOptions } from '../../contrib/tasks/common/tasks.js'; @@ -29,7 +29,9 @@ import { ExtHostContext, MainThreadTaskShape, ExtHostTaskShape, MainContext } fr import { ITaskDefinitionDTO, ITaskExecutionDTO, IProcessExecutionOptionsDTO, ITaskPresentationOptionsDTO, IProcessExecutionDTO, IShellExecutionDTO, IShellExecutionOptionsDTO, ICustomExecutionDTO, ITaskDTO, ITaskSourceDTO, ITaskHandleDTO, ITaskFilterDTO, ITaskProcessStartedDTO, ITaskProcessEndedDTO, ITaskSystemInfoDTO, - IRunOptionsDTO, ITaskGroupDTO + IRunOptionsDTO, ITaskGroupDTO, + ITaskStatus, + TaskEventKind } from '../common/shared/tasks.js'; import { IConfigurationResolverService } from '../../services/configurationResolver/common/configurationResolver.js'; import { ConfigurationTarget } from '../../../platform/configuration/common/configuration.js'; @@ -45,6 +47,24 @@ namespace TaskExecutionDTO { } } +export interface ITaskTerminalStatusDTO { + execution: ITaskExecutionDTO; + taskEventKind: TaskEventKind; +} + +export namespace TaskTerminalStatusDTO { + export function from(value: ITaskStatus): ITaskTerminalStatusDTO { + return { + execution: { + id: value.execution.id, + task: TaskDTO.from(value.execution.task) + }, + taskEventKind: value.taskEventKind + }; + } +} + + namespace TaskProcessStartedDTO { export function from(value: ITaskExecution, processId: number): ITaskProcessStartedDTO { return { @@ -455,6 +475,7 @@ export class MainThreadTask extends Disposable implements MainThreadTaskShape { } else if (event.kind === TaskEventKind.End) { this._proxy.$OnDidEndTask(TaskExecutionDTO.from(task.getTaskExecution())); } + this._proxy.$onDidChangeTaskTerminalStatus(TaskTerminalStatusDTO.from({ execution: task.getTaskExecution(), taskEventKind: event.kind })); })); } diff --git a/src/vs/workbench/api/browser/mainThreadTerminalService.ts b/src/vs/workbench/api/browser/mainThreadTerminalService.ts index ba135ce62159..7c3a1df01711 100644 --- a/src/vs/workbench/api/browser/mainThreadTerminalService.ts +++ b/src/vs/workbench/api/browser/mainThreadTerminalService.ts @@ -276,11 +276,10 @@ export class MainThreadTerminalService implements MainThreadTerminalServiceShape id, provideCompletions: async (commandLine, cursorPosition, allowFallbackCompletions, token) => { const completions = await this._proxy.$provideTerminalCompletions(id, { commandLine, cursorPosition, allowFallbackCompletions }, token); - if (Array.isArray(completions)) { - return completions.map(c => ({ ...c, provider: id })); - } else { - return { items: completions?.items.map(c => ({ ...c, provider: id })), resourceRequestConfig: completions?.resourceRequestConfig }; - } + return { + items: completions?.items.map(c => ({ ...c, provider: id })), + resourceRequestConfig: completions?.resourceRequestConfig + }; } }, ...triggerCharacters)); } diff --git a/src/vs/workbench/api/browser/mainThreadTerminalShellIntegration.ts b/src/vs/workbench/api/browser/mainThreadTerminalShellIntegration.ts index bfe720fc0872..aa0fd7d8b17c 100644 --- a/src/vs/workbench/api/browser/mainThreadTerminalShellIntegration.ts +++ b/src/vs/workbench/api/browser/mainThreadTerminalShellIntegration.ts @@ -33,26 +33,23 @@ export class MainThreadTerminalShellIntegration extends Disposable implements Ma } })); - // onDidchangeTerminalShellIntegration initial state + // onDidChangeTerminalShellIntegration initial state for (const terminal of this._terminalService.instances) { - if (terminal.capabilities.has(TerminalCapability.CommandDetection)) { + const cmdDetection = terminal.capabilities.get(TerminalCapability.CommandDetection); + if (cmdDetection?.hasRichCommandDetection) { this._proxy.$shellIntegrationChange(terminal.instanceId); - const cwdDetection = terminal.capabilities.get(TerminalCapability.CwdDetection); - if (cwdDetection) { - this._proxy.$cwdChange(terminal.instanceId, this._convertCwdToUri(cwdDetection.getCwd())); - } + } + const cwdDetection = terminal.capabilities.get(TerminalCapability.CwdDetection); + if (cwdDetection) { + this._proxy.$cwdChange(terminal.instanceId, this._convertCwdToUri(cwdDetection.getCwd())); } } - // onDidChangeTerminalShellIntegration via command detection - const onDidAddCommandDetection = this._store.add(this._terminalService.createOnInstanceEvent(instance => { - return Event.map( - Event.filter(instance.capabilities.onDidAddCapabilityType, e => { - return e === TerminalCapability.CommandDetection || e === TerminalCapability.CwdDetection; - }), () => instance - ); - })).event; - this._store.add(onDidAddCommandDetection(e => this._proxy.$shellIntegrationChange(e.instanceId))); + // onDidChangeTerminalShellIntegration via rich command detection + const onDidSetRichCommandDetection = this._store.add(this._terminalService.createOnInstanceCapabilityEvent(TerminalCapability.CommandDetection, e => e.onSetRichCommandDetection)); + this._store.add(onDidSetRichCommandDetection.event(e => { + this._proxy.$shellIntegrationChange(e.instance.instanceId); + })); // onDidChangeTerminalShellIntegration via cwd const cwdChangeEvent = this._store.add(this._terminalService.createOnInstanceCapabilityEvent(TerminalCapability.CwdDetection, e => e.onDidChangeCwd)); @@ -63,9 +60,14 @@ export class MainThreadTerminalShellIntegration extends Disposable implements Ma // onDidChangeTerminalShellIntegration via env const envChangeEvent = this._store.add(this._terminalService.createOnInstanceCapabilityEvent(TerminalCapability.ShellEnvDetection, e => e.onDidChangeEnv)); this._store.add(envChangeEvent.event(e => { - const keysArr = Array.from(e.data.keys()); - const valuesArr = Array.from(e.data.values()); - this._proxy.$shellEnvChange(e.instance.instanceId, keysArr, valuesArr); + if (e.data.value && typeof e.data.value === 'object') { + const envValue = e.data.value as { [key: string]: string | undefined }; + + // Extract keys and values + const keysArr = Object.keys(envValue); + const valuesArr = Object.values(envValue); + this._proxy.$shellEnvChange(e.instance.instanceId, keysArr, valuesArr as string[], e.data.isTrusted); + } })); // onDidStartTerminalShellExecution @@ -85,7 +87,9 @@ export class MainThreadTerminalShellIntegration extends Disposable implements Ma // TerminalShellExecution.createDataStream // Debounce events to reduce the message count - when this listener is disposed the events will be flushed instanceDataListeners.get(instanceId)?.dispose(); - instanceDataListeners.set(instanceId, Event.accumulate(e.instance.onData, 50, this._store)(events => this._proxy.$shellExecutionData(instanceId, events.join('')))); + instanceDataListeners.set(instanceId, Event.accumulate(e.instance.onData, 50, this._store)(events => { + this._proxy.$shellExecutionData(instanceId, events.join('')); + })); })); // onDidEndTerminalShellExecution @@ -94,10 +98,10 @@ export class MainThreadTerminalShellIntegration extends Disposable implements Ma currentCommand = undefined; const instanceId = e.instance.instanceId; instanceDataListeners.get(instanceId)?.dispose(); - // Send end in a microtask to ensure the data events are sent first - setTimeout(() => { - this._proxy.$shellExecutionEnd(instanceId, e.data.command, convertToExtHostCommandLineConfidence(e.data), e.data.isTrusted, e.data.exitCode); - }); + // Shell integration C (executed) and D (command finished) sequences should always be in + // their own events, so send this immediately. This means that the D sequence will not + // be included as it's currently being parsed when the command finished event fires. + this._proxy.$shellExecutionEnd(instanceId, e.data.command, convertToExtHostCommandLineConfidence(e.data), e.data.isTrusted, e.data.exitCode); })); // Clean up after dispose diff --git a/src/vs/workbench/api/browser/mainThreadTreeViews.ts b/src/vs/workbench/api/browser/mainThreadTreeViews.ts index dc7ca02ac90c..ac6eb174bfe9 100644 --- a/src/vs/workbench/api/browser/mainThreadTreeViews.ts +++ b/src/vs/workbench/api/browser/mainThreadTreeViews.ts @@ -227,7 +227,7 @@ class TreeViewDragAndDropController implements ITreeViewDragAndDropController { operationUuid?: string, sourceTreeId?: string, sourceTreeItemHandles?: string[]): Promise { const request = this.dataTransfersCache.add(dataTransfer); try { - const dataTransferDto = await typeConvert.DataTransfer.from(dataTransfer); + const dataTransferDto = await typeConvert.DataTransfer.fromList(dataTransfer); if (token.isCancellationRequested) { return; } diff --git a/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts b/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts index 3a7ddf961e8e..e96588a20c91 100644 --- a/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts +++ b/src/vs/workbench/api/browser/mainThreadWebviewPanels.ts @@ -10,9 +10,6 @@ import { URI } from '../../../base/common/uri.js'; import { generateUuid } from '../../../base/common/uuid.js'; import { IConfigurationService } from '../../../platform/configuration/common/configuration.js'; import { IStorageService } from '../../../platform/storage/common/storage.js'; -import { ITelemetryService } from '../../../platform/telemetry/common/telemetry.js'; -import { MainThreadWebviews, reviveWebviewContentOptions, reviveWebviewExtension } from './mainThreadWebviews.js'; -import * as extHostProtocol from '../common/extHost.protocol.js'; import { DiffEditorInput } from '../../common/editor/diffEditorInput.js'; import { EditorInput } from '../../common/editor/editorInput.js'; import { ExtensionKeyedWebviewOriginStore, WebviewOptions } from '../../contrib/webview/browser/webview.js'; @@ -24,6 +21,8 @@ import { GroupLocation, GroupsOrder, IEditorGroup, IEditorGroupsService, preferr import { ACTIVE_GROUP, IEditorService, PreferredGroup, SIDE_GROUP } from '../../services/editor/common/editorService.js'; import { IExtensionService } from '../../services/extensions/common/extensions.js'; import { IExtHostContext } from '../../services/extensions/common/extHostCustomers.js'; +import * as extHostProtocol from '../common/extHost.protocol.js'; +import { MainThreadWebviews, reviveWebviewContentOptions, reviveWebviewExtension } from './mainThreadWebviews.js'; /** * Bi-directional map between webview handles and inputs. @@ -98,7 +97,6 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc @IEditorService private readonly _editorService: IEditorService, @IExtensionService extensionService: IExtensionService, @IStorageService storageService: IStorageService, - @ITelemetryService private readonly _telemetryService: ITelemetryService, @IWebviewWorkbenchService private readonly _webviewWorkbenchService: IWebviewWorkbenchService, ) { super(); @@ -174,20 +172,6 @@ export class MainThreadWebviewPanels extends Disposable implements extHostProtoc }, this.webviewPanelViewType.fromExternal(viewType), initData.title, mainThreadShowOptions); this.addWebviewInput(handle, webview, { serializeBuffersForPostMessage: initData.serializeBuffersForPostMessage }); - - const payload = { - extensionId: extension.id.value, - viewType - } as const; - - type Classification = { - extensionId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Id of the extension that created the webview panel' }; - viewType: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Id of the webview' }; - owner: 'mjbvz'; - comment: 'Triggered when a webview is created. Records the type of webview and the extension which created it'; - }; - - this._telemetryService.publicLog2('webviews:createWebviewPanel', payload); } public $disposeWebview(handle: extHostProtocol.WebviewHandle): void { diff --git a/src/vs/workbench/api/browser/mainThreadWorkspace.ts b/src/vs/workbench/api/browser/mainThreadWorkspace.ts index dae04c793583..89b4f98c69e1 100644 --- a/src/vs/workbench/api/browser/mainThreadWorkspace.ts +++ b/src/vs/workbench/api/browser/mainThreadWorkspace.ts @@ -39,7 +39,7 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape { private readonly _toDispose = new DisposableStore(); private readonly _activeCancelTokens: { [id: number]: CancellationTokenSource } = Object.create(null); private readonly _proxy: ExtHostWorkspaceShape; - private readonly _queryBuilder = this._instantiationService.createInstance(QueryBuilder); + private readonly _queryBuilder: QueryBuilder; constructor( extHostContext: IExtHostContext, @@ -59,6 +59,7 @@ export class MainThreadWorkspace implements MainThreadWorkspaceShape { @IWorkspaceTrustRequestService private readonly _workspaceTrustRequestService: IWorkspaceTrustRequestService, @ITextFileService private readonly _textFileService: ITextFileService, ) { + this._queryBuilder = this._instantiationService.createInstance(QueryBuilder); this._proxy = extHostContext.getProxy(ExtHostContext.ExtHostWorkspace); const workspace = this._contextService.getWorkspace(); // The workspace file is provided be a unknown file system provider. It might come diff --git a/src/vs/workbench/api/browser/viewsExtensionPoint.ts b/src/vs/workbench/api/browser/viewsExtensionPoint.ts index 9a40656e9906..fdf354d1f544 100644 --- a/src/vs/workbench/api/browser/viewsExtensionPoint.ts +++ b/src/vs/workbench/api/browser/viewsExtensionPoint.ts @@ -71,7 +71,8 @@ export const viewsContainersContribution: IJSONSchema = { type: 'array', items: viewsContainerSchema } - } + }, + additionalProperties: false }; enum ViewType { diff --git a/src/vs/workbench/api/common/configurationExtensionPoint.ts b/src/vs/workbench/api/common/configurationExtensionPoint.ts index 8b54b9e5a1d7..d96bc0160c22 100644 --- a/src/vs/workbench/api/common/configurationExtensionPoint.ts +++ b/src/vs/workbench/api/common/configurationExtensionPoint.ts @@ -10,7 +10,7 @@ import { IJSONSchema } from '../../../base/common/jsonSchema.js'; import { ExtensionsRegistry, IExtensionPointUser } from '../../services/extensions/common/extensionsRegistry.js'; import { IConfigurationNode, IConfigurationRegistry, Extensions, validateProperty, ConfigurationScope, OVERRIDE_PROPERTY_REGEX, IConfigurationDefaults, configurationDefaultsSchemaId, IConfigurationDelta, getDefaultValue, getAllConfigurationProperties, parseScope } from '../../../platform/configuration/common/configurationRegistry.js'; import { IJSONContributionRegistry, Extensions as JSONExtensions } from '../../../platform/jsonschemas/common/jsonContributionRegistry.js'; -import { workspaceSettingsSchemaId, launchSchemaId, tasksSchemaId } from '../../services/configuration/common/configuration.js'; +import { workspaceSettingsSchemaId, launchSchemaId, tasksSchemaId, mcpSchemaId } from '../../services/configuration/common/configuration.js'; import { isObject, isUndefined } from '../../../base/common/types.js'; import { ExtensionIdentifierMap, IExtensionManifest } from '../../../platform/extensions/common/extensions.js'; import { IStringDictionary } from '../../../base/common/collections.js'; @@ -367,6 +367,20 @@ jsonRegistry.registerSchema('vscode://schemas/workspaceConfig', { description: nls.localize('workspaceConfig.tasks.description', "Workspace task configurations"), $ref: tasksSchemaId }, + 'mcp': { + type: 'object', + default: { + inputs: [], + servers: { + 'mcp-server-time': { + command: 'python', + args: ['-m', 'mcp_server_time', '--local-timezone=America/Los_Angeles'] + } + } + }, + description: nls.localize('workspaceConfig.mcp.description', "Model Context Protocol server configurations"), + $ref: mcpSchemaId + }, 'extensions': { type: 'object', default: {}, diff --git a/src/vs/workbench/api/common/extHost.api.impl.ts b/src/vs/workbench/api/common/extHost.api.impl.ts index f926cde3ee26..21ca7ffcc3f5 100644 --- a/src/vs/workbench/api/common/extHost.api.impl.ts +++ b/src/vs/workbench/api/common/extHost.api.impl.ts @@ -108,6 +108,7 @@ import { ProxyIdentifier } from '../../services/extensions/common/proxyIdentifie import { ExcludeSettingOptions, TextSearchCompleteMessageType, TextSearchContext2, TextSearchMatch2 } from '../../services/search/common/searchExtTypes.js'; import type * as vscode from 'vscode'; import { ExtHostCodeMapper } from './extHostCodeMapper.js'; +import { IExtHostMpcService } from './extHostMcp.js'; export interface IExtensionRegistries { mine: ExtensionDescriptionRegistry; @@ -145,6 +146,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const extHostManagedSockets = accessor.get(IExtHostManagedSockets); const extHostAuthentication = accessor.get(IExtHostAuthentication); const extHostLanguageModels = accessor.get(IExtHostLanguageModels); + const extHostMcp = accessor.get(IExtHostMpcService); // register addressable instances rpcProtocol.set(ExtHostContext.ExtHostFileSystemInfo, extHostFileSystemInfo); @@ -211,13 +213,14 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I const extHostUriOpeners = rpcProtocol.set(ExtHostContext.ExtHostUriOpeners, new ExtHostUriOpeners(rpcProtocol)); const extHostProfileContentHandlers = rpcProtocol.set(ExtHostContext.ExtHostProfileContentHandlers, new ExtHostProfileContentHandlers(rpcProtocol)); rpcProtocol.set(ExtHostContext.ExtHostInteractive, new ExtHostInteractive(rpcProtocol, extHostNotebook, extHostDocumentsAndEditors, extHostCommands, extHostLogService)); - const extHostChatAgents2 = rpcProtocol.set(ExtHostContext.ExtHostChatAgents2, new ExtHostChatAgents2(rpcProtocol, extHostLogService, extHostCommands, extHostDocuments, extHostLanguageModels)); const extHostLanguageModelTools = rpcProtocol.set(ExtHostContext.ExtHostLanguageModelTools, new ExtHostLanguageModelTools(rpcProtocol)); + const extHostChatAgents2 = rpcProtocol.set(ExtHostContext.ExtHostChatAgents2, new ExtHostChatAgents2(rpcProtocol, extHostLogService, extHostCommands, extHostDocuments, extHostLanguageModels, extHostDiagnostics, extHostLanguageModelTools)); const extHostAiRelatedInformation = rpcProtocol.set(ExtHostContext.ExtHostAiRelatedInformation, new ExtHostRelatedInformation(rpcProtocol)); const extHostAiEmbeddingVector = rpcProtocol.set(ExtHostContext.ExtHostAiEmbeddingVector, new ExtHostAiEmbeddingVector(rpcProtocol)); const extHostStatusBar = rpcProtocol.set(ExtHostContext.ExtHostStatusBar, new ExtHostStatusBar(rpcProtocol, extHostCommands.converter)); const extHostSpeech = rpcProtocol.set(ExtHostContext.ExtHostSpeech, new ExtHostSpeech(rpcProtocol)); const extHostEmbeddings = rpcProtocol.set(ExtHostContext.ExtHostEmbeddings, new ExtHostEmbeddings(rpcProtocol)); + rpcProtocol.set(ExtHostContext.ExtHostMcp, accessor.get(IExtHostMpcService)); // Check that no named customers are missing const expected = Object.values>(ExtHostContext); @@ -1024,10 +1027,14 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I set textDocuments(value) { throw new errors.ReadonlyError('textDocuments'); }, - openTextDocument(uriOrFileNameOrOptions?: vscode.Uri | string | { language?: string; content?: string }) { + openTextDocument(uriOrFileNameOrOptions?: vscode.Uri | string | { language?: string; content?: string; encoding?: string }, options?: { encoding?: string }) { let uriPromise: Thenable; - const options = uriOrFileNameOrOptions as { language?: string; content?: string }; + options = (options ?? uriOrFileNameOrOptions) as ({ language?: string; content?: string; encoding?: string } | undefined); + if (typeof options?.encoding === 'string') { + checkProposedApiEnabled(extension, 'textDocumentEncoding'); + } + if (typeof uriOrFileNameOrOptions === 'string') { uriPromise = Promise.resolve(URI.file(uriOrFileNameOrOptions)); } else if (URI.isUri(uriOrFileNameOrOptions)) { @@ -1043,7 +1050,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I if (uri.scheme === Schemas.vscodeRemote && !uri.authority) { extHostApiDeprecation.report('workspace.openTextDocument', extension, `A URI of 'vscode-remote' scheme requires an authority.`); } - return extHostDocuments.ensureDocumentData(uri).then(documentData => { + return extHostDocuments.ensureDocumentData(uri, options).then(documentData => { return documentData.document; }); }); @@ -1351,6 +1358,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I }, onDidEndTaskProcess: (listeners, thisArgs?, disposables?) => { return _asExtensionEvent(extHostTask.onDidEndTaskProcess)(listeners, thisArgs, disposables); + }, + onDidChangeTaskStatus: (listeners) => { + checkProposedApiEnabled(extension, 'taskStatus'); + return _asExtensionEvent(extHostTask.onDidChangeTaskTerminalStatus)(listeners); } }; @@ -1504,6 +1515,10 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I }, registerIgnoredFileProvider(provider: vscode.LanguageModelIgnoredFileProvider) { return extHostLanguageModels.registerIgnoredFileProvider(extension, provider); + }, + registerMcpConfigurationProvider(id, provider) { + checkProposedApiEnabled(extension, 'mcpConfigurationProvider'); + return extHostMcp.registerMcpConfigurationProvider(extension, id, provider); } }; @@ -1544,6 +1559,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I ChatResultFeedbackKind: extHostTypes.ChatResultFeedbackKind, ChatVariableLevel: extHostTypes.ChatVariableLevel, ChatCompletionItem: extHostTypes.ChatCompletionItem, + ChatReferenceDiagnostic: extHostTypes.ChatReferenceDiagnostic, CallHierarchyIncomingCall: extHostTypes.CallHierarchyIncomingCall, CallHierarchyItem: extHostTypes.CallHierarchyItem, CallHierarchyOutgoingCall: extHostTypes.CallHierarchyOutgoingCall, @@ -1647,6 +1663,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I SymbolKind: extHostTypes.SymbolKind, SymbolTag: extHostTypes.SymbolTag, Task: extHostTypes.Task, + TaskEventKind: extHostTypes.TaskEventKind, TaskGroup: extHostTypes.TaskGroup, TaskPanelKind: extHostTypes.TaskPanelKind, TaskRevealKind: extHostTypes.TaskRevealKind, @@ -1789,6 +1806,7 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I LanguageModelError: extHostTypes.LanguageModelError, LanguageModelToolResult: extHostTypes.LanguageModelToolResult, ExtendedLanguageModelToolResult: extHostTypes.ExtendedLanguageModelToolResult, + PreparedTerminalToolInvocation: extHostTypes.PreparedTerminalToolInvocation, LanguageModelChatToolMode: extHostTypes.LanguageModelChatToolMode, LanguageModelPromptTsxPart: extHostTypes.LanguageModelPromptTsxPart, NewSymbolName: extHostTypes.NewSymbolName, @@ -1800,6 +1818,9 @@ export function createApiFactoryAndRegisterActors(accessor: ServicesAccessor): I TextSearchContext2: TextSearchContext2, TextSearchMatch2: TextSearchMatch2, TextSearchCompleteMessageTypeNew: TextSearchCompleteMessageType, + ChatErrorLevel: extHostTypes.ChatErrorLevel, + McpSSEServerDefinition: extHostTypes.McpSSEServerDefinition, + McpStdioServerDefinition: extHostTypes.McpStdioServerDefinition, }; }; } diff --git a/src/vs/workbench/api/common/extHost.common.services.ts b/src/vs/workbench/api/common/extHost.common.services.ts index c81dd9fab4b0..5551bfa74522 100644 --- a/src/vs/workbench/api/common/extHost.common.services.ts +++ b/src/vs/workbench/api/common/extHost.common.services.ts @@ -31,6 +31,7 @@ import { ExtHostAuthentication, IExtHostAuthentication } from './extHostAuthenti import { ExtHostLanguageModels, IExtHostLanguageModels } from './extHostLanguageModels.js'; import { IExtHostTerminalShellIntegration, ExtHostTerminalShellIntegration } from './extHostTerminalShellIntegration.js'; import { ExtHostTesting, IExtHostTesting } from './extHostTesting.js'; +import { ExtHostMcpService, IExtHostMpcService } from './extHostMcp.js'; registerSingleton(IExtHostLocalizationService, ExtHostLocalizationService, InstantiationType.Delayed); registerSingleton(ILoggerService, ExtHostLoggerService, InstantiationType.Delayed); @@ -58,3 +59,4 @@ registerSingleton(IExtHostWorkspace, ExtHostWorkspace, InstantiationType.Eager); registerSingleton(IExtHostSecretState, ExtHostSecretState, InstantiationType.Eager); registerSingleton(IExtHostEditorTabs, ExtHostEditorTabs, InstantiationType.Eager); registerSingleton(IExtHostVariableResolverProvider, ExtHostVariableResolverProviderService, InstantiationType.Eager); +registerSingleton(IExtHostMpcService, ExtHostMcpService, InstantiationType.Eager); diff --git a/src/vs/workbench/api/common/extHost.protocol.ts b/src/vs/workbench/api/common/extHost.protocol.ts index 0e4072c38d90..f95ba612466e 100644 --- a/src/vs/workbench/api/common/extHost.protocol.ts +++ b/src/vs/workbench/api/common/extHost.protocol.ts @@ -51,15 +51,17 @@ import { WorkspaceTrustRequestOptions } from '../../../platform/workspace/common import { SaveReason } from '../../common/editor.js'; import { IRevealOptions, ITreeItem, IViewBadge } from '../../common/views.js'; import { CallHierarchyItem } from '../../contrib/callHierarchy/common/callHierarchy.js'; -import { ChatAgentLocation, IChatAgentMetadata, IChatAgentRequest, IChatAgentResult, IChatWelcomeMessageContent } from '../../contrib/chat/common/chatAgents.js'; +import { IChatAgentMetadata, IChatAgentRequest, IChatAgentResult } from '../../contrib/chat/common/chatAgents.js'; import { ICodeMapperRequest, ICodeMapperResult } from '../../contrib/chat/common/chatCodeMapperService.js'; import { IChatRelatedFile, IChatRelatedFileProviderMetadata as IChatRelatedFilesProviderMetadata, IChatRequestDraft } from '../../contrib/chat/common/chatEditingService.js'; import { IChatProgressHistoryResponseContent } from '../../contrib/chat/common/chatModel.js'; import { IChatContentInlineReference, IChatFollowup, IChatNotebookEdit, IChatProgress, IChatResponseErrorDetails, IChatTask, IChatTaskDto, IChatUserActionEvent, IChatVoteAction } from '../../contrib/chat/common/chatService.js'; import { IChatRequestVariableValue } from '../../contrib/chat/common/chatVariables.js'; +import { ChatAgentLocation } from '../../contrib/chat/common/constants.js'; import { IChatMessage, IChatResponseFragment, ILanguageModelChatMetadata, ILanguageModelChatSelector, ILanguageModelsChangeEvent } from '../../contrib/chat/common/languageModels.js'; import { IPreparedToolInvocation, IToolData, IToolInvocation, IToolResult } from '../../contrib/chat/common/languageModelToolsService.js'; import { DebugConfigurationProviderTriggerKind, IAdapterDescriptor, IConfig, IDebugSessionReplMode, IDebugTestRunReference, IDebugVisualization, IDebugVisualizationContext, IDebugVisualizationTreeItem, MainThreadDebugVisualization } from '../../contrib/debug/common/debug.js'; +import { McpCollectionDefinition, McpConnectionState, McpServerDefinition, McpServerLaunch } from '../../contrib/mcp/common/mcpTypes.js'; import * as notebookCommon from '../../contrib/notebook/common/notebookCommon.js'; import { CellExecutionUpdateType } from '../../contrib/notebook/common/notebookExecutionService.js'; import { ICellExecutionComplete, ICellExecutionStateUpdate } from '../../contrib/notebook/common/notebookExecutionStateService.js'; @@ -238,8 +240,8 @@ export interface MainThreadDocumentContentProvidersShape extends IDisposable { } export interface MainThreadDocumentsShape extends IDisposable { - $tryCreateDocument(options?: { language?: string; content?: string }): Promise; - $tryOpenDocument(uri: UriComponents): Promise; + $tryCreateDocument(options?: { language?: string; content?: string; encoding?: string }): Promise; + $tryOpenDocument(uri: UriComponents, options?: { encoding?: string }): Promise; $trySaveDocument(uri: UriComponents): Promise; } @@ -472,7 +474,7 @@ export interface MainThreadLanguageFeaturesShape extends IDisposable { $emitDocumentSemanticTokensEvent(eventHandle: number): void; $registerDocumentRangeSemanticTokensProvider(handle: number, selector: IDocumentFilterDto[], legend: languages.SemanticTokensLegend): void; $registerCompletionsProvider(handle: number, selector: IDocumentFilterDto[], triggerCharacters: string[], supportsResolveDetails: boolean, extensionId: ExtensionIdentifier): void; - $registerInlineCompletionsSupport(handle: number, selector: IDocumentFilterDto[], supportsHandleDidShowCompletionItem: boolean, extensionId: string, yieldsToExtensionIds: string[], debounceDelayMs: number | undefined): void; + $registerInlineCompletionsSupport(handle: number, selector: IDocumentFilterDto[], supportsHandleDidShowCompletionItem: boolean, extensionId: string, yieldsToExtensionIds: string[], displayName: string | undefined, debounceDelayMs: number | undefined): void; $registerInlineEditProvider(handle: number, selector: IDocumentFilterDto[], extensionId: ExtensionIdentifier, displayName: string): void; $registerSignatureHelpProvider(handle: number, selector: IDocumentFilterDto[], metadata: ISignatureHelpProviderMetadataDto): void; $registerInlayHintsProvider(handle: number, selector: IDocumentFilterDto[], supportsResolve: boolean, eventHandle: number | undefined, displayName: string | undefined): void; @@ -1356,7 +1358,6 @@ export interface ExtHostChatAgentsShape2 { $acceptFeedback(handle: number, result: IChatAgentResult, voteAction: IChatVoteAction): void; $acceptAction(handle: number, result: IChatAgentResult, action: IChatUserActionEvent): void; $invokeCompletionProvider(handle: number, query: string, token: CancellationToken): Promise; - $provideWelcomeMessage(handle: number, token: CancellationToken): Promise; $provideChatTitle(handle: number, context: IChatAgentHistoryEntryDto[], token: CancellationToken): Promise; $provideSampleQuestions(handle: number, location: ChatAgentLocation, token: CancellationToken): Promise; $releaseSession(sessionId: string): void; @@ -1889,13 +1890,14 @@ export interface IDataTransferFileDTO { } export interface DataTransferItemDTO { + id: string; readonly asString: string; readonly fileData: IDataTransferFileDTO | undefined; readonly uriListData?: ReadonlyArray; } export interface DataTransferDTO { - readonly items: Array<[/* type */string, DataTransferItemDTO]>; + items: Array; } export interface CheckboxUpdate { @@ -2333,6 +2335,7 @@ export interface ExtHostLanguageFeaturesShape { $provideInlineEditsForRange(handle: number, resource: UriComponents, range: IRange, context: languages.InlineCompletionContext, token: CancellationToken): Promise; $handleInlineCompletionDidShow(handle: number, pid: number, idx: number, updatedInsertText: string): void; $handleInlineCompletionPartialAccept(handle: number, pid: number, idx: number, acceptedCharacters: number, info: languages.PartialAcceptInfo): void; + $handleInlineCompletionRejection(handle: number, pid: number, idx: number): void; $freeInlineCompletionsList(handle: number, pid: number): void; $provideSignatureHelp(handle: number, resource: UriComponents, position: IPosition, context: languages.SignatureHelpContext, token: CancellationToken): Promise; $releaseSignatureHelp(handle: number, id: number): void; @@ -2479,6 +2482,7 @@ export class TerminalCompletionListDto; $jsonTasksSupported(): Promise; $findExecutable(command: string, cwd?: string, paths?: string[]): Promise; @@ -2963,6 +2968,21 @@ export interface ExtHostTestingShape { $disposeTestFollowups(id: number[]): void; } +export interface ExtHostMcpShape { + $startMcp(id: number, launch: McpServerLaunch.Serialized): void; + $stopMcp(id: number): void; + $sendMessage(id: number, message: string): void; + $waitForInitialCollectionProviders(): Promise; +} + +export interface MainThreadMcpShape { + $onDidChangeState(id: number, state: McpConnectionState): void; + $onDidPublishLog(id: number, log: string): void; + $onDidReceiveMessage(id: number, message: string): void; + $upsertMcpCollection(collection: McpCollectionDefinition.FromExtHost, servers: Dto[]): void; + $deleteMcpCollection(collectionId: string): void; +} + export interface ExtHostLocalizationShape { getMessage(extensionId: string, details: IStringDetails): string; getBundle(extensionId: string): { [key: string]: string } | undefined; @@ -3109,6 +3129,7 @@ export const MainContext = { MainThreadTimeline: createProxyIdentifier('MainThreadTimeline'), MainThreadTesting: createProxyIdentifier('MainThreadTesting'), MainThreadLocalization: createProxyIdentifier('MainThreadLocalizationShape'), + MainThreadMcp: createProxyIdentifier('MainThreadMcpShape'), MainThreadAiRelatedInformation: createProxyIdentifier('MainThreadAiRelatedInformation'), MainThreadAiEmbeddingVector: createProxyIdentifier('MainThreadAiEmbeddingVector') }; @@ -3180,5 +3201,6 @@ export const ExtHostContext = { ExtHostTimeline: createProxyIdentifier('ExtHostTimeline'), ExtHostTesting: createProxyIdentifier('ExtHostTesting'), ExtHostTelemetry: createProxyIdentifier('ExtHostTelemetry'), - ExtHostLocalization: createProxyIdentifier('ExtHostLocalization') + ExtHostLocalization: createProxyIdentifier('ExtHostLocalization'), + ExtHostMcp: createProxyIdentifier('ExtHostMcp'), }; diff --git a/src/vs/workbench/api/common/extHostAuthentication.ts b/src/vs/workbench/api/common/extHostAuthentication.ts index 2400f8ecae65..e705e38df50d 100644 --- a/src/vs/workbench/api/common/extHostAuthentication.ts +++ b/src/vs/workbench/api/common/extHostAuthentication.ts @@ -58,7 +58,9 @@ export class ExtHostAuthentication implements ExtHostAuthenticationShape { async getSession(requestingExtension: IExtensionDescription, providerId: string, scopes: readonly string[], options: vscode.AuthenticationGetSessionOptions = {}): Promise { const extensionId = ExtensionIdentifier.toKey(requestingExtension.identifier); const sortedScopes = [...scopes].sort().join(' '); - return await this._getSessionTaskSingler.getOrCreate(`${extensionId} ${providerId} ${sortedScopes}`, async () => { + const keys: (keyof vscode.AuthenticationGetSessionOptions)[] = Object.keys(options) as (keyof vscode.AuthenticationGetSessionOptions)[]; + const optionsStr = keys.sort().map(key => `${key}:${!!options[key]}`).join(', '); + return await this._getSessionTaskSingler.getOrCreate(`${extensionId} ${providerId} ${sortedScopes} ${optionsStr}`, async () => { await this._proxy.$ensureProvider(providerId); const extensionName = requestingExtension.displayName || requestingExtension.name; return this._proxy.$getSession(providerId, scopes, extensionId, extensionName, options); diff --git a/src/vs/workbench/api/common/extHostChatAgents2.ts b/src/vs/workbench/api/common/extHostChatAgents2.ts index d579589cc8a9..d72fa520c153 100644 --- a/src/vs/workbench/api/common/extHostChatAgents2.ts +++ b/src/vs/workbench/api/common/extHostChatAgents2.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import type * as vscode from 'vscode'; -import { coalesce } from '../../../base/common/arrays.js'; +import { coalesce, isNonEmptyArray } from '../../../base/common/arrays.js'; import { raceCancellation } from '../../../base/common/async.js'; import { CancellationToken, CancellationTokenSource } from '../../../base/common/cancellation.js'; import { toErrorMessage } from '../../../base/common/errorMessage.js'; @@ -13,25 +13,27 @@ import { Iterable } from '../../../base/common/iterator.js'; import { Disposable, DisposableMap, DisposableStore, toDisposable } from '../../../base/common/lifecycle.js'; import { revive } from '../../../base/common/marshalling.js'; import { StopWatch } from '../../../base/common/stopwatch.js'; -import { ThemeIcon } from '../../../base/common/themables.js'; import { assertType } from '../../../base/common/types.js'; import { URI } from '../../../base/common/uri.js'; import { generateUuid } from '../../../base/common/uuid.js'; import { Location } from '../../../editor/common/languages.js'; import { ExtensionIdentifier, IExtensionDescription, IRelaxedExtensionDescription } from '../../../platform/extensions/common/extensions.js'; import { ILogService } from '../../../platform/log/common/log.js'; -import { ChatAgentLocation, IChatAgentRequest, IChatAgentResult, IChatAgentResultTimings, IChatWelcomeMessageContent } from '../../contrib/chat/common/chatAgents.js'; +import { isChatViewTitleActionContext } from '../../contrib/chat/common/chatActions.js'; +import { IChatAgentRequest, IChatAgentResult, IChatAgentResultTimings } from '../../contrib/chat/common/chatAgents.js'; +import { IChatRelatedFile, IChatRequestDraft } from '../../contrib/chat/common/chatEditingService.js'; import { ChatAgentVoteDirection, IChatContentReference, IChatFollowup, IChatResponseErrorDetails, IChatUserActionEvent, IChatVoteAction } from '../../contrib/chat/common/chatService.js'; +import { ChatAgentLocation } from '../../contrib/chat/common/constants.js'; import { checkProposedApiEnabled, isProposedApiEnabled } from '../../services/extensions/common/extensions.js'; import { Dto } from '../../services/extensions/common/proxyIdentifier.js'; import { ExtHostChatAgentsShape2, IChatAgentCompletionItem, IChatAgentHistoryEntryDto, IChatProgressDto, IExtensionChatAgentMetadata, IMainContext, MainContext, MainThreadChatAgentsShape2 } from './extHost.protocol.js'; import { CommandsConverter, ExtHostCommands } from './extHostCommands.js'; +import { ExtHostDiagnostics } from './extHostDiagnostics.js'; import { ExtHostDocuments } from './extHostDocuments.js'; import { ExtHostLanguageModels } from './extHostLanguageModels.js'; +import { ExtHostLanguageModelTools } from './extHostLanguageModelTools.js'; import * as typeConvert from './extHostTypeConverters.js'; import * as extHostTypes from './extHostTypes.js'; -import { isChatViewTitleActionContext } from '../../contrib/chat/common/chatActions.js'; -import { IChatRelatedFile, IChatRequestDraft } from '../../contrib/chat/common/chatEditingService.js'; class ChatAgentResponseStream { @@ -324,7 +326,9 @@ export class ExtHostChatAgents2 extends Disposable implements ExtHostChatAgentsS private readonly _logService: ILogService, private readonly _commands: ExtHostCommands, private readonly _documents: ExtHostDocuments, - private readonly _languageModels: ExtHostLanguageModels + private readonly _languageModels: ExtHostLanguageModels, + private readonly _diagnostics: ExtHostDiagnostics, + private readonly _tools: ExtHostLanguageModelTools ) { super(); this._proxy = mainContext.getProxy(MainContext.MainThreadChatAgents2); @@ -402,7 +406,8 @@ export class ExtHostChatAgents2 extends Disposable implements ExtHostChatAgentsS const { request, location, history } = await this._createRequest(requestDto, context, detector.extension); const model = await this.getModelForRequest(request, detector.extension); - const extRequest = typeConvert.ChatAgentRequest.to(request, location, model, isProposedApiEnabled(detector.extension, 'chatReadonlyPromptReference')); + const includeInteractionId = isProposedApiEnabled(detector.extension, 'chatParticipantPrivate'); + const extRequest = typeConvert.ChatAgentRequest.to(includeInteractionId ? request : { ...request, requestId: '' }, location, model, this.getDiagnosticsWhenEnabled(detector.extension), this.getToolsForRequest(detector.extension, request)); return detector.provider.provideParticipantDetection( extRequest, @@ -486,7 +491,14 @@ export class ExtHostChatAgents2 extends Disposable implements ExtHostChatAgentsS stream = new ChatAgentResponseStream(agent.extension, request, this._proxy, this._commands.converter, sessionDisposables); const model = await this.getModelForRequest(request, agent.extension); - const extRequest = typeConvert.ChatAgentRequest.to(request, location, model, isProposedApiEnabled(agent.extension, 'chatReadonlyPromptReference')); + const includeInteractionId = isProposedApiEnabled(agent.extension, 'chatParticipantPrivate'); + const extRequest = typeConvert.ChatAgentRequest.to( + includeInteractionId ? request : { ...request, requestId: '' }, + location, + model, + this.getDiagnosticsWhenEnabled(agent.extension), + this.getToolsForRequest(agent.extension, request) + ); inFlightRequest = { requestId: requestDto.requestId, extRequest }; this._inFlightRequests.add(inFlightRequest); @@ -538,6 +550,21 @@ export class ExtHostChatAgents2 extends Disposable implements ExtHostChatAgentsS } } + private getDiagnosticsWhenEnabled(extension: Readonly) { + if (!isProposedApiEnabled(extension, 'chatReferenceDiagnostic')) { + return []; + } + return this._diagnostics.getDiagnostics(); + } + + private getToolsForRequest(extension: IExtensionDescription, request: Dto) { + if (!isNonEmptyArray(request.userSelectedTools)) { + return undefined; + } + const selector = new Set(request.userSelectedTools); + return this._tools.getTools(extension).filter(candidate => selector.has(candidate.name)); + } + private async prepareHistoryTurns(extension: Readonly, agentId: string, context: { history: IChatAgentHistoryEntryDto[] }): Promise<(vscode.ChatRequestTurn | vscode.ChatResponseTurn)[]> { const res: (vscode.ChatRequestTurn | vscode.ChatResponseTurn)[] = []; @@ -548,10 +575,9 @@ export class ExtHostChatAgents2 extends Disposable implements ExtHostChatAgentsS { ...ehResult, metadata: undefined }; // REQUEST turn - const hasReadonlyProposal = isProposedApiEnabled(extension, 'chatReadonlyPromptReference'); const varsWithoutTools = h.request.variables.variables .filter(v => !v.isTool) - .map(v => typeConvert.ChatPromptReference.to(v, hasReadonlyProposal)); + .map(v => typeConvert.ChatPromptReference.to(v, this.getDiagnosticsWhenEnabled(extension))); const toolReferences = h.request.variables.variables .filter(v => v.isTool) .map(typeConvert.ChatLanguageModelToolReference.to); @@ -655,15 +681,6 @@ export class ExtHostChatAgents2 extends Disposable implements ExtHostChatAgentsS return items.map((i) => typeConvert.ChatAgentCompletionItem.from(i, this._commands.converter, disposables)); } - async $provideWelcomeMessage(handle: number, token: CancellationToken): Promise { - const agent = this._agents.get(handle); - if (!agent) { - return; - } - - return await agent.provideWelcomeMessage(token); - } - async $provideChatTitle(handle: number, context: IChatAgentHistoryEntryDto[], token: CancellationToken): Promise { const agent = this._agents.get(handle); if (!agent) { @@ -712,6 +729,7 @@ class ExtHostChatAgent { private _supportIssueReporting: boolean | undefined; private _agentVariableProvider?: { provider: vscode.ChatParticipantCompletionItemProvider; triggerCharacters: string[] }; private _welcomeMessageProvider?: vscode.ChatWelcomeMessageProvider | undefined; + private _welcomeMessageContent?: vscode.ChatWelcomeMessageContent | undefined; private _titleProvider?: vscode.ChatTitleProvider | undefined; private _requester: vscode.ChatRequesterInformation | undefined; private _pauseStateEmitter = new Emitter(); @@ -760,24 +778,6 @@ class ExtHostChatAgent { .filter(f => !(f && 'message' in f)); } - async provideWelcomeMessage(token: CancellationToken): Promise { - if (!this._welcomeMessageProvider?.provideWelcomeMessage) { - return undefined; - } - - const content = await this._welcomeMessageProvider.provideWelcomeMessage(token); - const icon = content?.icon; // typescript - if (!content || !ThemeIcon.isThemeIcon(icon)) { - return undefined; - } - - return { - ...content, - icon, - message: typeConvert.MarkdownString.from(content.message), - }; - } - async provideTitle(context: vscode.ChatContext, token: CancellationToken): Promise { if (!this._titleProvider) { return; @@ -826,6 +826,10 @@ class ExtHostChatAgent { helpTextPostfix: (!this._helpTextPostfix || typeof this._helpTextPostfix === 'string') ? this._helpTextPostfix : typeConvert.MarkdownString.from(this._helpTextPostfix), supportIssueReporting: this._supportIssueReporting, requester: this._requester, + welcomeMessageContent: this._welcomeMessageContent && { + ...this._welcomeMessageContent, + message: typeConvert.MarkdownString.from(this._welcomeMessageContent.message), + } }); updateScheduled = false; }); @@ -931,6 +935,15 @@ class ExtHostChatAgent { checkProposedApiEnabled(that.extension, 'defaultChatParticipant'); return that._welcomeMessageProvider; }, + set welcomeMessageContent(v) { + checkProposedApiEnabled(that.extension, 'defaultChatParticipant'); + that._welcomeMessageContent = v; + updateMetadataSoon(); + }, + get welcomeMessageContent() { + checkProposedApiEnabled(that.extension, 'defaultChatParticipant'); + return that._welcomeMessageContent; + }, set titleProvider(v) { checkProposedApiEnabled(that.extension, 'defaultChatParticipant'); that._titleProvider = v; diff --git a/src/vs/workbench/api/common/extHostDocuments.ts b/src/vs/workbench/api/common/extHostDocuments.ts index c0d0ca5ad9c1..4e7dd522c51c 100644 --- a/src/vs/workbench/api/common/extHostDocuments.ts +++ b/src/vs/workbench/api/common/extHostDocuments.ts @@ -76,16 +76,16 @@ export class ExtHostDocuments implements ExtHostDocumentsShape { return data.document; } - public ensureDocumentData(uri: URI): Promise { + public ensureDocumentData(uri: URI, options?: { encoding?: string }): Promise { const cached = this._documentsAndEditors.getDocument(uri); - if (cached) { + if (cached && (!options?.encoding || cached.document.encoding === options.encoding)) { return Promise.resolve(cached); } let promise = this._documentLoader.get(uri.toString()); if (!promise) { - promise = this._proxy.$tryOpenDocument(uri).then(uriData => { + promise = this._proxy.$tryOpenDocument(uri, options).then(uriData => { this._documentLoader.delete(uri.toString()); const canonicalUri = URI.revive(uriData); return assertIsDefined(this._documentsAndEditors.getDocument(canonicalUri)); @@ -94,12 +94,21 @@ export class ExtHostDocuments implements ExtHostDocumentsShape { return Promise.reject(err); }); this._documentLoader.set(uri.toString(), promise); + } else { + if (options?.encoding) { + promise = promise.then(data => { + if (data.document.encoding !== options.encoding) { + return this.ensureDocumentData(uri, options); + } + return data; + }); + } } return promise; } - public createDocumentData(options?: { language?: string; content?: string }): Promise { + public createDocumentData(options?: { language?: string; content?: string; encoding?: string }): Promise { return this._proxy.$tryCreateDocument(options).then(data => URI.revive(data)); } diff --git a/src/vs/workbench/api/common/extHostFileSystemEventService.ts b/src/vs/workbench/api/common/extHostFileSystemEventService.ts index 9bacc4ae0daa..5fcd3c613d08 100644 --- a/src/vs/workbench/api/common/extHostFileSystemEventService.ts +++ b/src/vs/workbench/api/common/extHostFileSystemEventService.ts @@ -243,9 +243,7 @@ interface IExtensionListener { class LazyRevivedFileSystemEvents implements FileSystemEvents { - constructor(private readonly _events: FileSystemEvents) { } - - readonly session = this._events.session; + readonly session: number | undefined; private _created = new Lazy(() => this._events.created.map(URI.revive) as URI[]); get created(): URI[] { return this._created.value; } @@ -255,6 +253,10 @@ class LazyRevivedFileSystemEvents implements FileSystemEvents { private _deleted = new Lazy(() => this._events.deleted.map(URI.revive) as URI[]); get deleted(): URI[] { return this._deleted.value; } + + constructor(private readonly _events: FileSystemEvents) { + this.session = this._events.session; + } } export class ExtHostFileSystemEventService implements ExtHostFileSystemEventServiceShape { diff --git a/src/vs/workbench/api/common/extHostLanguageFeatures.ts b/src/vs/workbench/api/common/extHostLanguageFeatures.ts index a44a8709b8ba..b8122b07faa8 100644 --- a/src/vs/workbench/api/common/extHostLanguageFeatures.ts +++ b/src/vs/workbench/api/common/extHostLanguageFeatures.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import type * as vscode from 'vscode'; import { asArray, coalesce, isFalsyOrEmpty, isNonEmptyArray } from '../../../base/common/arrays.js'; import { raceCancellationError } from '../../../base/common/async.js'; import { VSBuffer } from '../../../base/common/buffer.js'; @@ -16,6 +17,7 @@ import { regExpLeadsToEndlessLoop } from '../../../base/common/strings.js'; import { assertType, isObject } from '../../../base/common/types.js'; import { URI, UriComponents } from '../../../base/common/uri.js'; import { IURITransformer } from '../../../base/common/uriIpc.js'; +import { generateUuid } from '../../../base/common/uuid.js'; import { IPosition } from '../../../editor/common/core/position.js'; import { Range as EditorRange, IRange } from '../../../editor/common/core/range.js'; import { ISelection, Selection } from '../../../editor/common/core/selection.js'; @@ -25,17 +27,16 @@ import { encodeSemanticTokensDto } from '../../../editor/common/services/semanti import { localize } from '../../../nls.js'; import { ExtensionIdentifier, IExtensionDescription } from '../../../platform/extensions/common/extensions.js'; import { ILogService } from '../../../platform/log/common/log.js'; +import { checkProposedApiEnabled, isProposedApiEnabled } from '../../services/extensions/common/extensions.js'; +import { Cache } from './cache.js'; +import * as extHostProtocol from './extHost.protocol.js'; import { IExtHostApiDeprecationService } from './extHostApiDeprecationService.js'; import { CommandsConverter, ExtHostCommands } from './extHostCommands.js'; import { ExtHostDiagnostics } from './extHostDiagnostics.js'; import { ExtHostDocuments } from './extHostDocuments.js'; import { ExtHostTelemetry, IExtHostTelemetry } from './extHostTelemetry.js'; import * as typeConvert from './extHostTypeConverters.js'; -import { CodeAction, CodeActionKind, CompletionList, Disposable, DocumentDropOrPasteEditKind, DocumentSymbol, InlineCompletionTriggerKind, InlineEditTriggerKind, InternalDataTransferItem, Location, NewSymbolNameTriggerKind, Range, SemanticTokens, SemanticTokensEdit, SemanticTokensEdits, SnippetString, SymbolInformation, SyntaxTokenType } from './extHostTypes.js'; -import { checkProposedApiEnabled, isProposedApiEnabled } from '../../services/extensions/common/extensions.js'; -import type * as vscode from 'vscode'; -import { Cache } from './cache.js'; -import * as extHostProtocol from './extHost.protocol.js'; +import { CodeAction, CodeActionKind, CompletionList, DataTransfer, Disposable, DocumentDropOrPasteEditKind, DocumentSymbol, InlineCompletionTriggerKind, InlineEditTriggerKind, InternalDataTransferItem, Location, NewSymbolNameTriggerKind, Range, SemanticTokens, SemanticTokensEdit, SemanticTokensEdits, SnippetString, SymbolInformation, SyntaxTokenType } from './extHostTypes.js'; // --- adapter @@ -586,7 +587,9 @@ class CodeActionAdapter { class DocumentPasteEditProvider { - private readonly _cache = new Cache('DocumentPasteEdit'); + private _cachedPrepare?: Map; + + private readonly _editsCache = new Cache('DocumentPasteEdit.edits'); constructor( private readonly _proxy: extHostProtocol.MainThreadLanguageFeaturesShape, @@ -601,6 +604,8 @@ class DocumentPasteEditProvider { return; } + this._cachedPrepare = undefined; + const doc = this._documents.getDocument(resource); const vscodeRanges = ranges.map(range => typeConvert.Range.to(range)); @@ -613,8 +618,20 @@ class DocumentPasteEditProvider { } // Only send back values that have been added to the data transfer - const entries = Array.from(dataTransfer).filter(([, value]) => !(value instanceof InternalDataTransferItem)); - return typeConvert.DataTransfer.from(entries); + const newEntries = Array.from(dataTransfer).filter(([, value]) => !(value instanceof InternalDataTransferItem)); + + // Store off original data transfer items so we can retrieve them on paste + const newCache = new Map(); + + const items = await Promise.all(Array.from(newEntries, async ([mime, value]) => { + const id = generateUuid(); + newCache.set(id, value); + return [mime, await typeConvert.DataTransferItem.from(mime, value, id)] as const; + })); + + this._cachedPrepare = newCache; + + return { items }; } async providePasteEdits(requestId: number, resource: URI, ranges: IRange[], dataTransferDto: extHostProtocol.DataTransferDTO, context: extHostProtocol.IDocumentPasteContextDto, token: CancellationToken): Promise { @@ -625,10 +642,22 @@ class DocumentPasteEditProvider { const doc = this._documents.getDocument(resource); const vscodeRanges = ranges.map(range => typeConvert.Range.to(range)); - const dataTransfer = typeConvert.DataTransfer.toDataTransfer(dataTransferDto, async (id) => { - return (await this._proxy.$resolvePasteFileData(this._handle, requestId, id)).buffer; + const items = dataTransferDto.items.map(([mime, value]): [string, vscode.DataTransferItem] => { + const cached = this._cachedPrepare?.get(value.id); + if (cached) { + return [mime, cached]; + } + + return [ + mime, + typeConvert.DataTransferItem.to(mime, value, async id => { + return (await this._proxy.$resolvePasteFileData(this._handle, requestId, id)).buffer; + }) + ]; }); + const dataTransfer = new DataTransfer(items); + const edits = await this._provider.provideDocumentPasteEdits(doc, vscodeRanges, dataTransfer, { only: context.only ? new DocumentDropOrPasteEditKind(context.only) : undefined, triggerKind: context.triggerKind, @@ -637,7 +666,7 @@ class DocumentPasteEditProvider { return []; } - const cacheId = this._cache.add(edits); + const cacheId = this._editsCache.add(edits); return edits.map((edit, i): extHostProtocol.IPasteEditDto => ({ _cacheId: [cacheId, i], @@ -651,7 +680,7 @@ class DocumentPasteEditProvider { async resolvePasteEdit(id: extHostProtocol.ChainedCacheId, token: CancellationToken): Promise<{ insertText?: string | vscode.SnippetString; additionalEdit?: extHostProtocol.IWorkspaceEditDto }> { const [sessionId, itemId] = id; - const item = this._cache.get(sessionId, itemId); + const item = this._editsCache.get(sessionId, itemId); if (!item || !this._provider.resolveDocumentPasteEdit) { return {}; // this should not happen... } @@ -664,7 +693,7 @@ class DocumentPasteEditProvider { } releasePasteEdits(id: number): any { - this._cache.delete(id); + this._editsCache.delete(id); } } @@ -1300,23 +1329,7 @@ class CompletionsAdapter { } } -class InlineCompletionAdapterBase { - async provideInlineCompletions(resource: URI, position: IPosition, context: languages.InlineCompletionContext, token: CancellationToken): Promise { - return undefined; - } - - async provideInlineEditsForRange(resource: URI, range: IRange, context: languages.InlineCompletionContext, token: CancellationToken): Promise { - return undefined; - } - - disposeCompletions(pid: number): void { } - - handleDidShowCompletionItem(pid: number, idx: number, updatedInsertText: string): void { } - - handlePartialAccept(pid: number, idx: number, acceptedCharacters: number, info: languages.PartialAcceptInfo): void { } -} - -class InlineCompletionAdapter extends InlineCompletionAdapterBase { +class InlineCompletionAdapter { private readonly _references = new ReferenceMap<{ dispose(): void; items: readonly vscode.InlineCompletionItem[]; @@ -1330,13 +1343,14 @@ class InlineCompletionAdapter extends InlineCompletionAdapterBase { private readonly _provider: vscode.InlineCompletionItemProvider, private readonly _commands: CommandsConverter, ) { - super(); } public get supportsHandleEvents(): boolean { return isProposedApiEnabled(this._extension, 'inlineCompletionsAdditions') && (typeof this._provider.handleDidShowCompletionItem === 'function' - || typeof this._provider.handleDidPartiallyAcceptCompletionItem === 'function'); + || typeof this._provider.handleDidPartiallyAcceptCompletionItem === 'function' + || typeof this._provider.handleDidRejectCompletionItem === 'function' + ); } private readonly languageTriggerKindToVSCodeTriggerKind: Record = { @@ -1344,7 +1358,7 @@ class InlineCompletionAdapter extends InlineCompletionAdapterBase { [languages.InlineCompletionTriggerKind.Explicit]: InlineCompletionTriggerKind.Invoke, }; - override async provideInlineCompletions(resource: URI, position: IPosition, context: languages.InlineCompletionContext, token: CancellationToken): Promise { + async provideInlineCompletions(resource: URI, position: IPosition, context: languages.InlineCompletionContext, token: CancellationToken): Promise { const doc = this._documents.getDocument(resource); const pos = typeConvert.Position.to(position); @@ -1407,11 +1421,13 @@ class InlineCompletionAdapter extends InlineCompletionAdapterBase { insertText: typeof insertText === 'string' ? insertText : { snippet: insertText.value }, filterText: item.filterText, range: item.range ? typeConvert.Range.from(item.range) : undefined, + showRange: (this._isAdditionsProposedApiEnabled && item.showRange) ? typeConvert.Range.from(item.showRange) : undefined, command, action, idx: idx, completeBracketPairs: this._isAdditionsProposedApiEnabled ? item.completeBracketPairs : false, isInlineEdit: this._isAdditionsProposedApiEnabled ? item.isInlineEdit : false, + showInlineEditMenu: this._isAdditionsProposedApiEnabled ? item.showInlineEditMenu : false, warning: (item.warning && this._isAdditionsProposedApiEnabled) ? { message: typeConvert.MarkdownString.from(item.warning.message), icon: item.warning.icon ? typeConvert.IconPath.fromThemeIcon(item.warning.icon) : undefined, @@ -1429,7 +1445,7 @@ class InlineCompletionAdapter extends InlineCompletionAdapterBase { }; } - override async provideInlineEditsForRange(resource: URI, range: IRange, context: languages.InlineCompletionContext, token: CancellationToken): Promise { + async provideInlineEditsForRange(resource: URI, range: IRange, context: languages.InlineCompletionContext, token: CancellationToken): Promise { if (!this._provider.provideInlineEditsForRange) { return undefined; } @@ -1515,12 +1531,12 @@ class InlineCompletionAdapter extends InlineCompletionAdapterBase { }; } - override disposeCompletions(pid: number) { + disposeCompletions(pid: number) { const data = this._references.disposeReferenceId(pid); data?.dispose(); } - override handleDidShowCompletionItem(pid: number, idx: number, updatedInsertText: string): void { + handleDidShowCompletionItem(pid: number, idx: number, updatedInsertText: string): void { const completionItem = this._references.get(pid)?.items[idx]; if (completionItem) { if (this._provider.handleDidShowCompletionItem && this._isAdditionsProposedApiEnabled) { @@ -1529,7 +1545,7 @@ class InlineCompletionAdapter extends InlineCompletionAdapterBase { } } - override handlePartialAccept(pid: number, idx: number, acceptedCharacters: number, info: languages.PartialAcceptInfo): void { + handlePartialAccept(pid: number, idx: number, acceptedCharacters: number, info: languages.PartialAcceptInfo): void { const completionItem = this._references.get(pid)?.items[idx]; if (completionItem) { if (this._provider.handleDidPartiallyAcceptCompletionItem && this._isAdditionsProposedApiEnabled) { @@ -1538,6 +1554,15 @@ class InlineCompletionAdapter extends InlineCompletionAdapterBase { } } } + + handleRejection(pid: number, idx: number): void { + const completionItem = this._references.get(pid)?.items[idx]; + if (completionItem) { + if (this._provider.handleDidRejectCompletionItem && this._isAdditionsProposedApiEnabled) { + this._provider.handleDidRejectCompletionItem(completionItem); + } + } + } } class InlineEditAdapter { @@ -2676,38 +2701,51 @@ export class ExtHostLanguageFeatures implements extHostProtocol.ExtHostLanguageF this._withAdapter(handle, CompletionsAdapter, adapter => adapter.releaseCompletionItems(id), undefined, undefined); } - // --- ghost test + // --- ghost text registerInlineCompletionsProvider(extension: IExtensionDescription, selector: vscode.DocumentSelector, provider: vscode.InlineCompletionItemProvider, metadata: vscode.InlineCompletionItemProviderMetadata | undefined): vscode.Disposable { const adapter = new InlineCompletionAdapter(extension, this._documents, provider, this._commands.converter); const handle = this._addNewAdapter(adapter, extension); - this._proxy.$registerInlineCompletionsSupport(handle, this._transformDocumentSelector(selector, extension), adapter.supportsHandleEvents, - ExtensionIdentifier.toKey(extension.identifier.value), metadata?.yieldTo?.map(extId => ExtensionIdentifier.toKey(extId)) || [], metadata?.debounceDelayMs); + this._proxy.$registerInlineCompletionsSupport( + handle, + this._transformDocumentSelector(selector, extension), + adapter.supportsHandleEvents, + ExtensionIdentifier.toKey(extension.identifier.value), + metadata?.yieldTo?.map(extId => ExtensionIdentifier.toKey(extId)) || [], + metadata?.displayName, + metadata?.debounceDelayMs, + ); return this._createDisposable(handle); } $provideInlineCompletions(handle: number, resource: UriComponents, position: IPosition, context: languages.InlineCompletionContext, token: CancellationToken): Promise { - return this._withAdapter(handle, InlineCompletionAdapterBase, adapter => adapter.provideInlineCompletions(URI.revive(resource), position, context, token), undefined, token); + return this._withAdapter(handle, InlineCompletionAdapter, adapter => adapter.provideInlineCompletions(URI.revive(resource), position, context, token), undefined, token); } $provideInlineEditsForRange(handle: number, resource: UriComponents, range: IRange, context: languages.InlineCompletionContext, token: CancellationToken): Promise { - return this._withAdapter(handle, InlineCompletionAdapterBase, adapter => adapter.provideInlineEditsForRange(URI.revive(resource), range, context, token), undefined, token); + return this._withAdapter(handle, InlineCompletionAdapter, adapter => adapter.provideInlineEditsForRange(URI.revive(resource), range, context, token), undefined, token); } $handleInlineCompletionDidShow(handle: number, pid: number, idx: number, updatedInsertText: string): void { - this._withAdapter(handle, InlineCompletionAdapterBase, async adapter => { + this._withAdapter(handle, InlineCompletionAdapter, async adapter => { adapter.handleDidShowCompletionItem(pid, idx, updatedInsertText); }, undefined, undefined); } $handleInlineCompletionPartialAccept(handle: number, pid: number, idx: number, acceptedCharacters: number, info: languages.PartialAcceptInfo): void { - this._withAdapter(handle, InlineCompletionAdapterBase, async adapter => { + this._withAdapter(handle, InlineCompletionAdapter, async adapter => { adapter.handlePartialAccept(pid, idx, acceptedCharacters, info); }, undefined, undefined); } + $handleInlineCompletionRejection(handle: number, pid: number, idx: number): void { + this._withAdapter(handle, InlineCompletionAdapter, async adapter => { + adapter.handleRejection(pid, idx); + }, undefined, undefined); + } + $freeInlineCompletionsList(handle: number, pid: number): void { - this._withAdapter(handle, InlineCompletionAdapterBase, async adapter => { adapter.disposeCompletions(pid); }, undefined, undefined); + this._withAdapter(handle, InlineCompletionAdapter, async adapter => { adapter.disposeCompletions(pid); }, undefined, undefined); } // --- inline edit diff --git a/src/vs/workbench/api/common/extHostLanguageModelTools.ts b/src/vs/workbench/api/common/extHostLanguageModelTools.ts index d61e28d2a095..7c6ad141c6e9 100644 --- a/src/vs/workbench/api/common/extHostLanguageModelTools.ts +++ b/src/vs/workbench/api/common/extHostLanguageModelTools.ts @@ -16,7 +16,7 @@ import { checkProposedApiEnabled, isProposedApiEnabled } from '../../services/ex import { ExtHostLanguageModelToolsShape, IMainContext, IToolDataDto, MainContext, MainThreadLanguageModelToolsShape } from './extHost.protocol.js'; import * as typeConvert from './extHostTypeConverters.js'; import { IToolInputProcessor } from '../../contrib/chat/common/tools/tools.js'; -import { EditToolData, EditToolId, EditToolInputProcessor } from '../../contrib/chat/common/tools/editFileTool.js'; +import { EditToolData, InternalEditToolId, EditToolInputProcessor, ExtensionEditToolId } from '../../contrib/chat/common/tools/editFileTool.js'; import { Dto } from '../../services/extensions/common/proxyIdentifier.js'; export class ExtHostLanguageModelTools implements ExtHostLanguageModelToolsShape { @@ -62,7 +62,7 @@ export class ExtHostLanguageModelTools implements ExtHostLanguageModelToolsShape throw new Error(`Invalid tool invocation token`); } - if (toolId === EditToolId && !isProposedApiEnabled(extension, 'chatParticipantPrivate')) { + if ((toolId === InternalEditToolId || toolId === ExtensionEditToolId) && !isProposedApiEnabled(extension, 'chatParticipantPrivate')) { throw new Error(`Invalid tool: ${toolId}`); } @@ -75,6 +75,7 @@ export class ExtHostLanguageModelTools implements ExtHostLanguageModelToolsShape tokenBudget: options.tokenizationOptions?.tokenBudget, context: options.toolInvocationToken as IToolInvocationContext | undefined, chatRequestId: isProposedApiEnabled(extension, 'chatParticipantPrivate') ? options.chatRequestId : undefined, + chatInteractionId: isProposedApiEnabled(extension, 'chatParticipantPrivate') ? options.chatInteractionId : undefined, }, token); return typeConvert.LanguageModelToolResult.to(revive(result)); } finally { @@ -93,7 +94,7 @@ export class ExtHostLanguageModelTools implements ExtHostLanguageModelToolsShape return Array.from(this._allTools.values()) .map(tool => typeConvert.LanguageModelToolDescription.to(tool)) .filter(tool => { - if (tool.name === EditToolId) { + if (tool.name === InternalEditToolId || tool.name === ExtensionEditToolId) { return isProposedApiEnabled(extension, 'chatParticipantPrivate'); } @@ -111,7 +112,12 @@ export class ExtHostLanguageModelTools implements ExtHostLanguageModelToolsShape input: dto.parameters, toolInvocationToken: dto.context as vscode.ChatParticipantToolToken | undefined, chatRequestId: dto.chatRequestId, + chatInteractionId: dto.chatInteractionId, }; + if (isProposedApiEnabled(item.extension, 'chatParticipantPrivate') && dto.toolSpecificData?.kind === 'terminal') { + options.terminalCommand = dto.toolSpecificData.command; + } + if (dto.tokenBudget !== undefined) { options.tokenizationOptions = { tokenBudget: dto.tokenBudget, @@ -134,30 +140,46 @@ export class ExtHostLanguageModelTools implements ExtHostLanguageModelToolsShape throw new Error(`Unknown tool ${toolId}`); } - if (!item.tool.prepareInvocation) { - return undefined; - } - const options: vscode.LanguageModelToolInvocationPrepareOptions = { input }; - const result = await item.tool.prepareInvocation(options, token); - if (!result) { - return undefined; - } + if (isProposedApiEnabled(item.extension, 'chatParticipantPrivate') && item.tool.prepareInvocation2) { + const result = await item.tool.prepareInvocation2(options, token); + if (!result) { + return undefined; + } + + return { + confirmationMessages: result.confirmationMessages ? { + title: result.confirmationMessages.title, + message: typeof result.confirmationMessages.message === 'string' ? result.confirmationMessages.message : typeConvert.MarkdownString.from(result.confirmationMessages.message), + } : undefined, + toolSpecificData: { + kind: 'terminal', + language: result.language, + command: result.command, + } + }; + } else if (item.tool.prepareInvocation) { + const result = await item.tool.prepareInvocation(options, token); + if (!result) { + return undefined; + } - if (result.pastTenseMessage || result.tooltip) { - checkProposedApiEnabled(item.extension, 'chatParticipantPrivate'); + if (result.pastTenseMessage || result.presentation) { + checkProposedApiEnabled(item.extension, 'chatParticipantPrivate'); + } + + return { + confirmationMessages: result.confirmationMessages ? { + title: result.confirmationMessages.title, + message: typeof result.confirmationMessages.message === 'string' ? result.confirmationMessages.message : typeConvert.MarkdownString.from(result.confirmationMessages.message), + } : undefined, + invocationMessage: typeConvert.MarkdownString.fromStrict(result.invocationMessage), + pastTenseMessage: typeConvert.MarkdownString.fromStrict(result.pastTenseMessage), + presentation: result.presentation + }; } - return { - confirmationMessages: result.confirmationMessages ? { - title: result.confirmationMessages.title, - message: typeof result.confirmationMessages.message === 'string' ? result.confirmationMessages.message : typeConvert.MarkdownString.from(result.confirmationMessages.message), - } : undefined, - invocationMessage: typeConvert.MarkdownString.fromStrict(result.invocationMessage), - pastTenseMessage: typeConvert.MarkdownString.fromStrict(result.pastTenseMessage), - tooltip: result.tooltip ? typeConvert.MarkdownString.fromStrict(result.tooltip) : undefined, - presentation: result.presentation - }; + return undefined; } registerTool(extension: IExtensionDescription, id: string, tool: vscode.LanguageModelTool): IDisposable { diff --git a/src/vs/workbench/api/common/extHostMcp.ts b/src/vs/workbench/api/common/extHostMcp.ts new file mode 100644 index 000000000000..eead33f21feb --- /dev/null +++ b/src/vs/workbench/api/common/extHostMcp.ts @@ -0,0 +1,240 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import type * as ES from '@c4312/eventsource-umd'; +import * as vscode from 'vscode'; +import { importAMDNodeModule } from '../../../amdX.js'; +import { DeferredPromise, Sequencer } from '../../../base/common/async.js'; +import { CancellationToken } from '../../../base/common/cancellation.js'; +import { Lazy } from '../../../base/common/lazy.js'; +import { Disposable, DisposableMap, DisposableStore, IDisposable, toDisposable } from '../../../base/common/lifecycle.js'; +import { ExtensionIdentifier, IExtensionDescription } from '../../../platform/extensions/common/extensions.js'; +import { createDecorator } from '../../../platform/instantiation/common/instantiation.js'; +import { StorageScope } from '../../../platform/storage/common/storage.js'; +import { extensionPrefixedIdentifier, McpCollectionDefinition, McpConnectionState, McpServerDefinition, McpServerLaunch, McpServerTransportSSE, McpServerTransportType } from '../../contrib/mcp/common/mcpTypes.js'; +import { ExtHostMcpShape, MainContext, MainThreadMcpShape } from './extHost.protocol.js'; +import { IExtHostRpcService } from './extHostRpcService.js'; + +export const IExtHostMpcService = createDecorator('IExtHostMpcService'); + +export interface IExtHostMpcService extends ExtHostMcpShape { + registerMcpConfigurationProvider(extension: IExtensionDescription, id: string, provider: vscode.McpConfigurationProvider): IDisposable; +} + +export class ExtHostMcpService extends Disposable implements IExtHostMpcService { + protected _proxy: MainThreadMcpShape; + private readonly _initialProviderPromises = new Set>(); + private readonly _sseEventSources = this._register(new DisposableMap()); + private readonly _eventSource = new Lazy(async () => { + const es = await importAMDNodeModule('@c4312/eventsource-umd', 'dist/index.umd.js'); + return es.EventSource; + }); + + constructor( + @IExtHostRpcService extHostRpc: IExtHostRpcService, + ) { + super(); + this._proxy = extHostRpc.getProxy(MainContext.MainThreadMcp); + } + + $startMcp(id: number, launch: McpServerLaunch.Serialized): void { + this._startMcp(id, McpServerLaunch.fromSerialized(launch)); + } + + protected _startMcp(id: number, launch: McpServerLaunch): void { + if (launch.type === McpServerTransportType.SSE) { + this._sseEventSources.set(id, new McpSSEHandle(this._eventSource.value, id, launch, this._proxy)); + return; + } + + throw new Error('not implemented'); + } + + $stopMcp(id: number): void { + if (this._sseEventSources.has(id)) { + this._sseEventSources.deleteAndDispose(id); + this._proxy.$onDidChangeState(id, { state: McpConnectionState.Kind.Stopped }); + } + } + + $sendMessage(id: number, message: string): void { + this._sseEventSources.get(id)?.send(message); + } + + async $waitForInitialCollectionProviders(): Promise { + await Promise.all(this._initialProviderPromises); + } + + /** {@link vscode.lm.registerMcpConfigurationProvider} */ + public registerMcpConfigurationProvider(extension: IExtensionDescription, id: string, provider: vscode.McpConfigurationProvider): IDisposable { + const store = new DisposableStore(); + + const metadata = extension.contributes?.modelContextServerCollections?.find(m => m.id === id); + if (!metadata) { + throw new Error(`MCP configuration providers must be registered in the contributes.modelContextServerCollections array within your package.json, but "${id}" was not`); + } + + const mcp: McpCollectionDefinition.FromExtHost = { + id: extensionPrefixedIdentifier(extension.identifier, id), + isTrustedByDefault: true, + label: metadata?.label ?? extension.displayName ?? extension.name, + scope: StorageScope.WORKSPACE + }; + + const update = async () => { + + const list = await provider.provideMcpServerDefinitions(CancellationToken.None); + + function isSSEConfig(candidate: vscode.McpServerDefinition): candidate is vscode.McpSSEServerDefinition { + return !!(candidate as vscode.McpSSEServerDefinition).uri; + } + + const servers: McpServerDefinition[] = []; + + for (const item of list ?? []) { + servers.push({ + id: ExtensionIdentifier.toKey(extension.identifier), + label: item.label, + launch: isSSEConfig(item) + ? { + type: McpServerTransportType.SSE, + uri: item.uri, + headers: item.headers, + } + : { + type: McpServerTransportType.Stdio, + cwd: item.cwd, + args: item.args, + command: item.command, + env: item.env + } + }); + } + + this._proxy.$upsertMcpCollection(mcp, servers); + }; + + store.add(toDisposable(() => { + this._proxy.$deleteMcpCollection(mcp.id); + })); + + if (provider.onDidChange) { + store.add(provider.onDidChange(update)); + } + + const promise = new Promise(resolve => { + setTimeout(() => update().finally(() => { + this._initialProviderPromises.delete(promise); + resolve(); + }), 0); + }); + + this._initialProviderPromises.add(promise); + + return store; + } +} + +class McpSSEHandle extends Disposable { + private readonly _requestSequencer = new Sequencer(); + private readonly _postEndpoint = new DeferredPromise(); + constructor( + eventSourceCtor: Promise, + private readonly _id: number, + launch: McpServerTransportSSE, + private readonly _proxy: MainThreadMcpShape + ) { + super(); + eventSourceCtor.then(EventSourceCtor => this._attach(EventSourceCtor, launch)); + } + + private _attach(EventSourceCtor: typeof ES.EventSource, launch: McpServerTransportSSE) { + if (this._store.isDisposed) { + return; + } + + const eventSource = new EventSourceCtor(launch.uri.toString(), { + // recommended way to do things https://github.com/EventSource/eventsource?tab=readme-ov-file#setting-http-request-headers + fetch: (input, init) => + fetch(input, { + ...init, + headers: { + ...Object.fromEntries(launch.headers), + ...init?.headers, + }, + }).then(async res => { + // we get more details on failure at this point, so handle it explicitly: + if (res.status >= 300) { + this._proxy.$onDidChangeState(this._id, { state: McpConnectionState.Kind.Error, message: `${res.status} status connecting to ${launch.uri}: ${await this._getErrText(res)}` }); + eventSource.close(); + } + return res; + }, err => { + this._proxy.$onDidChangeState(this._id, { state: McpConnectionState.Kind.Error, message: `Error connecting to ${launch.uri}: ${String(err)}` }); + + eventSource.close(); + return Promise.reject(err); + }) + }); + + this._register(toDisposable(() => eventSource.close())); + + // https://github.com/modelcontextprotocol/typescript-sdk/blob/0fa2397174eba309b54575294d56754c52b13a65/src/server/sse.ts#L52 + eventSource.addEventListener('endpoint', e => { + this._postEndpoint.complete(new URL(e.data, launch.uri.toString()).toString()); + }); + + // https://github.com/modelcontextprotocol/typescript-sdk/blob/0fa2397174eba309b54575294d56754c52b13a65/src/server/sse.ts#L133 + eventSource.addEventListener('message', e => { + this._proxy.$onDidReceiveMessage(this._id, e.data); + }); + + eventSource.addEventListener('open', () => { + this._proxy.$onDidChangeState(this._id, { state: McpConnectionState.Kind.Running }); + }); + + eventSource.addEventListener('error', (err) => { + this._postEndpoint.cancel(); + this._proxy.$onDidChangeState(this._id, { + state: McpConnectionState.Kind.Error, + message: `Error connecting to ${launch.uri}: ${err.code || 0} ${err.message || JSON.stringify(err)}`, + }); + eventSource.close(); + }); + } + + async send(message: string) { + // only the sending of the request needs to be sequenced + try { + const res = await this._requestSequencer.queue(async () => { + const endpoint = await this._postEndpoint.p; + const asBytes = new TextEncoder().encode(message); + + return fetch(endpoint, { + method: 'POST', + headers: { + 'Content-Type': 'application/json', + 'Content-Length': String(asBytes.length), + }, + body: asBytes, + }); + }); + + if (res.status >= 300) { + this._proxy.$onDidPublishLog(this._id, `${res.status} status sending message to ${this._postEndpoint}: ${await this._getErrText(res)}`); + } + } catch (err) { + // ignored + } + } + + private async _getErrText(res: Response) { + try { + return await res.text(); + } catch { + return res.statusText; + } + } +} diff --git a/src/vs/workbench/api/common/extHostTask.ts b/src/vs/workbench/api/common/extHostTask.ts index e0774e293a08..91de874b71b1 100644 --- a/src/vs/workbench/api/common/extHostTask.ts +++ b/src/vs/workbench/api/common/extHostTask.ts @@ -29,6 +29,7 @@ import { IExtHostApiDeprecationService } from './extHostApiDeprecationService.js import { USER_TASKS_GROUP_KEY } from '../../contrib/tasks/common/tasks.js'; import { ErrorNoTelemetry, NotSupportedError } from '../../../base/common/errors.js'; import { asArray } from '../../../base/common/arrays.js'; +import { ITaskStatusDTO } from './shared/tasks.js'; export interface IExtHostTask extends ExtHostTaskShape { @@ -39,6 +40,7 @@ export interface IExtHostTask extends ExtHostTaskShape { onDidEndTask: Event; onDidStartTaskProcess: Event; onDidEndTaskProcess: Event; + onDidChangeTaskTerminalStatus: Event; // Fixed the event type back to Event registerTaskProvider(extension: IExtensionDescription, type: string, provider: vscode.TaskProvider): vscode.Disposable; registerTaskSystem(scheme: string, info: tasks.ITaskSystemInfoDTO): void; @@ -406,6 +408,7 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape, IExtHostTask protected readonly _onDidTaskProcessStarted: Emitter = new Emitter(); protected readonly _onDidTaskProcessEnded: Emitter = new Emitter(); + protected readonly _onDidChangeTaskTerminalStatus: Emitter = new Emitter(); constructor( @IExtHostRpcService extHostRpc: IExtHostRpcService, @@ -540,6 +543,25 @@ export abstract class ExtHostTaskBase implements ExtHostTaskShape, IExtHostTask }); } + public get onDidChangeTaskTerminalStatus(): Event { + return this._onDidChangeTaskTerminalStatus.event; + } + + public async $onDidChangeTaskTerminalStatus(value: ITaskStatusDTO): Promise { + let execution; + try { + execution = await this.getTaskExecution(value.execution.id); + } catch (error) { + // The task execution is not available anymore + return; + } + + this._onDidChangeTaskTerminalStatus.fire({ + execution: execution, + eventKind: value.taskEventKind + }); + } + protected abstract provideTasksInternal(validTypes: { [key: string]: boolean }, taskIdPromises: Promise[], handler: HandlerData, value: vscode.Task[] | null | undefined): { tasks: tasks.ITaskDTO[]; extension: IExtensionDescription }; public $provideTasks(handle: number, validTypes: { [key: string]: boolean }): Promise { diff --git a/src/vs/workbench/api/common/extHostTerminalService.ts b/src/vs/workbench/api/common/extHostTerminalService.ts index c88dbbcaa35e..693cb1728a49 100644 --- a/src/vs/workbench/api/common/extHostTerminalService.ts +++ b/src/vs/workbench/api/common/extHostTerminalService.ts @@ -10,7 +10,7 @@ import { createDecorator } from '../../../platform/instantiation/common/instanti import { URI } from '../../../base/common/uri.js'; import { IExtHostRpcService } from './extHostRpcService.js'; import { IDisposable, DisposableStore, Disposable, MutableDisposable } from '../../../base/common/lifecycle.js'; -import { Disposable as VSCodeDisposable, EnvironmentVariableMutatorType, TerminalExitReason, TerminalCompletionItem, TerminalShellType as VSCodeTerminalShellType } from './extHostTypes.js'; +import { Disposable as VSCodeDisposable, EnvironmentVariableMutatorType, TerminalExitReason, TerminalCompletionItem } from './extHostTypes.js'; import { IExtensionDescription } from '../../../platform/extensions/common/extensions.js'; import { localize } from '../../../nls.js'; import { NotSupportedError } from '../../../base/common/errors.js'; @@ -18,7 +18,7 @@ import { serializeEnvironmentDescriptionMap, serializeEnvironmentVariableCollect import { CancellationTokenSource } from '../../../base/common/cancellation.js'; import { generateUuid } from '../../../base/common/uuid.js'; import { IEnvironmentVariableCollectionDescription, IEnvironmentVariableMutator, ISerializableEnvironmentVariableCollection } from '../../../platform/terminal/common/environmentVariable.js'; -import { ICreateContributedTerminalProfileOptions, IProcessReadyEvent, IShellLaunchConfigDto, ITerminalChildProcess, ITerminalLaunchError, ITerminalProfile, TerminalIcon, TerminalLocation, IProcessProperty, ProcessPropertyType, IProcessPropertyMap, TerminalShellType, PosixShellType, WindowsShellType, GeneralShellType } from '../../../platform/terminal/common/terminal.js'; +import { ICreateContributedTerminalProfileOptions, IProcessReadyEvent, IShellLaunchConfigDto, ITerminalChildProcess, ITerminalLaunchError, ITerminalProfile, TerminalIcon, TerminalLocation, IProcessProperty, ProcessPropertyType, IProcessPropertyMap, TerminalShellType } from '../../../platform/terminal/common/terminal.js'; import { TerminalDataBufferer } from '../../../platform/terminal/common/terminalDataBuffering.js'; import { ThemeColor } from '../../../base/common/themables.js'; import { Promises } from '../../../base/common/async.js'; @@ -85,7 +85,7 @@ export class ExtHostTerminal extends Disposable { private _pidPromiseComplete: ((value: number | undefined) => any) | undefined; private _rows: number | undefined; private _exitStatus: vscode.TerminalExitStatus | undefined; - private _state: vscode.TerminalState = { isInteractedWith: false, shellType: undefined }; + private _state: vscode.TerminalState = { isInteractedWith: false, shell: undefined }; private _selection: string | undefined; shellIntegration: vscode.TerminalShellIntegration | undefined; @@ -269,28 +269,10 @@ export class ExtHostTerminal extends Disposable { public setShellType(shellType: TerminalShellType | undefined): boolean { - let extHostType: VSCodeTerminalShellType | undefined; - - switch (shellType) { - case PosixShellType.Sh: extHostType = VSCodeTerminalShellType.Sh; break; - case PosixShellType.Bash: extHostType = VSCodeTerminalShellType.Bash; break; - case PosixShellType.Fish: extHostType = VSCodeTerminalShellType.Fish; break; - case PosixShellType.Csh: extHostType = VSCodeTerminalShellType.Csh; break; - case PosixShellType.Ksh: extHostType = VSCodeTerminalShellType.Ksh; break; - case PosixShellType.Zsh: extHostType = VSCodeTerminalShellType.Zsh; break; - case WindowsShellType.CommandPrompt: extHostType = VSCodeTerminalShellType.CommandPrompt; break; - case WindowsShellType.GitBash: extHostType = VSCodeTerminalShellType.GitBash; break; - case GeneralShellType.PowerShell: extHostType = VSCodeTerminalShellType.PowerShell; break; - case GeneralShellType.Python: extHostType = VSCodeTerminalShellType.Python; break; - case GeneralShellType.Julia: extHostType = VSCodeTerminalShellType.Julia; break; - case GeneralShellType.NuShell: extHostType = VSCodeTerminalShellType.NuShell; break; - default: extHostType = undefined; break; - } - - if (this._state.shellType !== shellType) { + if (this._state.shell !== shellType) { this._state = { ...this._state, - shellType: extHostType + shell: shellType }; return true; } diff --git a/src/vs/workbench/api/common/extHostTerminalShellIntegration.ts b/src/vs/workbench/api/common/extHostTerminalShellIntegration.ts index 076f1b725cc3..5ab8edeb955a 100644 --- a/src/vs/workbench/api/common/extHostTerminalShellIntegration.ts +++ b/src/vs/workbench/api/common/extHostTerminalShellIntegration.ts @@ -131,8 +131,8 @@ export class ExtHostTerminalShellIntegration extends Disposable implements IExtH this._activeShellIntegrations.get(instanceId)?.emitData(data); } - public $shellEnvChange(instanceId: number, shellEnvKeys: string[], shellEnvValues: string[]): void { - this._activeShellIntegrations.get(instanceId)?.setEnv(shellEnvKeys, shellEnvValues); + public $shellEnvChange(instanceId: number, shellEnvKeys: string[], shellEnvValues: string[], isTrusted: boolean): void { + this._activeShellIntegrations.get(instanceId)?.setEnv(shellEnvKeys, shellEnvValues, isTrusted); } public $cwdChange(instanceId: number, cwd: UriComponents | undefined): void { @@ -142,16 +142,24 @@ export class ExtHostTerminalShellIntegration extends Disposable implements IExtH public $closeTerminal(instanceId: number): void { this._activeShellIntegrations.get(instanceId)?.dispose(); this._activeShellIntegrations.delete(instanceId); - } } -class InternalTerminalShellIntegration extends Disposable { +interface IExecutionProperties { + isMultiLine: boolean; + unresolvedCommandLines: string[] | undefined; +} + +export class InternalTerminalShellIntegration extends Disposable { + private _pendingExecutions: InternalTerminalShellExecution[] = []; + private _pendingEndingExecution: InternalTerminalShellExecution | undefined; + + private _currentExecutionProperties: IExecutionProperties | undefined; private _currentExecution: InternalTerminalShellExecution | undefined; get currentExecution(): InternalTerminalShellExecution | undefined { return this._currentExecution; } - private _ignoreNextExecution: boolean = false; - private _env: { [key: string]: string | undefined } | undefined; + + private _env: vscode.TerminalShellIntegrationEnvironment | undefined; private _cwd: URI | undefined; readonly store: DisposableStore = this._register(new DisposableStore()); @@ -164,6 +172,8 @@ class InternalTerminalShellIntegration extends Disposable { readonly onDidRequestShellExecution = this._onDidRequestShellExecution.event; protected readonly _onDidRequestEndExecution = this._register(new Emitter()); readonly onDidRequestEndExecution = this._onDidRequestEndExecution.event; + protected readonly _onDidRequestNewExecution = this._register(new Emitter()); + readonly onDidRequestNewExecution = this._onDidRequestNewExecution.event; constructor( private readonly _terminal: vscode.Terminal, @@ -176,8 +186,14 @@ class InternalTerminalShellIntegration extends Disposable { get cwd(): URI | undefined { return that._cwd; }, - get env(): { [key: string]: string | undefined } | undefined { - return that._env; + get env(): vscode.TerminalShellIntegrationEnvironment | undefined { + if (!that._env) { + return undefined; + } + return Object.freeze({ + isTrusted: that._env.isTrusted, + value: Object.freeze({ ...that._env.value }) + }); }, // executeCommand(commandLine: string): vscode.TerminalShellExecution; // executeCommand(executable: string, args: string[]): vscode.TerminalShellExecution; @@ -202,30 +218,88 @@ class InternalTerminalShellIntegration extends Disposable { confidence: TerminalShellExecutionCommandLineConfidence.High, isTrusted: true }; - const execution = that.startShellExecution(commandLine, that._cwd, true).value; - that._ignoreNextExecution = true; + const execution = that.requestNewShellExecution(commandLine, that._cwd).value; return execution; } }; } - startShellExecution(commandLine: vscode.TerminalShellExecutionCommandLine, cwd: URI | undefined, fireEventInMicrotask?: boolean): InternalTerminalShellExecution { - if (this._ignoreNextExecution && this._currentExecution) { - this._ignoreNextExecution = false; - } else { - if (this._currentExecution) { - this._currentExecution.endExecution(undefined); - this._onDidRequestEndExecution.fire({ terminal: this._terminal, shellIntegration: this.value, execution: this._currentExecution.value, exitCode: undefined }); + requestNewShellExecution(commandLine: vscode.TerminalShellExecutionCommandLine, cwd: URI | undefined) { + const execution = new InternalTerminalShellExecution(commandLine, cwd ?? this._cwd); + const unresolvedCommandLines = splitAndSanitizeCommandLine(commandLine.value); + if (unresolvedCommandLines.length > 1) { + this._currentExecutionProperties = { + isMultiLine: true, + unresolvedCommandLines: splitAndSanitizeCommandLine(commandLine.value), + }; + } + this._pendingExecutions.push(execution); + this._onDidRequestNewExecution.fire(commandLine.value); + return execution; + } + + startShellExecution(commandLine: vscode.TerminalShellExecutionCommandLine, cwd: URI | undefined): undefined { + // Since an execution is starting, fire the end event for any execution that is awaiting to + // end. When this happens it means that the data stream may not be flushed and therefore may + // fire events after the end event. + if (this._pendingEndingExecution) { + this._onDidRequestEndExecution.fire({ terminal: this._terminal, shellIntegration: this.value, execution: this._pendingEndingExecution.value, exitCode: undefined }); + this._pendingEndingExecution = undefined; + } + + if (this._currentExecution) { + // If the current execution is multi-line, check if this command line is part of it. + if (this._currentExecutionProperties?.isMultiLine && this._currentExecutionProperties.unresolvedCommandLines) { + const subExecutionResult = isSubExecution(this._currentExecutionProperties.unresolvedCommandLines, commandLine); + if (subExecutionResult) { + this._currentExecutionProperties.unresolvedCommandLines = subExecutionResult.unresolvedCommandLines; + return; + } } - // Fallback to the shell integration's cwd as the cwd may not have been restored after a reload - const currentExecution = this._currentExecution = new InternalTerminalShellExecution(commandLine, cwd ?? this._cwd); - if (fireEventInMicrotask) { - queueMicrotask(() => this._onDidStartTerminalShellExecution.fire({ terminal: this._terminal, shellIntegration: this.value, execution: currentExecution.value })); - } else { - this._onDidStartTerminalShellExecution.fire({ terminal: this._terminal, shellIntegration: this.value, execution: this._currentExecution.value }); + this._currentExecution.endExecution(undefined); + this._currentExecution.flush(); + this._onDidRequestEndExecution.fire({ terminal: this._terminal, shellIntegration: this.value, execution: this._currentExecution.value, exitCode: undefined }); + } + + // Get the matching pending execution, how strict this is depends on the confidence of the + // command line + let currentExecution: InternalTerminalShellExecution | undefined; + if (commandLine.confidence === TerminalShellExecutionCommandLineConfidence.High) { + for (const [i, execution] of this._pendingExecutions.entries()) { + if (execution.value.commandLine.value === commandLine.value) { + currentExecution = execution; + this._currentExecutionProperties = { + isMultiLine: false, + unresolvedCommandLines: undefined, + }; + currentExecution = execution; + this._pendingExecutions.splice(i, 1); + break; + } else { + const subExecutionResult = isSubExecution(splitAndSanitizeCommandLine(execution.value.commandLine.value), commandLine); + if (subExecutionResult) { + this._currentExecutionProperties = { + isMultiLine: true, + unresolvedCommandLines: subExecutionResult.unresolvedCommandLines, + }; + currentExecution = execution; + this._pendingExecutions.splice(i, 1); + break; + } + } } + } else { + currentExecution = this._pendingExecutions.shift(); + } + + // If there is no execution, create a new one + if (!currentExecution) { + // Fallback to the shell integration's cwd as the cwd may not have been restored after a reload + currentExecution = new InternalTerminalShellExecution(commandLine, cwd ?? this._cwd); } - return this._currentExecution; + + this._currentExecution = currentExecution; + this._onDidStartTerminalShellExecution.fire({ terminal: this._terminal, shellIntegration: this.value, execution: this._currentExecution.value }); } emitData(data: string): void { @@ -233,19 +307,39 @@ class InternalTerminalShellIntegration extends Disposable { } endShellExecution(commandLine: vscode.TerminalShellExecutionCommandLine | undefined, exitCode: number | undefined): void { + // If the current execution is multi-line, don't end it until the next command line is + // confirmed to not be a part of it. + if (this._currentExecutionProperties?.isMultiLine) { + if (this._currentExecutionProperties.unresolvedCommandLines && this._currentExecutionProperties.unresolvedCommandLines.length > 0) { + return; + } + } + if (this._currentExecution) { - this._currentExecution.endExecution(commandLine); - this._onDidRequestEndExecution.fire({ terminal: this._terminal, shellIntegration: this.value, execution: this._currentExecution.value, exitCode }); + const commandLineForEvent = this._currentExecutionProperties?.isMultiLine ? this._currentExecution.value.commandLine : commandLine; + this._currentExecution.endExecution(commandLineForEvent); + const currentExecution = this._currentExecution; + this._pendingEndingExecution = currentExecution; this._currentExecution = undefined; + // IMPORTANT: Ensure the current execution's data events are flushed in order to + // prevent data events firing after the end event fires. + currentExecution.flush().then(() => { + // Only fire if it's still the same execution, if it's changed it would have already + // been fired. + if (this._pendingEndingExecution === currentExecution) { + this._onDidRequestEndExecution.fire({ terminal: this._terminal, shellIntegration: this.value, execution: currentExecution.value, exitCode }); + this._pendingEndingExecution = undefined; + } + }); } } - setEnv(keys: string[], values: string[]): void { + setEnv(keys: string[], values: string[], isTrusted: boolean): void { const env: { [key: string]: string | undefined } = {}; for (let i = 0; i < keys.length; i++) { env[keys[i]] = values[i]; } - this._env = env; + this._env = { value: env, isTrusted }; this._fireChangeEvent(); } @@ -268,12 +362,11 @@ class InternalTerminalShellIntegration extends Disposable { } class InternalTerminalShellExecution { - private _dataStream: ShellExecutionDataStream | undefined; - - private _ended: boolean = false; - readonly value: vscode.TerminalShellExecution; + private _dataStream: ShellExecutionDataStream | undefined; + private _isEnded: boolean = false; + constructor( private _commandLine: vscode.TerminalShellExecutionCommandLine, readonly cwd: URI | undefined, @@ -294,7 +387,7 @@ class InternalTerminalShellExecution { private _createDataStream(): AsyncIterable { if (!this._dataStream) { - if (this._ended) { + if (this._isEnded) { return AsyncIterableObject.EMPTY; } this._dataStream = new ShellExecutionDataStream(); @@ -303,7 +396,9 @@ class InternalTerminalShellExecution { } emitData(data: string): void { - this._dataStream?.emitData(data); + if (!this._isEnded) { + this._dataStream?.emitData(data); + } } endExecution(commandLine: vscode.TerminalShellExecutionCommandLine | undefined): void { @@ -311,13 +406,21 @@ class InternalTerminalShellExecution { this._commandLine = commandLine; } this._dataStream?.endExecution(); - this._dataStream = undefined; - this._ended = true; + this._isEnded = true; + } + + async flush(): Promise { + if (this._dataStream) { + await this._dataStream.flush(); + this._dataStream.dispose(); + this._dataStream = undefined; + } } } class ShellExecutionDataStream extends Disposable { private _barrier: Barrier | undefined; + private _iterables: AsyncIterableObject[] = []; private _emitters: AsyncIterableEmitter[] = []; createIterable(): AsyncIterable { @@ -329,6 +432,7 @@ class ShellExecutionDataStream extends Disposable { this._emitters.push(emitter); await barrier.wait(); }); + this._iterables.push(iterable); return iterable; } @@ -342,4 +446,44 @@ class ShellExecutionDataStream extends Disposable { this._barrier?.open(); this._barrier = undefined; } + + async flush(): Promise { + await Promise.all(this._iterables.map(e => e.toPromise())); + } +} + +function splitAndSanitizeCommandLine(commandLine: string): string[] { + return commandLine + .split('\n') + .map(line => line.trim()) + .filter(line => line.length > 0); +} + +/** + * When executing something that the shell considers multiple commands, such as + * a comment followed by a command, this needs to all be tracked under a single + * execution. + */ +function isSubExecution(unresolvedCommandLines: string[], commandLine: vscode.TerminalShellExecutionCommandLine): { unresolvedCommandLines: string[] } | false { + if (unresolvedCommandLines.length === 0) { + return false; + } + const newUnresolvedCommandLines = [...unresolvedCommandLines]; + const subExecutionLines = splitAndSanitizeCommandLine(commandLine.value); + if (newUnresolvedCommandLines && newUnresolvedCommandLines.length > 0) { + // If all sub-execution lines are in the command line, this is part of the + // multi-line execution. + while (newUnresolvedCommandLines.length > 0) { + if (newUnresolvedCommandLines[0] !== subExecutionLines[0]) { + break; + } + newUnresolvedCommandLines.shift(); + subExecutionLines.shift(); + } + + if (subExecutionLines.length === 0) { + return { unresolvedCommandLines: newUnresolvedCommandLines }; + } + } + return false; } diff --git a/src/vs/workbench/api/common/extHostTypeConverters.ts b/src/vs/workbench/api/common/extHostTypeConverters.ts index 1acf66bce5ea..55f3e4d1d5bb 100644 --- a/src/vs/workbench/api/common/extHostTypeConverters.ts +++ b/src/vs/workbench/api/common/extHostTypeConverters.ts @@ -13,14 +13,17 @@ import { DisposableStore } from '../../../base/common/lifecycle.js'; import { ResourceMap, ResourceSet } from '../../../base/common/map.js'; import * as marked from '../../../base/common/marked/marked.js'; import { parse, revive } from '../../../base/common/marshalling.js'; +import { MarshalledId } from '../../../base/common/marshallingIds.js'; import { Mimes } from '../../../base/common/mime.js'; import { cloneAndChange } from '../../../base/common/objects.js'; +import { isWindows } from '../../../base/common/platform.js'; import { IPrefixTreeNode, WellDefinedPrefixTree } from '../../../base/common/prefixTree.js'; import { basename } from '../../../base/common/resources.js'; import { ThemeIcon } from '../../../base/common/themables.js'; import { isDefined, isEmptyObject, isNumber, isString, isUndefinedOrNull } from '../../../base/common/types.js'; import { URI, UriComponents, isUriComponents } from '../../../base/common/uri.js'; import { IURITransformer } from '../../../base/common/uriIpc.js'; +import { generateUuid } from '../../../base/common/uuid.js'; import { RenderLineNumbersType } from '../../../editor/common/config/editorOptions.js'; import { IPosition } from '../../../editor/common/core/position.js'; import * as editorRange from '../../../editor/common/core/range.js'; @@ -36,31 +39,30 @@ import { IMarkerData, IRelatedInformation, MarkerSeverity, MarkerTag } from '../ import { ProgressLocation as MainProgressLocation } from '../../../platform/progress/common/progress.js'; import { DEFAULT_EDITOR_ASSOCIATION, SaveReason } from '../../common/editor.js'; import { IViewBadge } from '../../common/views.js'; -import { ChatAgentLocation, IChatAgentRequest, IChatAgentResult } from '../../contrib/chat/common/chatAgents.js'; +import { IChatAgentRequest, IChatAgentResult } from '../../contrib/chat/common/chatAgents.js'; +import { IChatRequestDraft } from '../../contrib/chat/common/chatEditingService.js'; import { IChatRequestVariableEntry } from '../../contrib/chat/common/chatModel.js'; import { IChatAgentMarkdownContentWithVulnerability, IChatCodeCitation, IChatCommandButton, IChatConfirmation, IChatContentInlineReference, IChatContentReference, IChatFollowup, IChatMarkdownContent, IChatMoveMessage, IChatProgressMessage, IChatResponseCodeblockUriPart, IChatTaskDto, IChatTaskResult, IChatTextEdit, IChatTreeData, IChatUserActionEvent, IChatWarningMessage } from '../../contrib/chat/common/chatService.js'; import { IToolData, IToolResult } from '../../contrib/chat/common/languageModelToolsService.js'; import * as chatProvider from '../../contrib/chat/common/languageModels.js'; +import { IChatResponsePromptTsxPart, IChatResponseTextPart } from '../../contrib/chat/common/languageModels.js'; import { DebugTreeItemCollapsibleState, IDebugVisualizationTreeItem } from '../../contrib/debug/common/debug.js'; import * as notebooks from '../../contrib/notebook/common/notebookCommon.js'; +import { CellEditType } from '../../contrib/notebook/common/notebookCommon.js'; import { ICellRange } from '../../contrib/notebook/common/notebookRange.js'; import * as search from '../../contrib/search/common/search.js'; import { TestId } from '../../contrib/testing/common/testId.js'; import { CoverageDetails, DetailType, ICoverageCount, IFileCoverage, ISerializedTestResults, ITestErrorMessage, ITestItem, ITestRunProfileReference, ITestTag, TestMessageType, TestResultItem, TestRunProfileBitset, denamespaceTestTag, namespaceTestTag } from '../../contrib/testing/common/testTypes.js'; import { EditorGroupColumn } from '../../services/editor/common/editorGroupColumn.js'; import { ACTIVE_GROUP, SIDE_GROUP } from '../../services/editor/common/editorService.js'; +import { checkProposedApiEnabled } from '../../services/extensions/common/extensions.js'; import { Dto } from '../../services/extensions/common/proxyIdentifier.js'; import * as extHostProtocol from './extHost.protocol.js'; import { CommandsConverter } from './extHostCommands.js'; import { getPrivateApiFor } from './extHostTestingPrivateApi.js'; import * as types from './extHostTypes.js'; -import { IChatResponseTextPart, IChatResponsePromptTsxPart } from '../../contrib/chat/common/languageModels.js'; -import { LanguageModelTextPart, LanguageModelPromptTsxPart } from './extHostTypes.js'; -import { MarshalledId } from '../../../base/common/marshallingIds.js'; -import { IChatRequestDraft } from '../../contrib/chat/common/chatEditingService.js'; -import { isWindows } from '../../../base/common/platform.js'; -import { checkProposedApiEnabled } from '../../services/extensions/common/extensions.js'; -import { CellEditType } from '../../contrib/notebook/common/notebookCommon.js'; +import { LanguageModelPromptTsxPart, LanguageModelTextPart } from './extHostTypes.js'; +import { ChatAgentLocation } from '../../contrib/chat/common/constants.js'; export namespace Command { @@ -2198,11 +2200,12 @@ export namespace DataTransferItem { return new types.InternalDataTransferItem(item.asString); } - export async function from(mime: string, item: vscode.DataTransferItem | IDataTransferItem): Promise { + export async function from(mime: string, item: vscode.DataTransferItem | IDataTransferItem, id: string = generateUuid()): Promise { const stringValue = await item.asString(); if (mime === Mimes.uriList) { return { + id, asString: stringValue, fileData: undefined, uriListData: serializeUriList(stringValue), @@ -2211,6 +2214,7 @@ export namespace DataTransferItem { const fileValue = item.asFile(); return { + id, asString: stringValue, fileData: fileValue ? { name: fileValue.name, @@ -2251,19 +2255,20 @@ export namespace DataTransfer { return new types.DataTransfer(init); } - export async function from(dataTransfer: Iterable): Promise { - const newDTO: extHostProtocol.DataTransferDTO = { items: [] }; + export async function from(dataTransfer: vscode.DataTransfer): Promise { + const items = await Promise.all(Array.from(dataTransfer, async ([mime, value]) => { + return [mime, await DataTransferItem.from(mime, value)] as const; + })); - const promises: Promise[] = []; - for (const [mime, value] of dataTransfer) { - promises.push((async () => { - newDTO.items.push([mime, await DataTransferItem.from(mime, value)]); - })()); - } + return { items }; + } - await Promise.all(promises); + export async function fromList(dataTransfer: Iterable): Promise { + const items = await Promise.all(Array.from(dataTransfer, async ([mime, value]) => { + return [mime, await DataTransferItem.from(mime, value, value.id)] as const; + })); - return newDTO; + return { items }; } } @@ -2761,24 +2766,33 @@ export namespace ChatResponsePart { } export namespace ChatAgentRequest { - export function to(request: IChatAgentRequest, location2: vscode.ChatRequestEditorData | vscode.ChatRequestNotebookData | undefined, model: vscode.LanguageModelChat, hasReadonlyProposal: boolean): vscode.ChatRequest { + export function to(request: IChatAgentRequest, location2: vscode.ChatRequestEditorData | vscode.ChatRequestNotebookData | undefined, model: vscode.LanguageModelChat, diagnostics: readonly [vscode.Uri, readonly vscode.Diagnostic[]][], tools: vscode.LanguageModelToolInformation[] | undefined): vscode.ChatRequest { const toolReferences = request.variables.variables.filter(v => v.isTool); const variableReferences = request.variables.variables.filter(v => !v.isTool); - return { + const requestWithoutId = { prompt: request.message, command: request.command, attempt: request.attempt ?? 0, enableCommandDetection: request.enableCommandDetection ?? true, isParticipantDetected: request.isParticipantDetected ?? false, - references: variableReferences.map(v => ChatPromptReference.to(v, hasReadonlyProposal)), + references: variableReferences.map(v => ChatPromptReference.to(v, diagnostics)), toolReferences: toolReferences.map(ChatLanguageModelToolReference.to), location: ChatLocation.to(request.location), acceptedConfirmationData: request.acceptedConfirmationData, rejectedConfirmationData: request.rejectedConfirmationData, location2, toolInvocationToken: Object.freeze({ sessionId: request.sessionId }) as never, + tools, model }; + if (request.requestId) { + return { + ...requestWithoutId, + id: request.requestId + }; + } + // This cast is done to allow sending the stabl version of ChatRequest which does not have an id property + return requestWithoutId as unknown as vscode.ChatRequest; } } @@ -2814,21 +2828,49 @@ export namespace ChatLocation { } export namespace ChatPromptReference { - export function to(variable: IChatRequestVariableEntry, hasReadonlyProposal: boolean): vscode.ChatPromptReference { - const value = variable.value; + export function to(variable: IChatRequestVariableEntry, diagnostics: readonly [vscode.Uri, readonly vscode.Diagnostic[]][]): vscode.ChatPromptReference { + let value: vscode.ChatPromptReference['value'] = variable.value; if (!value) { throw new Error('Invalid value reference'); } + if (isUriComponents(value)) { + value = URI.revive(value); + } else if (value && typeof value === 'object' && 'uri' in value && 'range' in value && isUriComponents(value.uri)) { + value = Location.to(revive(value)); + } else if (variable.isImage) { + value = new types.ChatReferenceBinaryData( + variable.mimeType ?? 'image/png', + () => Promise.resolve(new Uint8Array(Object.values(variable.value as number[]))), + variable.references && URI.isUri(variable.references[0].reference) ? variable.references[0].reference : undefined + ); + } else if (variable.kind === 'diagnostic') { + const filterSeverity = variable.filterSeverity && DiagnosticSeverity.to(variable.filterSeverity); + const filterUri = variable.filterUri && URI.revive(variable.filterUri).toString(); + value = new types.ChatReferenceDiagnostic(diagnostics.map(([uri, d]): [vscode.Uri, vscode.Diagnostic[]] => { + if (variable.filterUri && uri.toString() !== filterUri) { + return [uri, []]; + } + + return [uri, d.filter(d => { + if (filterSeverity && d.severity > filterSeverity) { + return false; + } + if (variable.filterRange && !editorRange.Range.areIntersectingOrTouching(variable.filterRange, Range.from(d.range))) { + return false; + } + + return true; + })]; + }).filter(([, d]) => d.length > 0)); + } + return { id: variable.id, name: variable.name, range: variable.range && [variable.range.start, variable.range.endExclusive], - value: isUriComponents(value) ? URI.revive(value) : - value && typeof value === 'object' && 'uri' in value && 'range' in value && isUriComponents(value.uri) ? - Location.to(revive(value)) : variable.isImage ? new types.ChatReferenceBinaryData(variable.mimeType ?? 'image/png', () => Promise.resolve(new Uint8Array(Object.values(value))), variable.references && URI.isUri(variable.references[0].reference) ? variable.references[0].reference : undefined) : value, + value, modelDescription: variable.modelDescription, - isReadonly: hasReadonlyProposal ? variable.isMarkedReadonly : undefined, }; } } diff --git a/src/vs/workbench/api/common/extHostTypes.ts b/src/vs/workbench/api/common/extHostTypes.ts index f27aac8e0072..9d28387e9cc9 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -2141,10 +2141,12 @@ export class TerminalProfile implements vscode.TerminalProfile { export enum TerminalCompletionItemKind { File = 0, Folder = 1, - Flag = 2, - Method = 3, + Method = 2, + Alias = 3, Argument = 4, - Alias = 5 + Option = 5, + OptionValue = 6, + Flag = 7, } export class TerminalCompletionItem implements vscode.TerminalCompletionItem { @@ -2202,6 +2204,7 @@ export class TerminalCompletionList(); + #items = new Map(); - constructor(init?: Iterable) { + constructor(init?: Iterable) { for (const [mime, item] of init ?? []) { const existing = this.#items.get(this.#normalizeMime(mime)); if (existing) { @@ -2912,17 +2957,17 @@ export class DataTransfer implements vscode.DataTransfer { } } - get(mimeType: string): DataTransferItem | undefined { + get(mimeType: string): vscode.DataTransferItem | undefined { return this.#items.get(this.#normalizeMime(mimeType))?.[0]; } - set(mimeType: string, value: DataTransferItem): void { + set(mimeType: string, value: vscode.DataTransferItem): void { // This intentionally overwrites all entries for a given mimetype. // This is similar to how the DOM DataTransfer type works this.#items.set(this.#normalizeMime(mimeType), [value]); } - forEach(callbackfn: (value: DataTransferItem, key: string, dataTransfer: DataTransfer) => void, thisArg?: unknown): void { + forEach(callbackfn: (value: vscode.DataTransferItem, key: string, dataTransfer: DataTransfer) => void, thisArg?: unknown): void { for (const [mime, items] of this.#items) { for (const item of items) { callbackfn.call(thisArg, item, mime, this); @@ -4716,6 +4761,10 @@ export class ChatReferenceBinaryData implements vscode.ChatReferenceBinaryData { } } +export class ChatReferenceDiagnostic implements vscode.ChatReferenceDiagnostic { + constructor(public readonly diagnostics: [vscode.Uri, vscode.Diagnostic[]][]) { } +} + export enum LanguageModelChatMessageRole { User = 1, Assistant = 2, @@ -4735,6 +4784,20 @@ export class LanguageModelToolResultPart implements vscode.LanguageModelToolResu } } +export class PreparedTerminalToolInvocation { + constructor( + public readonly command: string, + public readonly language: string, + public readonly confirmationMessages?: vscode.LanguageModelToolConfirmationMessages, + ) { } +} + +export enum ChatErrorLevel { + Info = 0, + Warning = 1, + Error = 2 +} + export class LanguageModelChatMessage implements vscode.LanguageModelChatMessage { static User(content: string | (LanguageModelTextPart | LanguageModelToolResultPart | LanguageModelToolCallPart)[], name?: string): LanguageModelChatMessage { @@ -4976,3 +5039,24 @@ export enum InlineEditTriggerKind { } //#endregion + +//#region MC +export class McpStdioServerDefinition implements vscode.McpStdioServerDefinition { + cwd?: URI; + + constructor( + public label: string, + public command: string, + public args: string[], + public env: Record + ) { } +} + +export class McpSSEServerDefinition implements vscode.McpSSEServerDefinition { + headers: [string, string][] = []; + constructor( + public label: string, + public uri: URI + ) { } +} +//#endregion diff --git a/src/vs/workbench/api/common/extHostWorkspace.ts b/src/vs/workbench/api/common/extHostWorkspace.ts index 4b6e88df4901..e81e17d4f877 100644 --- a/src/vs/workbench/api/common/extHostWorkspace.ts +++ b/src/vs/workbench/api/common/extHostWorkspace.ts @@ -505,68 +505,38 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac throw new Error(`Invalid file pattern provided ${JSON.stringify(filePatternsToUse)}`); } - const queryOptions: QueryOptions[] = filePatternsToUse.map(filePattern => { - - const excludePatterns = globsToISearchPatternBuilder(options.exclude); - - const fileQueries: IFileQueryBuilderOptions = { - ignoreSymlinks: typeof options.followSymlinks === 'boolean' ? !options.followSymlinks : undefined, - disregardIgnoreFiles: typeof options.useIgnoreFiles?.local === 'boolean' ? !options.useIgnoreFiles.local : undefined, - disregardGlobalIgnoreFiles: typeof options.useIgnoreFiles?.global === 'boolean' ? !options.useIgnoreFiles.global : undefined, - disregardParentIgnoreFiles: typeof options.useIgnoreFiles?.parent === 'boolean' ? !options.useIgnoreFiles.parent : undefined, - disregardExcludeSettings: options.useExcludeSettings !== undefined && options.useExcludeSettings === ExcludeSettingOptions.None, - disregardSearchExcludeSettings: options.useExcludeSettings !== undefined && (options.useExcludeSettings !== ExcludeSettingOptions.SearchAndFilesExclude), - maxResults: options.maxResults, - excludePattern: excludePatterns.length > 0 ? excludePatterns : undefined, - _reason: 'startFileSearch', - shouldGlobSearch: query.type === 'include' ? undefined : true, - }; - - const parseInclude = parseSearchExcludeInclude(GlobPattern.from(filePattern)); - const folderToUse = parseInclude?.folder; - if (query.type === 'include') { - fileQueries.includePattern = parseInclude?.pattern; - } else { - fileQueries.filePattern = parseInclude?.pattern; - } - - return { - folder: folderToUse, - options: fileQueries - }; - }); - - return this._findFilesBase(queryOptions, token); - } - - private async _findFilesBase( - queryOptions: QueryOptions[] | undefined, - token: CancellationToken - ): Promise { - const result = await Promise.all(queryOptions?.map(option => this._proxy.$startFileSearch( - option.folder ?? null, - option.options, - token).then(data => Array.isArray(data) ? data.map(d => URI.revive(d)) : []) - ) ?? []); - - return result.flat(); - } - - findTextInFiles2(query: vscode.TextSearchQuery2, options: vscode.FindTextInFilesOptions2 | undefined, extensionId: ExtensionIdentifier, token: vscode.CancellationToken = CancellationToken.None): vscode.FindTextInFilesResponse { - this._logService.trace(`extHostWorkspace#findTextInFiles2: textSearch, extension: ${extensionId.value}, entryPoint: findTextInFiles2`); - - - const getOptions = (include: vscode.GlobPattern | undefined): QueryOptions => { - if (!options) { - return { - folder: undefined, - options: {} - }; - } - const parsedInclude = include ? parseSearchExcludeInclude(GlobPattern.from(include)) : undefined; - - const excludePatterns = options.exclude ? globsToISearchPatternBuilder(options.exclude) : undefined; - + console.log(JSON.stringify(fileQueries)); + return this._proxy.$startFileSearch( + folderToUse ?? null, + fileQueries, + token + ) + .then(data => Array.isArray(data) ? data.map(d => URI.revive(d)) : []); + } + findTextInFilesNew(query: vscode.TextSearchQueryNew, extensionId: ExtensionIdentifier, options?: vscode.FindTextInFilesOptionsNew, token?: vscode.CancellationToken): vscode.FindTextInFilesResponse { + this._logService.trace(`extHostWorkspace#findTextInFilesNew: textSearch, extension: ${extensionId.value}, entryPoint: findTextInFilesNew`); + const queryOptionsRaw: (QueryOptions | undefined)[] = ((options?.include?.map((include) => { + const parsedInclude = parseSearchExcludeInclude(GlobPattern.from(include)); + + const excludePatterns = ( + options.exclude?.map((exclude): ISearchPatternBuilder | undefined => { + if (typeof exclude === 'string') { + return { + pattern: exclude, + uri: undefined + } satisfies ISearchPatternBuilder; + } else { + const parsedExclude = parseSearchExcludeInclude(exclude); + if (!parsedExclude) { + return undefined; + } + return { + pattern: parsedExclude.pattern, + uri: parsedExclude.folder + } satisfies ISearchPatternBuilder; + } + }) ?? [] + ).filter((e): e is ISearchPatternBuilder => !!e); return { options: { @@ -667,12 +637,13 @@ export class ExtHostWorkspace implements ExtHostWorkspaceShape, IExtHostWorkspac } try { - const result = await Promise.all(queryOptions?.map(option => this._proxy.$startTextSearch( - query, - option.folder ?? null, - option.options, - requestId, - token) || {} + const result = await Promise.all(queryOptions?.map(option => + this._proxy.$startTextSearch( + query, + option.folder ?? null, + option.options, + requestId, + token) || {} ) ?? []); delete this._activeSearchCallbacks[requestId]; return result.reduce((acc, val) => { diff --git a/src/vs/workbench/api/common/shared/tasks.ts b/src/vs/workbench/api/common/shared/tasks.ts index 67a3fe9e05db..da343180cb99 100644 --- a/src/vs/workbench/api/common/shared/tasks.ts +++ b/src/vs/workbench/api/common/shared/tasks.ts @@ -6,6 +6,7 @@ import { UriComponents } from '../../../../base/common/uri.js'; import { IExtensionDescription } from '../../../../platform/extensions/common/extensions.js'; import type { Dto } from '../../../services/extensions/common/proxyIdentifier.js'; +import { ITaskExecution } from '../../../contrib/tasks/common/tasks.js'; export interface ITaskDefinitionDTO { type: string; @@ -50,6 +51,48 @@ export interface IShellQuotingOptionsDTO { weak?: string; } +export enum TaskEventKind { + /** Indicates that a task's properties or configuration have changed */ + Changed = 'changed', + + /** Indicates that a task has begun executing */ + ProcessStarted = 'processStarted', + + /** Indicates that a task process has completed */ + ProcessEnded = 'processEnded', + + /** Indicates that a task was terminated, either by user action or by the system */ + Terminated = 'terminated', + + /** Indicates that a task has started running */ + Start = 'start', + + /** Indicates that a task has acquired all needed input/variables to execute */ + AcquiredInput = 'acquiredInput', + + /** Indicates that a dependent task has started */ + DependsOnStarted = 'dependsOnStarted', + + /** Indicates that a task is actively running/processing */ + Active = 'active', + + /** Indicates that a task is paused/waiting but not complete */ + Inactive = 'inactive', + + /** Indicates that a task has completed fully */ + End = 'end', + + /** Indicates that a task's problem matcher has started */ + ProblemMatcherStarted = 'problemMatcherStarted', + + /** Indicates that a task's problem matcher has ended */ + ProblemMatcherEnded = 'problemMatcherEnded', + + /** Indicates that a task's problem matcher has found errors */ + ProblemMatcherFoundErrors = 'problemMatcherFoundErrors' +} + + export interface IShellExecutionOptionsDTO extends IExecutionOptionsDTO { executable?: string; shellArgs?: string[]; @@ -137,3 +180,13 @@ export interface ITaskSystemInfoDTO { authority: string; platform: string; } + +export interface ITaskStatus { + execution: ITaskExecution; + taskEventKind: TaskEventKind; +} + +export interface ITaskStatusDTO { + execution: ITaskExecutionDTO; + taskEventKind: TaskEventKind; +} diff --git a/src/vs/workbench/api/node/extHost.node.services.ts b/src/vs/workbench/api/node/extHost.node.services.ts index ade6d90abb40..331d9a7b180e 100644 --- a/src/vs/workbench/api/node/extHost.node.services.ts +++ b/src/vs/workbench/api/node/extHost.node.services.ts @@ -27,6 +27,8 @@ import { SyncDescriptor } from '../../../platform/instantiation/common/descripto import { ISignService } from '../../../platform/sign/common/sign.js'; import { SignService } from '../../../platform/sign/node/signService.js'; import { ExtHostTelemetry, IExtHostTelemetry } from '../common/extHostTelemetry.js'; +import { IExtHostMpcService } from '../common/extHostMcp.js'; +import { NodeExtHostMpcService } from './extHostMpcNode.js'; // ######################################################################### // ### ### @@ -47,3 +49,4 @@ registerSingleton(IExtHostTask, ExtHostTask, InstantiationType.Eager); registerSingleton(IExtHostTerminalService, ExtHostTerminalService, InstantiationType.Eager); registerSingleton(IExtHostTunnelService, NodeExtHostTunnelService, InstantiationType.Eager); registerSingleton(IExtHostVariableResolverProvider, NodeExtHostVariableResolverProviderService, InstantiationType.Eager); +registerSingleton(IExtHostMpcService, NodeExtHostMpcService, InstantiationType.Eager); diff --git a/src/vs/workbench/api/node/extHostCLIServer.ts b/src/vs/workbench/api/node/extHostCLIServer.ts index 01bc190d579f..0b040acb868a 100644 --- a/src/vs/workbench/api/node/extHostCLIServer.ts +++ b/src/vs/workbench/api/node/extHostCLIServer.ts @@ -112,8 +112,7 @@ export class CLIServerBase { } sendResponse(200, returnObj); } catch (e) { - const message = e instanceof Error ? e.message : JSON.stringify(e); - sendResponse(500, message); + sendResponse(500, "An internal server error occurred."); this.logService.error('Error while processing pipe request', e); } }); diff --git a/src/vs/workbench/api/node/extHostDiskFileSystemProvider.ts b/src/vs/workbench/api/node/extHostDiskFileSystemProvider.ts index 2be6ba4652fa..cb6a09dc96d5 100644 --- a/src/vs/workbench/api/node/extHostDiskFileSystemProvider.ts +++ b/src/vs/workbench/api/node/extHostDiskFileSystemProvider.ts @@ -27,9 +27,11 @@ export class ExtHostDiskFileSystemProvider { class DiskFileSystemProviderAdapter implements vscode.FileSystemProvider { - private readonly impl = new DiskFileSystemProvider(this.logService); + private readonly impl: DiskFileSystemProvider; - constructor(private readonly logService: ILogService) { } + constructor(logService: ILogService) { + this.impl = new DiskFileSystemProvider(logService); + } async stat(uri: vscode.Uri): Promise { const stat = await this.impl.stat(uri); diff --git a/src/vs/workbench/api/node/extHostMpcNode.ts b/src/vs/workbench/api/node/extHostMpcNode.ts new file mode 100644 index 000000000000..15fb24c0a842 --- /dev/null +++ b/src/vs/workbench/api/node/extHostMpcNode.ts @@ -0,0 +1,111 @@ +/*--------------------------------------------------------------------------------------------- + * 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 { mapValues } from '../../../base/common/objects.js'; +import { URI } from '../../../base/common/uri.js'; +import { StreamSplitter } from '../../../base/node/nodeStreams.js'; +import { McpConnectionState, McpServerLaunch, McpServerTransportStdio, McpServerTransportType } from '../../contrib/mcp/common/mcpTypes.js'; +import { ExtHostMcpService } from '../common/extHostMcp.js'; +import { IExtHostRpcService } from '../common/extHostRpcService.js'; +import { homedir } from 'os'; +import { PassThrough } from 'stream'; + +export class NodeExtHostMpcService extends ExtHostMcpService { + constructor( + @IExtHostRpcService extHostRpc: IExtHostRpcService, + ) { + super(extHostRpc); + } + + private nodeServers = new Map(); + + protected override _startMcp(id: number, launch: McpServerLaunch): void { + if (launch.type === McpServerTransportType.Stdio) { + this.startNodeMpc(id, launch); + } else { + super._startMcp(id, launch); + } + } + + override $stopMcp(id: number): void { + const nodeServer = this.nodeServers.get(id); + if (nodeServer) { + nodeServer.abortCtrl.abort(); + this.nodeServers.delete(id); + } else { + super.$stopMcp(id); + } + } + + override $sendMessage(id: number, message: string): void { + const nodeServer = this.nodeServers.get(id); + if (nodeServer) { + this._proxy.$onDidPublishLog(id, '[Client Says] ' + message.toString()); + + nodeServer.child.stdin.write(message + '\n'); + } else { + super.$sendMessage(id, message); + } + } + + private startNodeMpc(id: number, launch: McpServerTransportStdio): void { + const onError = (err: Error) => this._proxy.$onDidChangeState(id, { + state: McpConnectionState.Kind.Error, + message: err.message, + }); + + const abortCtrl = new AbortController(); + let child: ChildProcessWithoutNullStreams; + try { + child = spawn(launch.command, launch.args, { + stdio: 'pipe', + cwd: launch.cwd ? URI.revive(launch.cwd).fsPath : homedir(), + signal: abortCtrl.signal, + env: { + ...process.env, + ...mapValues(launch.env, v => typeof v === 'number' ? String(v) : (v === null ? undefined : v)), + }, + }); + } catch (e) { + onError(e); + abortCtrl.abort(); + return; + } + + this._proxy.$onDidChangeState(id, { state: McpConnectionState.Kind.Starting }); + + const debug = new PassThrough(); + debug.on('data', line => { + this._proxy.$onDidPublishLog(id, '[Server Says] ' + line.toString()); + }); + + child.stdout.pipe(new StreamSplitter('\n')).pipe(debug).on('data', line => this._proxy.$onDidReceiveMessage(id, line.toString())); + + child.stdin.on('error', onError); + child.stdout.on('error', onError); + + // Stderr handling is not currently specified https://github.com/modelcontextprotocol/specification/issues/177 + // Just treat it as generic log data for now + child.stderr.pipe(new StreamSplitter('\n')).on('data', line => this._proxy.$onDidPublishLog(id, line.toString())); + + child.on('spawn', () => this._proxy.$onDidChangeState(id, { state: McpConnectionState.Kind.Running })); + + child.on('error', onError); + child.on('exit', code => + code === 0 + ? this._proxy.$onDidChangeState(id, { state: McpConnectionState.Kind.Stopped }) + : this._proxy.$onDidChangeState(id, { + state: McpConnectionState.Kind.Error, + message: `Process exited with code ${code}`, + }) + ); + + this.nodeServers.set(id, { abortCtrl, child }); + } +} diff --git a/src/vs/workbench/api/node/proxyResolver.ts b/src/vs/workbench/api/node/proxyResolver.ts index 533144660e47..48c6bf2801cb 100644 --- a/src/vs/workbench/api/node/proxyResolver.ts +++ b/src/vs/workbench/api/node/proxyResolver.ts @@ -223,9 +223,9 @@ const fetchFeatureUse: FetchFeatureUseEvent = { }; let timer: NodeJS.Timeout | undefined; - +const enableFeatureUseTelemetry = false; function recordFetchFeatureUse(mainThreadTelemetry: MainThreadTelemetryShape, feature: keyof typeof fetchFeatureUse) { - if (!fetchFeatureUse[feature]++) { + if (enableFeatureUseTelemetry && !fetchFeatureUse[feature]++) { if (timer) { clearTimeout(timer); } @@ -394,9 +394,9 @@ type ProxyAuthenticationEvent = { }; let telemetrySent = false; - +const enableProxyAuthenticationTelemetry = false; function sendTelemetry(mainThreadTelemetry: MainThreadTelemetryShape, authenticate: string[], isRemote: boolean) { - if (telemetrySent || !authenticate.length) { + if (!enableProxyAuthenticationTelemetry || telemetrySent || !authenticate.length) { return; } telemetrySent = true; diff --git a/src/vs/workbench/api/test/browser/extHostLanguageFeatures.test.ts b/src/vs/workbench/api/test/browser/extHostLanguageFeatures.test.ts index 7aa83e5df457..294a8286dbbd 100644 --- a/src/vs/workbench/api/test/browser/extHostLanguageFeatures.test.ts +++ b/src/vs/workbench/api/test/browser/extHostLanguageFeatures.test.ts @@ -56,6 +56,7 @@ import { CodeActionTriggerSource } from '../../../../editor/contrib/codeAction/c import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js'; import { IExtHostTelemetry } from '../../common/extHostTelemetry.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; +import { runWithFakedTimers } from '../../../../base/test/common/timeTravelScheduler.js'; suite('ExtHostLanguageFeatures', function () { @@ -233,62 +234,65 @@ suite('ExtHostLanguageFeatures', function () { // --- code lens test('CodeLens, evil provider', async () => { + return runWithFakedTimers({ useFakeTimers: true }, async () => { + disposables.add(extHost.registerCodeLensProvider(defaultExtension, defaultSelector, new class implements vscode.CodeLensProvider { + provideCodeLenses(): any { + throw new Error('evil'); + } + })); + disposables.add(extHost.registerCodeLensProvider(defaultExtension, defaultSelector, new class implements vscode.CodeLensProvider { + provideCodeLenses() { + return [new types.CodeLens(new types.Range(0, 0, 0, 0))]; + } + })); - disposables.add(extHost.registerCodeLensProvider(defaultExtension, defaultSelector, new class implements vscode.CodeLensProvider { - provideCodeLenses(): any { - throw new Error('evil'); - } - })); - disposables.add(extHost.registerCodeLensProvider(defaultExtension, defaultSelector, new class implements vscode.CodeLensProvider { - provideCodeLenses() { - return [new types.CodeLens(new types.Range(0, 0, 0, 0))]; - } - })); - - await rpcProtocol.sync(); - const value = await getCodeLensModel(languageFeaturesService.codeLensProvider, model, CancellationToken.None); - assert.strictEqual(value.lenses.length, 1); - value.dispose(); + await rpcProtocol.sync(); + const value = await getCodeLensModel(languageFeaturesService.codeLensProvider, model, CancellationToken.None); + assert.strictEqual(value.lenses.length, 1); + value.dispose(); + }); }); test('CodeLens, do not resolve a resolved lens', async () => { - - disposables.add(extHost.registerCodeLensProvider(defaultExtension, defaultSelector, new class implements vscode.CodeLensProvider { - provideCodeLenses(): any { - return [new types.CodeLens( - new types.Range(0, 0, 0, 0), - { command: 'id', title: 'Title' })]; - } - resolveCodeLens(): any { - assert.ok(false, 'do not resolve'); - } - })); - - await rpcProtocol.sync(); - const value = await getCodeLensModel(languageFeaturesService.codeLensProvider, model, CancellationToken.None); - assert.strictEqual(value.lenses.length, 1); - const [data] = value.lenses; - const symbol = await Promise.resolve(data.provider.resolveCodeLens!(model, data.symbol, CancellationToken.None)); - assert.strictEqual(symbol!.command!.id, 'id'); - assert.strictEqual(symbol!.command!.title, 'Title'); - value.dispose(); + return runWithFakedTimers({ useFakeTimers: true }, async () => { + disposables.add(extHost.registerCodeLensProvider(defaultExtension, defaultSelector, new class implements vscode.CodeLensProvider { + provideCodeLenses(): any { + return [new types.CodeLens( + new types.Range(0, 0, 0, 0), + { command: 'id', title: 'Title' })]; + } + resolveCodeLens(): any { + assert.ok(false, 'do not resolve'); + } + })); + + await rpcProtocol.sync(); + const value = await getCodeLensModel(languageFeaturesService.codeLensProvider, model, CancellationToken.None); + assert.strictEqual(value.lenses.length, 1); + const [data] = value.lenses; + const symbol = await Promise.resolve(data.provider.resolveCodeLens!(model, data.symbol, CancellationToken.None)); + assert.strictEqual(symbol!.command!.id, 'id'); + assert.strictEqual(symbol!.command!.title, 'Title'); + value.dispose(); + }); }); test('CodeLens, missing command', async () => { - - disposables.add(extHost.registerCodeLensProvider(defaultExtension, defaultSelector, new class implements vscode.CodeLensProvider { - provideCodeLenses() { - return [new types.CodeLens(new types.Range(0, 0, 0, 0))]; - } - })); - - await rpcProtocol.sync(); - const value = await getCodeLensModel(languageFeaturesService.codeLensProvider, model, CancellationToken.None); - assert.strictEqual(value.lenses.length, 1); - const [data] = value.lenses; - const symbol = await Promise.resolve(data.provider.resolveCodeLens!(model, data.symbol, CancellationToken.None)); - assert.strictEqual(symbol, undefined); - value.dispose(); + return runWithFakedTimers({ useFakeTimers: true }, async () => { + disposables.add(extHost.registerCodeLensProvider(defaultExtension, defaultSelector, new class implements vscode.CodeLensProvider { + provideCodeLenses() { + return [new types.CodeLens(new types.Range(0, 0, 0, 0))]; + } + })); + + await rpcProtocol.sync(); + const value = await getCodeLensModel(languageFeaturesService.codeLensProvider, model, CancellationToken.None); + assert.strictEqual(value.lenses.length, 1); + const [data] = value.lenses; + const symbol = await Promise.resolve(data.provider.resolveCodeLens!(model, data.symbol, CancellationToken.None)); + assert.strictEqual(symbol, undefined); + value.dispose(); + }); }); // --- definition @@ -636,92 +640,96 @@ suite('ExtHostLanguageFeatures', function () { // --- quick fix test('Quick Fix, command data conversion', async () => { - - disposables.add(extHost.registerCodeActionProvider(defaultExtension, defaultSelector, { - provideCodeActions(): vscode.Command[] { - return [ - { command: 'test1', title: 'Testing1' }, - { command: 'test2', title: 'Testing2' } - ]; - } - })); - - await rpcProtocol.sync(); - const value = await getCodeActions(languageFeaturesService.codeActionProvider, model, model.getFullModelRange(), { type: languages.CodeActionTriggerType.Invoke, triggerAction: CodeActionTriggerSource.QuickFix }, Progress.None, CancellationToken.None); - const { validActions: actions } = value; - assert.strictEqual(actions.length, 2); - const [first, second] = actions; - assert.strictEqual(first.action.title, 'Testing1'); - assert.strictEqual(first.action.command!.id, 'test1'); - assert.strictEqual(second.action.title, 'Testing2'); - assert.strictEqual(second.action.command!.id, 'test2'); - value.dispose(); + return runWithFakedTimers({ useFakeTimers: true }, async () => { + disposables.add(extHost.registerCodeActionProvider(defaultExtension, defaultSelector, { + provideCodeActions(): vscode.Command[] { + return [ + { command: 'test1', title: 'Testing1' }, + { command: 'test2', title: 'Testing2' } + ]; + } + })); + + await rpcProtocol.sync(); + const value = await getCodeActions(languageFeaturesService.codeActionProvider, model, model.getFullModelRange(), { type: languages.CodeActionTriggerType.Invoke, triggerAction: CodeActionTriggerSource.QuickFix }, Progress.None, CancellationToken.None); + const { validActions: actions } = value; + assert.strictEqual(actions.length, 2); + const [first, second] = actions; + assert.strictEqual(first.action.title, 'Testing1'); + assert.strictEqual(first.action.command!.id, 'test1'); + assert.strictEqual(second.action.title, 'Testing2'); + assert.strictEqual(second.action.command!.id, 'test2'); + value.dispose(); + }); }); test('Quick Fix, code action data conversion', async () => { - - disposables.add(extHost.registerCodeActionProvider(defaultExtension, defaultSelector, { - provideCodeActions(): vscode.CodeAction[] { - return [ - { - title: 'Testing1', - command: { title: 'Testing1Command', command: 'test1' }, - kind: types.CodeActionKind.Empty.append('test.scope') - } - ]; - } - })); - - await rpcProtocol.sync(); - const value = await getCodeActions(languageFeaturesService.codeActionProvider, model, model.getFullModelRange(), { type: languages.CodeActionTriggerType.Invoke, triggerAction: CodeActionTriggerSource.Default }, Progress.None, CancellationToken.None); - const { validActions: actions } = value; - assert.strictEqual(actions.length, 1); - const [first] = actions; - assert.strictEqual(first.action.title, 'Testing1'); - assert.strictEqual(first.action.command!.title, 'Testing1Command'); - assert.strictEqual(first.action.command!.id, 'test1'); - assert.strictEqual(first.action.kind, 'test.scope'); - value.dispose(); + return runWithFakedTimers({ useFakeTimers: true }, async () => { + disposables.add(extHost.registerCodeActionProvider(defaultExtension, defaultSelector, { + provideCodeActions(): vscode.CodeAction[] { + return [ + { + title: 'Testing1', + command: { title: 'Testing1Command', command: 'test1' }, + kind: types.CodeActionKind.Empty.append('test.scope') + } + ]; + } + })); + + await rpcProtocol.sync(); + const value = await getCodeActions(languageFeaturesService.codeActionProvider, model, model.getFullModelRange(), { type: languages.CodeActionTriggerType.Invoke, triggerAction: CodeActionTriggerSource.Default }, Progress.None, CancellationToken.None); + const { validActions: actions } = value; + assert.strictEqual(actions.length, 1); + const [first] = actions; + assert.strictEqual(first.action.title, 'Testing1'); + assert.strictEqual(first.action.command!.title, 'Testing1Command'); + assert.strictEqual(first.action.command!.id, 'test1'); + assert.strictEqual(first.action.kind, 'test.scope'); + value.dispose(); + }); }); test('Cannot read property \'id\' of undefined, #29469', async () => { + return runWithFakedTimers({ useFakeTimers: true }, async () => { + disposables.add(extHost.registerCodeActionProvider(defaultExtension, defaultSelector, new class implements vscode.CodeActionProvider { + provideCodeActions(): any { + return [ + undefined, + null, + { command: 'test', title: 'Testing' } + ]; + } + })); - disposables.add(extHost.registerCodeActionProvider(defaultExtension, defaultSelector, new class implements vscode.CodeActionProvider { - provideCodeActions(): any { - return [ - undefined, - null, - { command: 'test', title: 'Testing' } - ]; - } - })); - - await rpcProtocol.sync(); - const value = await getCodeActions(languageFeaturesService.codeActionProvider, model, model.getFullModelRange(), { type: languages.CodeActionTriggerType.Invoke, triggerAction: CodeActionTriggerSource.Default }, Progress.None, CancellationToken.None); - const { validActions: actions } = value; - assert.strictEqual(actions.length, 1); - value.dispose(); + await rpcProtocol.sync(); + const value = await getCodeActions(languageFeaturesService.codeActionProvider, model, model.getFullModelRange(), { type: languages.CodeActionTriggerType.Invoke, triggerAction: CodeActionTriggerSource.Default }, Progress.None, CancellationToken.None); + const { validActions: actions } = value; + assert.strictEqual(actions.length, 1); + value.dispose(); + }); }); test('Quick Fix, evil provider', async () => { + return runWithFakedTimers({ useFakeTimers: true }, async () => { + disposables.add(extHost.registerCodeActionProvider(defaultExtension, defaultSelector, new class implements vscode.CodeActionProvider { + provideCodeActions(): any { + throw new Error('evil'); + } + })); + disposables.add(extHost.registerCodeActionProvider(defaultExtension, defaultSelector, new class implements vscode.CodeActionProvider { + provideCodeActions(): any { + return [{ command: 'test', title: 'Testing' }]; + } + })); - disposables.add(extHost.registerCodeActionProvider(defaultExtension, defaultSelector, new class implements vscode.CodeActionProvider { - provideCodeActions(): any { - throw new Error('evil'); - } - })); - disposables.add(extHost.registerCodeActionProvider(defaultExtension, defaultSelector, new class implements vscode.CodeActionProvider { - provideCodeActions(): any { - return [{ command: 'test', title: 'Testing' }]; - } - })); - - await rpcProtocol.sync(); - const value = await getCodeActions(languageFeaturesService.codeActionProvider, model, model.getFullModelRange(), { type: languages.CodeActionTriggerType.Invoke, triggerAction: CodeActionTriggerSource.QuickFix }, Progress.None, CancellationToken.None); - const { validActions: actions } = value; - assert.strictEqual(actions.length, 1); - value.dispose(); + await rpcProtocol.sync(); + const value = await getCodeActions(languageFeaturesService.codeActionProvider, model, model.getFullModelRange(), { type: languages.CodeActionTriggerType.Invoke, triggerAction: CodeActionTriggerSource.QuickFix }, Progress.None, CancellationToken.None); + const { validActions: actions } = value; + assert.strictEqual(actions.length, 1); + value.dispose(); + }); }); // --- navigate types @@ -965,103 +973,107 @@ suite('ExtHostLanguageFeatures', function () { // --- suggestions test('Suggest, order 1/3', async () => { + return runWithFakedTimers({ useFakeTimers: true }, async () => { + disposables.add(extHost.registerCompletionItemProvider(defaultExtension, '*', new class implements vscode.CompletionItemProvider { + provideCompletionItems(): any { + return [new types.CompletionItem('testing1')]; + } + }, [])); - disposables.add(extHost.registerCompletionItemProvider(defaultExtension, '*', new class implements vscode.CompletionItemProvider { - provideCompletionItems(): any { - return [new types.CompletionItem('testing1')]; - } - }, [])); - - disposables.add(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider { - provideCompletionItems(): any { - return [new types.CompletionItem('testing2')]; - } - }, [])); + disposables.add(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider { + provideCompletionItems(): any { + return [new types.CompletionItem('testing2')]; + } + }, [])); - await rpcProtocol.sync(); - const value = await provideSuggestionItems(languageFeaturesService.completionProvider, model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set().add(languages.CompletionItemKind.Snippet))); - assert.strictEqual(value.items.length, 1); - assert.strictEqual(value.items[0].completion.insertText, 'testing2'); - value.disposable.dispose(); + await rpcProtocol.sync(); + const value = await provideSuggestionItems(languageFeaturesService.completionProvider, model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set().add(languages.CompletionItemKind.Snippet))); + assert.strictEqual(value.items.length, 1); + assert.strictEqual(value.items[0].completion.insertText, 'testing2'); + value.disposable.dispose(); + }); }); test('Suggest, order 2/3', async () => { + return runWithFakedTimers({ useFakeTimers: true }, async () => { + disposables.add(extHost.registerCompletionItemProvider(defaultExtension, '*', new class implements vscode.CompletionItemProvider { + provideCompletionItems(): any { + return [new types.CompletionItem('weak-selector')]; // weaker selector but result + } + }, [])); - disposables.add(extHost.registerCompletionItemProvider(defaultExtension, '*', new class implements vscode.CompletionItemProvider { - provideCompletionItems(): any { - return [new types.CompletionItem('weak-selector')]; // weaker selector but result - } - }, [])); - - disposables.add(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider { - provideCompletionItems(): any { - return []; // stronger selector but not a good result; - } - }, [])); + disposables.add(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider { + provideCompletionItems(): any { + return []; // stronger selector but not a good result; + } + }, [])); - await rpcProtocol.sync(); - const value = await provideSuggestionItems(languageFeaturesService.completionProvider, model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set().add(languages.CompletionItemKind.Snippet))); - assert.strictEqual(value.items.length, 1); - assert.strictEqual(value.items[0].completion.insertText, 'weak-selector'); - value.disposable.dispose(); + await rpcProtocol.sync(); + const value = await provideSuggestionItems(languageFeaturesService.completionProvider, model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set().add(languages.CompletionItemKind.Snippet))); + assert.strictEqual(value.items.length, 1); + assert.strictEqual(value.items[0].completion.insertText, 'weak-selector'); + value.disposable.dispose(); + }); }); test('Suggest, order 3/3', async () => { + return runWithFakedTimers({ useFakeTimers: true }, async () => { + disposables.add(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider { + provideCompletionItems(): any { + return [new types.CompletionItem('strong-1')]; + } + }, [])); - disposables.add(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider { - provideCompletionItems(): any { - return [new types.CompletionItem('strong-1')]; - } - }, [])); - - disposables.add(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider { - provideCompletionItems(): any { - return [new types.CompletionItem('strong-2')]; - } - }, [])); - - await rpcProtocol.sync(); - const value = await provideSuggestionItems(languageFeaturesService.completionProvider, model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set().add(languages.CompletionItemKind.Snippet))); - assert.strictEqual(value.items.length, 2); - assert.strictEqual(value.items[0].completion.insertText, 'strong-1'); // sort by label - assert.strictEqual(value.items[1].completion.insertText, 'strong-2'); - value.disposable.dispose(); + disposables.add(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider { + provideCompletionItems(): any { + return [new types.CompletionItem('strong-2')]; + } + }, [])); + + await rpcProtocol.sync(); + const value = await provideSuggestionItems(languageFeaturesService.completionProvider, model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set().add(languages.CompletionItemKind.Snippet))); + assert.strictEqual(value.items.length, 2); + assert.strictEqual(value.items[0].completion.insertText, 'strong-1'); // sort by label + assert.strictEqual(value.items[1].completion.insertText, 'strong-2'); + value.disposable.dispose(); + }); }); test('Suggest, evil provider', async () => { + return runWithFakedTimers({ useFakeTimers: true }, async () => { + disposables.add(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider { + provideCompletionItems(): any { + throw new Error('evil'); + } + }, [])); - disposables.add(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider { - provideCompletionItems(): any { - throw new Error('evil'); - } - }, [])); - - disposables.add(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider { - provideCompletionItems(): any { - return [new types.CompletionItem('testing')]; - } - }, [])); - + disposables.add(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider { + provideCompletionItems(): any { + return [new types.CompletionItem('testing')]; + } + }, [])); - await rpcProtocol.sync(); - const value = await provideSuggestionItems(languageFeaturesService.completionProvider, model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set().add(languages.CompletionItemKind.Snippet))); - assert.strictEqual(value.items[0].container.incomplete, false); - value.disposable.dispose(); + await rpcProtocol.sync(); + const value = await provideSuggestionItems(languageFeaturesService.completionProvider, model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set().add(languages.CompletionItemKind.Snippet))); + assert.strictEqual(value.items[0].container.incomplete, false); + value.disposable.dispose(); + }); }); test('Suggest, CompletionList', async () => { + return runWithFakedTimers({ useFakeTimers: true }, async () => { + disposables.add(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider { + provideCompletionItems(): any { + return new types.CompletionList([new types.CompletionItem('hello')], true); + } + }, [])); - disposables.add(extHost.registerCompletionItemProvider(defaultExtension, defaultSelector, new class implements vscode.CompletionItemProvider { - provideCompletionItems(): any { - return new types.CompletionList([new types.CompletionItem('hello')], true); - } - }, [])); - - await rpcProtocol.sync(); - await provideSuggestionItems(languageFeaturesService.completionProvider, model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set().add(languages.CompletionItemKind.Snippet))).then(model => { - assert.strictEqual(model.items[0].container.incomplete, true); - model.disposable.dispose(); + await rpcProtocol.sync(); + await provideSuggestionItems(languageFeaturesService.completionProvider, model, new EditorPosition(1, 1), new CompletionOptions(undefined, new Set().add(languages.CompletionItemKind.Snippet))).then(model => { + assert.strictEqual(model.items[0].container.incomplete, true); + model.disposable.dispose(); + }); }); }); diff --git a/src/vs/workbench/api/test/browser/mainThreadConfiguration.test.ts b/src/vs/workbench/api/test/browser/mainThreadConfiguration.test.ts index a616447bbcc1..fe7e7337167b 100644 --- a/src/vs/workbench/api/test/browser/mainThreadConfiguration.test.ts +++ b/src/vs/workbench/api/test/browser/mainThreadConfiguration.test.ts @@ -15,9 +15,12 @@ import { SingleProxyRPCProtocol } from '../common/testRPCProtocol.js'; import { IConfigurationService, ConfigurationTarget } from '../../../../platform/configuration/common/configuration.js'; import { WorkspaceService } from '../../../services/configuration/browser/configurationService.js'; import { IEnvironmentService } from '../../../../platform/environment/common/environment.js'; +import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; suite('MainThreadConfiguration', function () { + ensureNoDisposablesAreLeakedInTestSuite(); + const proxy = { $initializeConfiguration: () => { } }; diff --git a/src/vs/workbench/api/test/common/extHostTerminalShellIntegration.test.ts b/src/vs/workbench/api/test/common/extHostTerminalShellIntegration.test.ts new file mode 100644 index 000000000000..2b9b89da2250 --- /dev/null +++ b/src/vs/workbench/api/test/common/extHostTerminalShellIntegration.test.ts @@ -0,0 +1,307 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { type Terminal, type TerminalShellExecution, type TerminalShellExecutionCommandLine, type TerminalShellExecutionStartEvent } from 'vscode'; +import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; +import { InternalTerminalShellIntegration } from '../../common/extHostTerminalShellIntegration.js'; +import { Emitter } from '../../../../base/common/event.js'; +import { TerminalShellExecutionCommandLineConfidence } from '../../common/extHostTypes.js'; +import { deepStrictEqual, notStrictEqual, strictEqual } from 'assert'; +import type { URI } from '../../../../base/common/uri.js'; +import { DeferredPromise } from '../../../../base/common/async.js'; + +function cmdLine(value: string): TerminalShellExecutionCommandLine { + return Object.freeze({ + confidence: TerminalShellExecutionCommandLineConfidence.High, + value, + isTrusted: true, + }); +} +function asCmdLine(value: string | TerminalShellExecutionCommandLine): TerminalShellExecutionCommandLine { + if (typeof value === 'string') { + return cmdLine(value); + } + return value; +} +function vsc(data: string) { + return `\x1b]633;${data}\x07`; +} + +const testCommandLine = 'echo hello world'; +const testCommandLine2 = 'echo goodbye world'; + +interface ITrackedEvent { + type: 'start' | 'data' | 'end'; + commandLine: string; + data?: string; +} + +suite('InternalTerminalShellIntegration', () => { + const store = ensureNoDisposablesAreLeakedInTestSuite(); + + let si: InternalTerminalShellIntegration; + let terminal: Terminal; + let onDidStartTerminalShellExecution: Emitter; + let trackedEvents: ITrackedEvent[]; + let readIteratorsFlushed: Promise[]; + + async function startExecutionAwaitObject(commandLine: string | TerminalShellExecutionCommandLine, cwd?: URI): Promise { + return await new Promise(r => { + store.add(onDidStartTerminalShellExecution.event(e => { + r(e.execution); + })); + si.startShellExecution(asCmdLine(commandLine), cwd); + }); + } + + async function endExecutionAwaitObject(commandLine: string | TerminalShellExecutionCommandLine): Promise { + return await new Promise(r => { + store.add(si.onDidRequestEndExecution(e => r(e.execution))); + si.endShellExecution(asCmdLine(commandLine), 0); + }); + } + + async function emitData(data: string): Promise { + // AsyncIterableObjects are initialized in a microtask, this doesn't matter in practice + // since the events will always come through in different events. + await new Promise(r => queueMicrotask(r)); + si.emitData(data); + } + + function assertTrackedEvents(expected: ITrackedEvent[]) { + deepStrictEqual(trackedEvents, expected); + } + + function assertNonDataTrackedEvents(expected: ITrackedEvent[]) { + deepStrictEqual(trackedEvents.filter(e => e.type !== 'data'), expected); + } + + function assertDataTrackedEvents(expected: ITrackedEvent[]) { + deepStrictEqual(trackedEvents.filter(e => e.type === 'data'), expected); + } + + setup(() => { + terminal = Symbol('testTerminal') as any; + onDidStartTerminalShellExecution = store.add(new Emitter()); + si = store.add(new InternalTerminalShellIntegration(terminal, onDidStartTerminalShellExecution)); + + trackedEvents = []; + readIteratorsFlushed = []; + store.add(onDidStartTerminalShellExecution.event(async e => { + trackedEvents.push({ + type: 'start', + commandLine: e.execution.commandLine.value, + }); + const stream = e.execution.read(); + const readIteratorsFlushedDeferred = new DeferredPromise(); + readIteratorsFlushed.push(readIteratorsFlushedDeferred.p); + for await (const data of stream) { + trackedEvents.push({ + type: 'data', + commandLine: e.execution.commandLine.value, + data, + }); + } + readIteratorsFlushedDeferred.complete(); + })); + store.add(si.onDidRequestEndExecution(e => trackedEvents.push({ + type: 'end', + commandLine: e.execution.commandLine.value, + }))); + }); + + test('simple execution', async () => { + const execution = await startExecutionAwaitObject(testCommandLine); + deepStrictEqual(execution.commandLine.value, testCommandLine); + const execution2 = await endExecutionAwaitObject(testCommandLine); + strictEqual(execution2, execution); + + assertTrackedEvents([ + { commandLine: testCommandLine, type: 'start' }, + { commandLine: testCommandLine, type: 'end' }, + ]); + }); + + test('different execution unexpectedly ended', async () => { + const execution1 = await startExecutionAwaitObject(testCommandLine); + const execution2 = await endExecutionAwaitObject(testCommandLine2); + strictEqual(execution1, execution2, 'when a different execution is ended, the one that started first should end'); + + assertTrackedEvents([ + { commandLine: testCommandLine, type: 'start' }, + // This looks weird, but it's the same execution behind the scenes, just the command + // line was updated + { commandLine: testCommandLine2, type: 'end' }, + ]); + }); + + test('no end event', async () => { + const execution1 = await startExecutionAwaitObject(testCommandLine); + const endedExecution = await new Promise(r => { + store.add(si.onDidRequestEndExecution(e => r(e.execution))); + startExecutionAwaitObject(testCommandLine2); + }); + strictEqual(execution1, endedExecution, 'when no end event is fired, the current execution should end'); + + // Clean up disposables + await endExecutionAwaitObject(testCommandLine2); + await Promise.all(readIteratorsFlushed); + + assertTrackedEvents([ + { commandLine: testCommandLine, type: 'start' }, + { commandLine: testCommandLine, type: 'end' }, + { commandLine: testCommandLine2, type: 'start' }, + { commandLine: testCommandLine2, type: 'end' }, + ]); + }); + + suite('executeCommand', () => { + test('^C to clear previous command', async () => { + const commandLine = 'foo'; + const apiRequestedExecution = si.requestNewShellExecution(cmdLine(commandLine), undefined); + const firstExecution = await startExecutionAwaitObject('^C'); + notStrictEqual(firstExecution, apiRequestedExecution.value); + si.emitData('SIGINT'); + si.endShellExecution(cmdLine('^C'), 0); + si.startShellExecution(cmdLine(commandLine), undefined); + await emitData('1'); + await endExecutionAwaitObject(commandLine); + // IMPORTANT: We cannot reliably assert the order of data events here because flushing + // of the async iterator is asynchronous and could happen after the execution's end + // event fires if an execution is started immediately afterwards. + await Promise.all(readIteratorsFlushed); + + assertNonDataTrackedEvents([ + { commandLine: '^C', type: 'start' }, + { commandLine: '^C', type: 'end' }, + { commandLine, type: 'start' }, + { commandLine, type: 'end' }, + ]); + assertDataTrackedEvents([ + { commandLine: '^C', type: 'data', data: 'SIGINT' }, + { commandLine, type: 'data', data: '1' }, + ]); + }); + + test('multi-line command line', async () => { + const commandLine = 'foo\nbar'; + const apiRequestedExecution = si.requestNewShellExecution(cmdLine(commandLine), undefined); + const startedExecution = await startExecutionAwaitObject('foo'); + strictEqual(startedExecution, apiRequestedExecution.value); + + si.emitData('1'); + si.emitData('2'); + si.endShellExecution(cmdLine('foo'), 0); + si.startShellExecution(cmdLine('bar'), undefined); + si.emitData('3'); + si.emitData('4'); + const endedExecution = await endExecutionAwaitObject('bar'); + strictEqual(startedExecution, endedExecution); + + assertTrackedEvents([ + { commandLine, type: 'start' }, + { commandLine, type: 'data', data: '1' }, + { commandLine, type: 'data', data: '2' }, + { commandLine, type: 'data', data: '3' }, + { commandLine, type: 'data', data: '4' }, + { commandLine, type: 'end' }, + ]); + }); + + test('multi-line command with long second command', async () => { + const commandLine = 'echo foo\ncat << EOT\nline1\nline2\nline3\nEOT'; + const subCommandLine1 = 'echo foo'; + const subCommandLine2 = 'cat << EOT\nline1\nline2\nline3\nEOT'; + + const apiRequestedExecution = si.requestNewShellExecution(cmdLine(commandLine), undefined); + const startedExecution = await startExecutionAwaitObject(subCommandLine1); + strictEqual(startedExecution, apiRequestedExecution.value); + + si.emitData(`${vsc('C')}foo`); + si.endShellExecution(cmdLine(subCommandLine1), 0); + si.startShellExecution(cmdLine(subCommandLine2), undefined); + si.emitData(`${vsc('C')}line1`); + si.emitData('line2'); + si.emitData('line3'); + const endedExecution = await endExecutionAwaitObject(subCommandLine2); + strictEqual(startedExecution, endedExecution); + + assertTrackedEvents([ + { commandLine, type: 'start' }, + { commandLine, type: 'data', data: `${vsc('C')}foo` }, + { commandLine, type: 'data', data: `${vsc('C')}line1` }, + { commandLine, type: 'data', data: 'line2' }, + { commandLine, type: 'data', data: 'line3' }, + { commandLine, type: 'end' }, + ]); + }); + + test('multi-line command comment followed by long second command', async () => { + const commandLine = '# comment: foo\ncat << EOT\nline1\nline2\nline3\nEOT'; + const subCommandLine1 = '# comment: foo'; + const subCommandLine2 = 'cat << EOT\nline1\nline2\nline3\nEOT'; + + const apiRequestedExecution = si.requestNewShellExecution(cmdLine(commandLine), undefined); + const startedExecution = await startExecutionAwaitObject(subCommandLine1); + strictEqual(startedExecution, apiRequestedExecution.value); + + si.emitData(`${vsc('C')}`); + si.endShellExecution(cmdLine(subCommandLine1), 0); + si.startShellExecution(cmdLine(subCommandLine2), undefined); + si.emitData(`${vsc('C')}line1`); + si.emitData('line2'); + si.emitData('line3'); + const endedExecution = await endExecutionAwaitObject(subCommandLine2); + strictEqual(startedExecution, endedExecution); + + assertTrackedEvents([ + { commandLine, type: 'start' }, + { commandLine, type: 'data', data: `${vsc('C')}` }, + { commandLine, type: 'data', data: `${vsc('C')}line1` }, + { commandLine, type: 'data', data: 'line2' }, + { commandLine, type: 'data', data: 'line3' }, + { commandLine, type: 'end' }, + ]); + }); + + test('4 multi-line commands with output', async () => { + const commandLine = 'echo "\nfoo"\ngit commit -m "hello\n\nworld"\ncat << EOT\nline1\nline2\nline3\nEOT\n{\necho "foo"\n}'; + const subCommandLine1 = 'echo "\nfoo"'; + const subCommandLine2 = 'git commit -m "hello\n\nworld"'; + const subCommandLine3 = 'cat << EOT\nline1\nline2\nline3\nEOT'; + const subCommandLine4 = '{\necho "foo"\n}'; + + const apiRequestedExecution = si.requestNewShellExecution(cmdLine(commandLine), undefined); + const startedExecution = await startExecutionAwaitObject(subCommandLine1); + strictEqual(startedExecution, apiRequestedExecution.value); + + si.emitData(`${vsc('C')}foo`); + si.endShellExecution(cmdLine(subCommandLine1), 0); + si.startShellExecution(cmdLine(subCommandLine2), undefined); + si.emitData(`${vsc('C')} 2 files changed, 61 insertions(+), 2 deletions(-)`); + si.endShellExecution(cmdLine(subCommandLine2), 0); + si.startShellExecution(cmdLine(subCommandLine3), undefined); + si.emitData(`${vsc('C')}line1`); + si.emitData('line2'); + si.emitData('line3'); + si.endShellExecution(cmdLine(subCommandLine3), 0); + si.emitData(`${vsc('C')}foo`); + si.startShellExecution(cmdLine(subCommandLine4), undefined); + const endedExecution = await endExecutionAwaitObject(subCommandLine4); + strictEqual(startedExecution, endedExecution); + + assertTrackedEvents([ + { commandLine, type: 'start' }, + { commandLine, type: 'data', data: `${vsc('C')}foo` }, + { commandLine, type: 'data', data: `${vsc('C')} 2 files changed, 61 insertions(+), 2 deletions(-)` }, + { commandLine, type: 'data', data: `${vsc('C')}line1` }, + { commandLine, type: 'data', data: 'line2' }, + { commandLine, type: 'data', data: 'line3' }, + { commandLine, type: 'data', data: `${vsc('C')}foo` }, + { commandLine, type: 'end' }, + ]); + }); + }); +}); diff --git a/src/vs/workbench/api/test/node/extHostTunnelService.test.ts b/src/vs/workbench/api/test/node/extHostTunnelService.test.ts index d260fbe726e8..cd2e03b12476 100644 --- a/src/vs/workbench/api/test/node/extHostTunnelService.test.ts +++ b/src/vs/workbench/api/test/node/extHostTunnelService.test.ts @@ -5,6 +5,7 @@ import assert from 'assert'; import { findPorts, getRootProcesses, getSockets, loadConnectionTable, loadListeningPorts, parseIpAddress, tryFindRootPorts } from '../../node/extHostTunnelService.js'; +import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../base/test/common/utils.js'; const tcp = ` sl local_address rem_address st tx_queue rx_queue tr tm->when retrnsmt uid timeout inode @@ -225,6 +226,7 @@ const psStdOut = 0 S node 1058 1056 0 80 0 - 770 pipe_w 888 9 14:43 ? 00:00:00 grep root`; suite('ExtHostTunnelService', () => { + ensureNoDisposablesAreLeakedInTestSuite(); test('getSockets', function () { const result = getSockets(procSockets); assert.strictEqual(Object.keys(result).length, 75); diff --git a/src/vs/workbench/browser/actions/helpActions.ts b/src/vs/workbench/browser/actions/helpActions.ts index 3a366903e8af..efda59bfc12f 100644 --- a/src/vs/workbench/browser/actions/helpActions.ts +++ b/src/vs/workbench/browser/actions/helpActions.ts @@ -309,7 +309,9 @@ class OpenPrivacyStatementUrlAction extends Action2 { } class GetStartedWithAccessibilityFeatures extends Action2 { + static readonly ID = 'workbench.action.getStartedWithAccessibilityFeatures'; + constructor() { super({ id: GetStartedWithAccessibilityFeatures.ID, @@ -329,6 +331,30 @@ class GetStartedWithAccessibilityFeatures extends Action2 { } } +class GetStartedWithCopilot extends Action2 { + + static readonly ID = 'workbench.action.getStartedWithCopilot'; + static readonly AVAILABE = !!product.defaultChatAgent?.documentationUrl; + + constructor() { + super({ + id: GetStartedWithCopilot.ID, + title: localize2('getStartedWithCopilot', 'Get Started with Copilot'), + category: Categories.Help, + f1: true, + menu: { + id: MenuId.MenubarHelpMenu, + group: '1_welcome', + order: 7 + } + }); + } + run(accessor: ServicesAccessor): void { + const openerService = accessor.get(IOpenerService); + openerService.open(URI.parse(product.defaultChatAgent!.documentationUrl)); + } +} + // --- Actions Registration if (KeybindingsReferenceAction.AVAILABLE) { @@ -368,3 +394,7 @@ if (OpenPrivacyStatementUrlAction.AVAILABE) { } registerAction2(GetStartedWithAccessibilityFeatures); + +if (GetStartedWithCopilot.AVAILABE) { + registerAction2(GetStartedWithCopilot); +} diff --git a/src/vs/workbench/browser/actions/layoutActions.ts b/src/vs/workbench/browser/actions/layoutActions.ts index fd3a68425898..47ff678409c9 100644 --- a/src/vs/workbench/browser/actions/layoutActions.ts +++ b/src/vs/workbench/browser/actions/layoutActions.ts @@ -22,7 +22,7 @@ import { IPaneCompositePartService } from '../../services/panecomposite/browser/ import { ToggleAuxiliaryBarAction } from '../parts/auxiliarybar/auxiliaryBarActions.js'; import { TogglePanelAction } from '../parts/panel/panelActions.js'; import { ICommandService } from '../../../platform/commands/common/commands.js'; -import { AuxiliaryBarVisibleContext, PanelAlignmentContext, PanelVisibleContext, SideBarVisibleContext, FocusedViewContext, InEditorZenModeContext, IsMainEditorCenteredLayoutContext, MainEditorAreaVisibleContext, IsMainWindowFullscreenContext, PanelPositionContext, IsAuxiliaryWindowFocusedContext, TitleBarStyleContext } from '../../common/contextkeys.js'; +import { AuxiliaryBarVisibleContext, PanelAlignmentContext, PanelVisibleContext, SideBarVisibleContext, FocusedViewContext, InEditorZenModeContext, IsMainEditorCenteredLayoutContext, MainEditorAreaVisibleContext, IsMainWindowFullscreenContext, PanelPositionContext, IsAuxiliaryWindowFocusedContext } from '../../common/contextkeys.js'; import { Codicon } from '../../../base/common/codicons.js'; import { ThemeIcon } from '../../../base/common/themables.js'; import { DisposableStore } from '../../../base/common/lifecycle.js'; @@ -30,7 +30,6 @@ import { registerIcon } from '../../../platform/theme/common/iconRegistry.js'; import { ICommandActionTitle } from '../../../platform/action/common/action.js'; import { mainWindow } from '../../../base/browser/window.js'; import { IKeybindingService } from '../../../platform/keybinding/common/keybinding.js'; -import { TitlebarStyle } from '../../../platform/window/common/window.js'; import { IPreferencesService } from '../../services/preferences/common/preferences.js'; import { QuickInputAlignmentContextKey } from '../../../platform/quickinput/browser/quickInput.js'; import { IEditorGroupsService } from '../../services/editor/common/editorGroupsService.js'; @@ -773,20 +772,6 @@ if (isWindows || isLinux || isWeb) { return accessor.get(IWorkbenchLayoutService).toggleMenuBar(); } }); - - // Add separately to title bar context menu so we can use a different title - for (const menuId of [MenuId.TitleBarContext, MenuId.TitleBarTitleContext]) { - MenuRegistry.appendMenuItem(menuId, { - command: { - id: 'workbench.action.toggleMenuBar', - title: localize('miMenuBarNoMnemonic', "Menu Bar"), - toggled: ContextKeyExpr.and(IsMacNativeContext.toNegated(), ContextKeyExpr.notEquals('config.window.menuBarVisibility', 'hidden'), ContextKeyExpr.notEquals('config.window.menuBarVisibility', 'toggle'), ContextKeyExpr.notEquals('config.window.menuBarVisibility', 'compact')) - }, - when: ContextKeyExpr.and(IsAuxiliaryWindowFocusedContext.toNegated(), ContextKeyExpr.notEquals(TitleBarStyleContext.key, TitlebarStyle.NATIVE), IsMainWindowFullscreenContext.negate()), - group: '2_config', - order: 0 - }); - } } // --- Reset View Locations diff --git a/src/vs/workbench/browser/actions/textInputActions.ts b/src/vs/workbench/browser/actions/textInputActions.ts index 144544e2032d..7932de953a88 100644 --- a/src/vs/workbench/browser/actions/textInputActions.ts +++ b/src/vs/workbench/browser/actions/textInputActions.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { IAction, Action, Separator } from '../../../base/common/actions.js'; +import { IAction, Separator, toAction } from '../../../base/common/actions.js'; import { localize } from '../../../nls.js'; import { IWorkbenchLayoutService } from '../../services/layout/browser/layoutService.js'; import { IContextMenuService } from '../../../platform/contextview/browser/contextView.js'; @@ -18,30 +18,29 @@ import { Lazy } from '../../../base/common/lazy.js'; export function createTextInputActions(clipboardService: IClipboardService): IAction[] { return [ - // Undo/Redo - new Action('undo', localize('undo', "Undo"), undefined, true, async () => getActiveDocument().execCommand('undo')), - new Action('redo', localize('redo', "Redo"), undefined, true, async () => getActiveDocument().execCommand('redo')), + toAction({ id: 'undo', label: localize('undo', "Undo"), run: () => getActiveDocument().execCommand('undo') }), + toAction({ id: 'redo', label: localize('redo', "Redo"), run: () => getActiveDocument().execCommand('redo') }), new Separator(), - - // Cut / Copy / Paste - new Action('editor.action.clipboardCutAction', localize('cut', "Cut"), undefined, true, async () => getActiveDocument().execCommand('cut')), - new Action('editor.action.clipboardCopyAction', localize('copy', "Copy"), undefined, true, async () => getActiveDocument().execCommand('copy')), - new Action('editor.action.clipboardPasteAction', localize('paste', "Paste"), undefined, true, async element => { - const clipboardText = await clipboardService.readText(); - if (isHTMLTextAreaElement(element) || isHTMLInputElement(element)) { - const selectionStart = element.selectionStart || 0; - const selectionEnd = element.selectionEnd || 0; - - element.value = `${element.value.substring(0, selectionStart)}${clipboardText}${element.value.substring(selectionEnd, element.value.length)}`; - element.selectionStart = selectionStart + clipboardText.length; - element.selectionEnd = element.selectionStart; - element.dispatchEvent(new Event('input', { bubbles: true, cancelable: true })); + toAction({ id: 'editor.action.clipboardCutAction', label: localize('cut', "Cut"), run: () => getActiveDocument().execCommand('cut') }), + toAction({ id: 'editor.action.clipboardCopyAction', label: localize('copy', "Copy"), run: () => getActiveDocument().execCommand('copy') }), + toAction({ + id: 'editor.action.clipboardPasteAction', + label: localize('paste', "Paste"), + run: async (element: unknown) => { + const clipboardText = await clipboardService.readText(); + if (isHTMLTextAreaElement(element) || isHTMLInputElement(element)) { + const selectionStart = element.selectionStart || 0; + const selectionEnd = element.selectionEnd || 0; + + element.value = `${element.value.substring(0, selectionStart)}${clipboardText}${element.value.substring(selectionEnd, element.value.length)}`; + element.selectionStart = selectionStart + clipboardText.length; + element.selectionEnd = element.selectionStart; + element.dispatchEvent(new Event('input', { bubbles: true, cancelable: true })); + } } }), new Separator(), - - // Select All - new Action('editor.action.selectAll', localize('selectAll', "Select All"), undefined, true, async () => getActiveDocument().execCommand('selectAll')) + toAction({ id: 'editor.action.selectAll', label: localize('selectAll', "Select All"), run: () => getActiveDocument().execCommand('selectAll') }) ]; } diff --git a/src/vs/workbench/browser/dnd.ts b/src/vs/workbench/browser/dnd.ts index 239e5bb89ac5..72def8c4b99c 100644 --- a/src/vs/workbench/browser/dnd.ts +++ b/src/vs/workbench/browser/dnd.ts @@ -646,18 +646,24 @@ export class ResourceListDnDHandler implements IListDragAndDrop { onDragStart(data: IDragAndDropData, originalEvent: DragEvent): void { const resources: URI[] = []; - for (const element of (data as ElementsDragAndDropData).elements) { + const elements = (data as ElementsDragAndDropData).elements; + for (const element of elements) { const resource = this.toResource(element); if (resource) { resources.push(resource); } } + this.onWillDragElements(elements, originalEvent); if (resources.length) { // Apply some datatransfer types to allow for dragging the element outside of the application this.instantiationService.invokeFunction(accessor => fillEditorsDragData(accessor, resources, originalEvent)); } } + protected onWillDragElements(elements: readonly T[], originalEvent: DragEvent): void { + // noop + } + onDragOver(data: IDragAndDropData, targetElement: T, targetIndex: number, targetSector: ListViewTargetSector | undefined, originalEvent: DragEvent): boolean | ITreeDragOverReaction { return false; } diff --git a/src/vs/workbench/browser/layout.ts b/src/vs/workbench/browser/layout.ts index fc33edf32941..501d95a87132 100644 --- a/src/vs/workbench/browser/layout.ts +++ b/src/vs/workbench/browser/layout.ts @@ -599,10 +599,9 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi for (const container of this.containers) { const isMainContainer = container === this.mainContainer; const isActiveContainer = this.activeContainer === container; - const containerWindowId = getWindowId(getWindow(container)); let windowBorder = false; - if (!this.state.runtime.mainWindowFullscreen && !this.state.runtime.maximized.has(containerWindowId) && (activeBorder || inactiveBorder)) { + if (!this.state.runtime.mainWindowFullscreen && (activeBorder || inactiveBorder)) { windowBorder = true; // If the inactive color is missing, fallback to the active one @@ -2079,11 +2078,10 @@ export abstract class Layout extends Disposable implements IWorkbenchLayoutServi } getMainWindowBorderRadius(): string | undefined { - return this.state.runtime.mainWindowBorder && isMacintosh ? '5px' : undefined; + return this.state.runtime.mainWindowBorder && isMacintosh ? '10px' : undefined; } isPanelMaximized(): boolean { - // the workbench grid currently prevents us from supporting panel maximization with non-center panel alignment return (this.getPanelAlignment() === 'center' || !isHorizontal(this.getPanelPosition())) && !this.isVisible(Parts.EDITOR_PART, mainWindow); } diff --git a/src/vs/workbench/browser/media/style.css b/src/vs/workbench/browser/media/style.css index 4a83f3b2d90a..ffcf69618067 100644 --- a/src/vs/workbench/browser/media/style.css +++ b/src/vs/workbench/browser/media/style.css @@ -231,7 +231,7 @@ body { .monaco-workbench textarea:focus, .monaco-workbench input[type="search"]:focus, .monaco-workbench input[type="checkbox"]:focus { - outline-width: 1px; + outline-width: 2px; outline-style: solid; outline-offset: -1px; outline-color: var(--vscode-focusBorder); diff --git a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts index f2af5fb3a2f2..ed629878cddd 100644 --- a/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts +++ b/src/vs/workbench/browser/parts/activitybar/activitybarPart.ts @@ -280,8 +280,7 @@ export class ActivityBarCompositeBar extends PaneCompositeBar { return; // prevent menu bar from installing twice #110720 } - this.menuBarContainer = document.createElement('div'); - this.menuBarContainer.classList.add('menubar'); + this.menuBarContainer = $('.menubar'); const content = assertIsDefined(this.element); content.prepend(this.menuBarContainer); diff --git a/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart.ts b/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart.ts index 508322d359f7..75a3ce42d689 100644 --- a/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart.ts +++ b/src/vs/workbench/browser/parts/auxiliarybar/auxiliaryBarPart.ts @@ -80,7 +80,7 @@ export class AuxiliaryBarPart extends AbstractPaneCompositePart { readonly priority = LayoutPriority.Low; - private configuration = this.resolveConfiguration(); + private configuration: IAuxiliaryBarPartConfiguration; constructor( @INotificationService notificationService: INotificationService, @@ -125,6 +125,8 @@ export class AuxiliaryBarPart extends AbstractPaneCompositePart { menuService, ); + this.configuration = this.resolveConfiguration(); + this._register(configurationService.onDidChangeConfiguration(e => { if (e.affectsConfiguration(LayoutSettings.ACTIVITY_BAR_LOCATION)) { this.configuration = this.resolveConfiguration(); diff --git a/src/vs/workbench/browser/parts/compositeBarActions.ts b/src/vs/workbench/browser/parts/compositeBarActions.ts index 136b41c9800c..5a792fe9a3a0 100644 --- a/src/vs/workbench/browser/parts/compositeBarActions.ts +++ b/src/vs/workbench/browser/parts/compositeBarActions.ts @@ -9,7 +9,7 @@ import { $, addDisposableListener, append, clearNode, EventHelper, EventType, ge import { ICommandService } from '../../../platform/commands/common/commands.js'; import { toDisposable, DisposableStore, MutableDisposable } from '../../../base/common/lifecycle.js'; import { IContextMenuService } from '../../../platform/contextview/browser/contextView.js'; -import { IThemeService, IColorTheme } from '../../../platform/theme/common/themeService.js'; +import { IThemeService, IColorTheme, IThemeChangeEvent } from '../../../platform/theme/common/themeService.js'; import { NumberBadge, IBadge, IActivity, ProgressBadge, IconBadge } from '../../services/activity/common/activity.js'; import { IInstantiationService, ServicesAccessor } from '../../../platform/instantiation/common/instantiation.js'; import { DelayedDragHandler } from '../../../base/browser/dnd.js'; @@ -289,7 +289,7 @@ export class CompositeBarActionViewItem extends BaseActionViewItem { this.updateTitle(); } - private onThemeChange(theme: IColorTheme): void { + private onThemeChange(theme: IThemeChangeEvent): void { this.updateStyles(); } diff --git a/src/vs/workbench/browser/parts/dialogs/dialogHandler.ts b/src/vs/workbench/browser/parts/dialogs/dialogHandler.ts index 7563b30cd475..ac461ef94384 100644 --- a/src/vs/workbench/browser/parts/dialogs/dialogHandler.ts +++ b/src/vs/workbench/browser/parts/dialogs/dialogHandler.ts @@ -10,17 +10,14 @@ import { ILogService } from '../../../../platform/log/common/log.js'; import Severity from '../../../../base/common/severity.js'; import { Dialog, IDialogResult } from '../../../../base/browser/ui/dialog/dialog.js'; import { DisposableStore } from '../../../../base/common/lifecycle.js'; -import { StandardKeyboardEvent } from '../../../../base/browser/keyboardEvent.js'; -import { EventHelper } from '../../../../base/browser/dom.js'; import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; import { IProductService } from '../../../../platform/product/common/productService.js'; import { IClipboardService } from '../../../../platform/clipboard/common/clipboardService.js'; import { fromNow } from '../../../../base/common/date.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { MarkdownRenderer, openLinkFromMarkdown } from '../../../../editor/browser/widget/markdownRenderer/browser/markdownRenderer.js'; -import { defaultButtonStyles, defaultCheckboxStyles, defaultDialogStyles, defaultInputBoxStyles } from '../../../../platform/theme/browser/defaultStyles.js'; -import { ResultKind } from '../../../../platform/keybinding/common/keybindingResolver.js'; import { IOpenerService } from '../../../../platform/opener/common/opener.js'; +import { createWorkbenchDialogOptions } from '../../../../platform/dialogs/browser/dialog.js'; export class BrowserDialogHandler extends AbstractDialogHandler { @@ -33,18 +30,20 @@ export class BrowserDialogHandler extends AbstractDialogHandler { 'editor.action.clipboardPasteAction' ]; - private readonly markdownRenderer = this.instantiationService.createInstance(MarkdownRenderer, {}); + private readonly markdownRenderer: MarkdownRenderer; constructor( @ILogService private readonly logService: ILogService, @ILayoutService private readonly layoutService: ILayoutService, @IKeybindingService private readonly keybindingService: IKeybindingService, - @IInstantiationService private readonly instantiationService: IInstantiationService, + @IInstantiationService instantiationService: IInstantiationService, @IProductService private readonly productService: IProductService, @IClipboardService private readonly clipboardService: IClipboardService, @IOpenerService private readonly openerService: IOpenerService ) { super(); + + this.markdownRenderer = instantiationService.createInstance(MarkdownRenderer, {}); } async prompt(prompt: IPrompt): Promise> { @@ -115,7 +114,12 @@ export class BrowserDialogHandler extends AbstractDialogHandler { customOptions.markdownDetails?.forEach(markdownDetail => { const result = this.markdownRenderer.render(markdownDetail.markdown, { actionHandler: { - callback: link => openLinkFromMarkdown(this.openerService, link, markdownDetail.markdown.isTrusted, true /* skip URL validation to prevent another dialog from showing which is unsupported */), + callback: link => { + if (markdownDetail.dismissOnLinkClick) { + dialog.dispose(); + } + return openLinkFromMarkdown(this.openerService, link, markdownDetail.markdown.isTrusted, true /* skip URL validation to prevent another dialog from showing which is unsupported */); + }, disposables: dialogDisposables } }); @@ -129,30 +133,18 @@ export class BrowserDialogHandler extends AbstractDialogHandler { this.layoutService.activeContainer, message, buttons, - { + createWorkbenchDialogOptions({ detail, cancelId, type: this.getDialogType(type), - keyEventProcessor: (event: StandardKeyboardEvent) => { - const resolved = this.keybindingService.softDispatch(event, this.layoutService.activeContainer); - if (resolved.kind === ResultKind.KbFound && resolved.commandId) { - if (BrowserDialogHandler.ALLOWABLE_COMMANDS.indexOf(resolved.commandId) === -1) { - EventHelper.stop(event, true); - } - } - }, renderBody, icon: customOptions?.icon, disableCloseAction: customOptions?.disableCloseAction, buttonDetails: customOptions?.buttonDetails, checkboxLabel: checkbox?.label, checkboxChecked: checkbox?.checked, - inputs, - buttonStyles: defaultButtonStyles, - checkboxStyles: defaultCheckboxStyles, - inputBoxStyles: defaultInputBoxStyles, - dialogStyles: defaultDialogStyles - } + inputs + }, this.keybindingService, this.layoutService, BrowserDialogHandler.ALLOWABLE_COMMANDS) ); dialogDisposables.add(dialog); diff --git a/src/vs/workbench/browser/parts/editor/auxiliaryEditorPart.ts b/src/vs/workbench/browser/parts/editor/auxiliaryEditorPart.ts index 220bdf1cc65a..4a001a815ecd 100644 --- a/src/vs/workbench/browser/parts/editor/auxiliaryEditorPart.ts +++ b/src/vs/workbench/browser/parts/editor/auxiliaryEditorPart.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { onDidChangeFullscreen } from '../../../../base/browser/browser.js'; -import { hide, show } from '../../../../base/browser/dom.js'; +import { $, hide, show } from '../../../../base/browser/dom.js'; import { Emitter, Event } from '../../../../base/common/event.js'; import { DisposableStore } from '../../../../base/common/lifecycle.js'; import { isNative } from '../../../../base/common/platform.js'; @@ -105,9 +105,7 @@ export class AuxiliaryEditorPart { const auxiliaryWindow = disposables.add(await this.auxiliaryWindowService.open(options)); // Editor Part - const editorPartContainer = document.createElement('div'); - editorPartContainer.classList.add('part', 'editor'); - editorPartContainer.setAttribute('role', 'main'); + const editorPartContainer = $('.part.editor', { role: 'main' }); editorPartContainer.style.position = 'relative'; auxiliaryWindow.container.appendChild(editorPartContainer); @@ -315,7 +313,7 @@ class AuxiliaryEditorPartImpl extends EditorPart implements IAuxiliaryEditorPart const result = this.mergeAllGroups(targetGroup, { // Try to reduce the impact of closing the auxiliary window // as much as possible by not changing existing editors - // in the main window. + // in the main window. preserveExistingIndex: true }); targetGroup.focus(); diff --git a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts index 2792e624c1d6..351252cecda0 100644 --- a/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts +++ b/src/vs/workbench/browser/parts/editor/breadcrumbsControl.ts @@ -4,10 +4,10 @@ *--------------------------------------------------------------------------------------------*/ import * as dom from '../../../../base/browser/dom.js'; -import { $ } from '../../../../base/browser/dom.js'; import { StandardMouseEvent } from '../../../../base/browser/mouseEvent.js'; import { PixelRatio } from '../../../../base/browser/pixelRatio.js'; import { BreadcrumbsItem, BreadcrumbsWidget, IBreadcrumbsItemEvent, IBreadcrumbsWidgetStyles } from '../../../../base/browser/ui/breadcrumbs/breadcrumbsWidget.js'; +import { applyDragImage } from '../../../../base/browser/ui/dnd/dnd.js'; import { IHoverDelegate } from '../../../../base/browser/ui/hover/hoverDelegate.js'; import { getDefaultHoverDelegate } from '../../../../base/browser/ui/hover/hoverDelegateFactory.js'; import { timeout } from '../../../../base/common/async.js'; @@ -190,21 +190,7 @@ function createBreadcrumbDndObserver(accessor: ServicesAccessor, container: HTML } }); - // Create drag image and remove when dropped - const dragImage = $('.monaco-drag-image'); - dragImage.textContent = label; - - const getDragImageContainer = (e: HTMLElement | null) => { - while (e && !e.classList.contains('monaco-workbench')) { - e = e.parentElement; - } - return e || container.ownerDocument; - }; - - const dragContainer = getDragImageContainer(container); - dragContainer.appendChild(dragImage); - event.dataTransfer.setDragImage(dragImage, -10, -10); - setTimeout(() => dragImage.remove(), 0); + applyDragImage(event, container, label); } }); } diff --git a/src/vs/workbench/browser/parts/editor/editor.contribution.ts b/src/vs/workbench/browser/parts/editor/editor.contribution.ts index 1c777faacaf8..093b62cf6ecc 100644 --- a/src/vs/workbench/browser/parts/editor/editor.contribution.ts +++ b/src/vs/workbench/browser/parts/editor/editor.contribution.ts @@ -72,6 +72,8 @@ import { DynamicEditorConfigurations } from './editorConfiguration.js'; import { ConfigureEditorAction, ConfigureEditorTabsAction, EditorActionsDefaultAction, EditorActionsTitleBarAction, HideEditorActionsAction, HideEditorTabsAction, ShowMultipleEditorTabsAction, ShowSingleEditorTabAction, ZenHideEditorTabsAction, ZenShowMultipleEditorTabsAction, ZenShowSingleEditorTabAction } from '../../actions/layoutActions.js'; import { ICommandAction } from '../../../../platform/action/common/action.js'; import { EditorContextKeys } from '../../../../editor/common/editorContextKeys.js'; +import { getFontSnippets } from '../../../../base/browser/fonts.js'; +import { registerEditorFontConfigurations } from '../../../../editor/common/config/editorConfigurationSchema.js'; //#region Editor Registrations @@ -1102,3 +1104,6 @@ MenuRegistry.appendMenuItem(MenuId.MenubarGoMenu, { }); //#endregion + + +registerEditorFontConfigurations(getFontSnippets); diff --git a/src/vs/workbench/browser/parts/editor/editorActions.ts b/src/vs/workbench/browser/parts/editor/editorActions.ts index c922c06e8177..52d1dc5859cc 100644 --- a/src/vs/workbench/browser/parts/editor/editorActions.ts +++ b/src/vs/workbench/browser/parts/editor/editorActions.ts @@ -2493,7 +2493,7 @@ export class ReOpenInTextEditorAction extends Action2 { constructor() { super({ id: 'workbench.action.reopenTextEditor', - title: localize2('reopenTextEditor', 'Reopen Editor With Text Editor'), + title: localize2('reopenTextEditor', 'Reopen Editor with Text Editor'), f1: true, category: Categories.View, precondition: ActiveEditorAvailableEditorIdsContext diff --git a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts index 244ef9cb8906..9f0b51ba1c27 100644 --- a/src/vs/workbench/browser/parts/editor/editorDropTarget.ts +++ b/src/vs/workbench/browser/parts/editor/editorDropTarget.ts @@ -5,7 +5,7 @@ import './media/editordroptarget.css'; import { DataTransfers } from '../../../../base/browser/dnd.js'; -import { addDisposableListener, DragAndDropObserver, EventHelper, EventType, getWindow, isAncestor } from '../../../../base/browser/dom.js'; +import { $, addDisposableListener, DragAndDropObserver, EventHelper, EventType, getWindow, isAncestor } from '../../../../base/browser/dom.js'; import { renderFormattedText } from '../../../../base/browser/formattedTextRenderer.js'; import { RunOnceScheduler } from '../../../../base/common/async.js'; import { toDisposable } from '../../../../base/common/lifecycle.js'; @@ -84,8 +84,7 @@ class DropOverlay extends Themable { const overlayOffsetHeight = this.getOverlayOffsetHeight(); // Container - const container = this.container = document.createElement('div'); - container.id = DropOverlay.OVERLAY_ID; + const container = this.container = $('div', { id: DropOverlay.OVERLAY_ID }); container.style.top = `${overlayOffsetHeight}px`; // Parent @@ -97,8 +96,7 @@ class DropOverlay extends Themable { })); // Overlay - this.overlay = document.createElement('div'); - this.overlay.classList.add('editor-group-overlay-indicator'); + this.overlay = $('.editor-group-overlay-indicator'); container.appendChild(this.overlay); if (this.enableDropIntoEditor) { diff --git a/src/vs/workbench/browser/parts/editor/editorGroupView.ts b/src/vs/workbench/browser/parts/editor/editorGroupView.ts index 15012e7e7238..1a6788573ec3 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupView.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupView.ts @@ -11,7 +11,7 @@ import { EditorInput } from '../../../common/editor/editorInput.js'; import { SideBySideEditorInput } from '../../../common/editor/sideBySideEditorInput.js'; import { Emitter, Relay } from '../../../../base/common/event.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; -import { Dimension, trackFocus, addDisposableListener, EventType, EventHelper, findParentWithClass, isAncestor, IDomNodePagePosition, isMouseEvent, isActiveElement, getWindow, getActiveElement } from '../../../../base/browser/dom.js'; +import { Dimension, trackFocus, addDisposableListener, EventType, EventHelper, findParentWithClass, isAncestor, IDomNodePagePosition, isMouseEvent, isActiveElement, getWindow, getActiveElement, $ } from '../../../../base/browser/dom.js'; import { ServiceCollection } from '../../../../platform/instantiation/common/serviceCollection.js'; import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { ProgressBar } from '../../../../base/browser/ui/progressbar/progressbar.js'; @@ -208,16 +208,14 @@ export class EditorGroupView extends Themable implements IEditorGroupView { this.handleGroupContextKeys(); // Title container - this.titleContainer = document.createElement('div'); - this.titleContainer.classList.add('title'); + this.titleContainer = $('.title'); this.element.appendChild(this.titleContainer); // Title control this.titleControl = this._register(this.scopedInstantiationService.createInstance(EditorTitleControl, this.titleContainer, this.editorPartsView, this.groupsView, this, this.model)); // Editor container - this.editorContainer = document.createElement('div'); - this.editorContainer.classList.add('editor-container'); + this.editorContainer = $('.editor-container'); this.element.appendChild(this.editorContainer); // Editor pane @@ -406,8 +404,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { private createContainerToolbar(): void { // Toolbar Container - const toolbarContainer = document.createElement('div'); - toolbarContainer.classList.add('editor-group-container-toolbar'); + const toolbarContainer = $('.editor-group-container-toolbar'); this.element.appendChild(toolbarContainer); // Toolbar @@ -683,16 +680,6 @@ export class EditorGroupView extends Themable implements IEditorGroupView { } } - /* __GDPR__ - "editorClosed" : { - "owner": "isidorn", - "${include}": [ - "${EditorTelemetryDescriptor}" - ] - } - */ - this.telemetryService.publicLog('editorClosed', this.toEditorTelemetryDescriptor(editor)); - // Update container this.updateContainer(); @@ -2151,7 +2138,7 @@ export class EditorGroupView extends Themable implements IEditorGroupView { //#region ISerializableView - readonly element: HTMLElement = document.createElement('div'); + readonly element: HTMLElement = $('div'); get minimumWidth(): number { return this.editorPane.minimumWidth; } get minimumHeight(): number { return this.editorPane.minimumHeight; } diff --git a/src/vs/workbench/browser/parts/editor/editorGroupWatermark.ts b/src/vs/workbench/browser/parts/editor/editorGroupWatermark.ts index 0c7e37ccc097..2e6d68d514af 100644 --- a/src/vs/workbench/browser/parts/editor/editorGroupWatermark.ts +++ b/src/vs/workbench/browser/parts/editor/editorGroupWatermark.ts @@ -38,8 +38,10 @@ const findInFiles: WatermarkEntry = { text: localize('watermark.findInFiles', "F const toggleTerminal: WatermarkEntry = { text: localize({ key: 'watermark.toggleTerminal', comment: ['toggle is a verb here'] }, "Toggle Terminal"), id: 'workbench.action.terminal.toggleTerminal', when: { web: ContextKeyExpr.equals('terminalProcessSupported', true) } }; const startDebugging: WatermarkEntry = { text: localize('watermark.startDebugging', "Start Debugging"), id: 'workbench.action.debug.start', when: { web: ContextKeyExpr.equals('terminalProcessSupported', true) } }; const openSettings: WatermarkEntry = { text: localize('watermark.openSettings', "Open Settings"), id: 'workbench.action.openSettings' }; -const openChat: WatermarkEntry = { text: localize('watermark.openChat', "Open Chat"), id: 'workbench.action.chat.open', when: { native: ContextKeyExpr.equals('chatSetupInstalled', true), web: ContextKeyExpr.equals('chatSetupInstalled', true) } }; -const openCopilotEdits: WatermarkEntry = { text: localize('watermark.openCopilotEdits', "Open Copilot Edits"), id: 'workbench.action.chat.openEditSession', when: { native: ContextKeyExpr.equals('chatSetupInstalled', true), web: ContextKeyExpr.equals('chatSetupInstalled', true) } }; + +const showCopilot = ContextKeyExpr.or(ContextKeyExpr.equals('chatSetupHidden', false), ContextKeyExpr.equals('chatSetupInstalled', true)); +const openChat: WatermarkEntry = { text: localize('watermark.openChat', "Open Chat"), id: 'workbench.action.chat.open', when: { native: showCopilot, web: showCopilot } }; +const openCopilotEdits: WatermarkEntry = { text: localize('watermark.openCopilotEdits', "Open Copilot Edits"), id: 'workbench.action.chat.openEditSession', when: { native: showCopilot, web: showCopilot } }; const emptyWindowEntries: WatermarkEntry[] = coalesce([ showCommands, @@ -71,14 +73,14 @@ export class EditorGroupWatermark extends Disposable { private static readonly CACHED_WHEN = 'editorGroupWatermark.whenConditions'; - private readonly cachedWhen: { [when: string]: boolean } = this.storageService.getObject(EditorGroupWatermark.CACHED_WHEN, StorageScope.PROFILE, Object.create(null)); + private readonly cachedWhen: { [when: string]: boolean }; private readonly shortcuts: HTMLElement; private readonly transientDisposables = this._register(new DisposableStore()); private readonly keybindingLabels = this._register(new DisposableStore()); private enabled = false; - private workbenchState = this.contextService.getWorkbenchState(); + private workbenchState: WorkbenchState; constructor( container: HTMLElement, @@ -90,6 +92,9 @@ export class EditorGroupWatermark extends Disposable { ) { super(); + this.cachedWhen = this.storageService.getObject(EditorGroupWatermark.CACHED_WHEN, StorageScope.PROFILE, Object.create(null)); + this.workbenchState = this.contextService.getWorkbenchState(); + const elements = h('.editor-group-watermark', [ h('.letterpress'), h('.shortcuts@shortcuts'), diff --git a/src/vs/workbench/browser/parts/editor/editorPanes.ts b/src/vs/workbench/browser/parts/editor/editorPanes.ts index 68b73d9a5888..af5df2dd0b07 100644 --- a/src/vs/workbench/browser/parts/editor/editorPanes.ts +++ b/src/vs/workbench/browser/parts/editor/editorPanes.ts @@ -10,7 +10,7 @@ import Severity from '../../../../base/common/severity.js'; import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.js'; import { EditorExtensions, EditorInputCapabilities, IEditorOpenContext, IVisibleEditorPane, isEditorOpenError } from '../../../common/editor.js'; import { EditorInput } from '../../../common/editor/editorInput.js'; -import { Dimension, show, hide, IDomNodePagePosition, isAncestor, getActiveElement, getWindowById, isEditableElement } from '../../../../base/browser/dom.js'; +import { Dimension, show, hide, IDomNodePagePosition, isAncestor, getActiveElement, getWindowById, isEditableElement, $ } from '../../../../base/browser/dom.js'; import { Registry } from '../../../../platform/registry/common/platform.js'; import { IEditorPaneRegistry, IEditorPaneDescriptor } from '../../editor.js'; import { IWorkbenchLayoutService } from '../../../services/layout/browser/layoutService.js'; @@ -87,9 +87,11 @@ export class EditorPanes extends Disposable { private readonly mapEditorPaneToPendingSetInput = new Map>(); private readonly activeEditorPaneDisposables = this._register(new DisposableStore()); + private pagePosition: IDomNodePagePosition | undefined; private boundarySashes: IBoundarySashes | undefined; - private readonly editorOperation = this._register(new LongRunningOperation(this.editorProgressService)); + + private readonly editorOperation: LongRunningOperation; private readonly editorPanesRegistry = Registry.as(EditorExtensions.EditorPane); constructor( @@ -98,7 +100,7 @@ export class EditorPanes extends Disposable { private readonly groupView: IEditorGroupView, @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService, @IInstantiationService private readonly instantiationService: IInstantiationService, - @IEditorProgressService private readonly editorProgressService: IEditorProgressService, + @IEditorProgressService editorProgressService: IEditorProgressService, @IWorkspaceTrustManagementService private readonly workspaceTrustService: IWorkspaceTrustManagementService, @ILogService private readonly logService: ILogService, @IDialogService private readonly dialogService: IDialogService, @@ -106,6 +108,8 @@ export class EditorPanes extends Disposable { ) { super(); + this.editorOperation = this._register(new LongRunningOperation(editorProgressService)); + this.registerListeners(); } @@ -360,15 +364,28 @@ export class EditorPanes extends Disposable { // Create editor container as needed if (!editorPane.getContainer()) { - const editorPaneContainer = document.createElement('div'); - editorPaneContainer.classList.add('editor-instance'); + const editorPaneContainer = $('.editor-instance'); // It is cruicial to append the container to its parent before // passing on to the create() method of the pane so that the // right `window` can be determined in floating window cases. this.editorPanesParent.appendChild(editorPaneContainer); - editorPane.create(editorPaneContainer); + try { + editorPane.create(editorPaneContainer); + } catch (error) { + + // At this point the editor pane container is not healthy + // and as such, we remove it from the pane parent and hide + // it so that we have a chance to show an error placeholder. + // Not doing so would result in multiple `.editor-instance` + // lingering around in the DOM. + + editorPaneContainer.remove(); + hide(editorPaneContainer); + + throw error; + } } return editorPane; diff --git a/src/vs/workbench/browser/parts/editor/editorPart.ts b/src/vs/workbench/browser/parts/editor/editorPart.ts index 90254ef45be5..c3a6d7eb29ce 100644 --- a/src/vs/workbench/browser/parts/editor/editorPart.ts +++ b/src/vs/workbench/browser/parts/editor/editorPart.ts @@ -170,6 +170,8 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupsView { ) { super(id, { hasTitle: false }, themeService, storageService, layoutService); + this._partOptions = getEditorPartOptions(this.configurationService, this.themeService); + this.registerListeners(); } @@ -200,7 +202,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupsView { private enforcedPartOptions: DeepPartial[] = []; - private _partOptions = getEditorPartOptions(this.configurationService, this.themeService); + private _partOptions: IEditorPartOptions; get partOptions(): IEditorPartOptions { return this._partOptions; } enforcePartOptions(options: DeepPartial): IDisposable { @@ -993,8 +995,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupsView { // Container this.element = parent; - this.container = document.createElement('div'); - this.container.classList.add('content'); + this.container = $('.content'); if (this.windowId !== mainWindow.vscodeWindowId) { this.container.classList.add('auxiliary'); } @@ -1067,8 +1068,7 @@ export class EditorPart extends Part implements IEditorPart, IEditorGroupsView { this._register(this.createEditorDropTarget(container, Object.create(null))); // No drop in the editor - const overlay = document.createElement('div'); - overlay.classList.add('drop-block-overlay'); + const overlay = $('.drop-block-overlay'); parent.appendChild(overlay); // Hide the block if a mouse down event occurs #99065 diff --git a/src/vs/workbench/browser/parts/editor/editorParts.ts b/src/vs/workbench/browser/parts/editor/editorParts.ts index 5d8dd28ea474..14daf210d0c4 100644 --- a/src/vs/workbench/browser/parts/editor/editorParts.ts +++ b/src/vs/workbench/browser/parts/editor/editorParts.ts @@ -44,9 +44,9 @@ export class EditorParts extends MultiWindowParts implements IEditor declare readonly _serviceBrand: undefined; - readonly mainPart = this._register(this.createMainEditorPart()); + readonly mainPart: MainEditorPart; - private mostRecentActiveParts = [this.mainPart]; + private mostRecentActiveParts: MainEditorPart[]; constructor( @IInstantiationService protected readonly instantiationService: IInstantiationService, @@ -57,8 +57,11 @@ export class EditorParts extends MultiWindowParts implements IEditor ) { super('workbench.editorParts', themeService, storageService); + this.mainPart = this._register(this.createMainEditorPart()); this._register(this.registerPart(this.mainPart)); + this.mostRecentActiveParts = [this.mainPart]; + this.restoreParts(); this.registerListeners(); } diff --git a/src/vs/workbench/browser/parts/editor/editorPlaceholder.ts b/src/vs/workbench/browser/parts/editor/editorPlaceholder.ts index 15d87f9ad15d..4b3ef8b4dc7d 100644 --- a/src/vs/workbench/browser/parts/editor/editorPlaceholder.ts +++ b/src/vs/workbench/browser/parts/editor/editorPlaceholder.ts @@ -67,10 +67,10 @@ export abstract class EditorPlaceholder extends EditorPane { protected createEditor(parent: HTMLElement): void { // Container - this.container = document.createElement('div'); - this.container.className = 'monaco-editor-pane-placeholder'; + this.container = $('.monaco-editor-pane-placeholder', { + tabIndex: 0 // enable focus support from the editor part (do not remove) + }); this.container.style.outline = 'none'; - this.container.tabIndex = 0; // enable focus support from the editor part (do not remove) // Custom Scrollbars this.scrollbar = this._register(new DomScrollableElement(this.container, { horizontal: ScrollbarVisibility.Auto, vertical: ScrollbarVisibility.Auto })); @@ -107,7 +107,7 @@ export abstract class EditorPlaceholder extends EditorPane { // Label const labelContainer = container.appendChild($('.editor-placeholder-label-container')); - const labelWidget = document.createElement('span'); + const labelWidget = $('span'); labelWidget.textContent = truncatedLabel; labelContainer.appendChild(labelWidget); diff --git a/src/vs/workbench/browser/parts/editor/editorStatus.ts b/src/vs/workbench/browser/parts/editor/editorStatus.ts index a1b9bd5215c2..52c6462f7618 100644 --- a/src/vs/workbench/browser/parts/editor/editorStatus.ts +++ b/src/vs/workbench/browser/parts/editor/editorStatus.ts @@ -353,9 +353,9 @@ class EditorStatus extends Disposable { private readonly languageElement = this._register(new MutableDisposable()); private readonly metadataElement = this._register(new MutableDisposable()); - private readonly currentMarkerStatus = this._register(this.instantiationService.createInstance(ShowCurrentMarkerInStatusbarContribution)); - private readonly tabFocusMode = this._register(this.instantiationService.createInstance(TabFocusMode)); - private readonly inputMode = this._register(this.instantiationService.createInstance(StatusInputMode)); + private readonly currentMarkerStatus: ShowCurrentMarkerInStatusbarContribution; + private readonly tabFocusMode: TabFocusMode; + private readonly inputMode: StatusInputMode; private readonly state = new State(); private toRender: StateChange | undefined = undefined; @@ -370,11 +370,15 @@ class EditorStatus extends Disposable { @ILanguageService private readonly languageService: ILanguageService, @ITextFileService private readonly textFileService: ITextFileService, @IStatusbarService private readonly statusbarService: IStatusbarService, - @IInstantiationService private readonly instantiationService: IInstantiationService, + @IInstantiationService instantiationService: IInstantiationService, @IConfigurationService private readonly configurationService: IConfigurationService ) { super(); + this.currentMarkerStatus = this._register(instantiationService.createInstance(ShowCurrentMarkerInStatusbarContribution)); + this.tabFocusMode = this._register(instantiationService.createInstance(TabFocusMode)); + this.inputMode = this._register(instantiationService.createInstance(StatusInputMode)); + this.registerCommands(); this.registerListeners(); } @@ -994,9 +998,10 @@ class ShowCurrentMarkerInStatusbarContribution extends Disposable { const line = splitLines(this.currentMarker.message)[0]; const text = `${this.getType(this.currentMarker)} ${line}`; if (!this.statusBarEntryAccessor.value) { - this.statusBarEntryAccessor.value = this.statusbarService.addEntry({ name: localize('currentProblem', "Current Problem"), text: '', ariaLabel: '' }, 'statusbar.currentProblem', StatusbarAlignment.LEFT); + this.statusBarEntryAccessor.value = this.statusbarService.addEntry({ name: localize('currentProblem', "Current Problem"), text, ariaLabel: text }, 'statusbar.currentProblem', StatusbarAlignment.LEFT); + } else { + this.statusBarEntryAccessor.value.update({ name: localize('currentProblem', "Current Problem"), text, ariaLabel: text }); } - this.statusBarEntryAccessor.value.update({ name: localize('currentProblem', "Current Problem"), text, ariaLabel: text }); } else { this.statusBarEntryAccessor.clear(); } diff --git a/src/vs/workbench/browser/parts/editor/editorTabsControl.ts b/src/vs/workbench/browser/parts/editor/editorTabsControl.ts index 7306519d4f79..4d4573776a73 100644 --- a/src/vs/workbench/browser/parts/editor/editorTabsControl.ts +++ b/src/vs/workbench/browser/parts/editor/editorTabsControl.ts @@ -5,8 +5,8 @@ import './media/editortabscontrol.css'; import { localize } from '../../../../nls.js'; -import { applyDragImage, DataTransfers } from '../../../../base/browser/dnd.js'; -import { Dimension, getActiveWindow, getWindow, isMouseEvent } from '../../../../base/browser/dom.js'; +import { DataTransfers } from '../../../../base/browser/dnd.js'; +import { $, Dimension, getActiveWindow, getWindow, isMouseEvent } from '../../../../base/browser/dom.js'; import { StandardMouseEvent } from '../../../../base/browser/mouseEvent.js'; import { ActionsOrientation, IActionViewItem, prepareActions } from '../../../../base/browser/ui/actionbar/actionbar.js'; import { IAction, ActionRunner } from '../../../../base/common/actions.js'; @@ -20,7 +20,6 @@ import { IInstantiationService } from '../../../../platform/instantiation/common import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; import { INotificationService } from '../../../../platform/notification/common/notification.js'; import { IQuickInputService } from '../../../../platform/quickinput/common/quickInput.js'; -import { listActiveSelectionBackground, listActiveSelectionForeground } from '../../../../platform/theme/common/colorRegistry.js'; import { IThemeService, Themable } from '../../../../platform/theme/common/themeService.js'; import { DraggedEditorGroupIdentifier, DraggedEditorIdentifier, fillEditorsDragData, isWindowDraggedOver } from '../../dnd.js'; import { EditorPane } from './editorPane.js'; @@ -47,6 +46,7 @@ import { ServiceCollection } from '../../../../platform/instantiation/common/ser import { IBaseActionViewItemOptions } from '../../../../base/browser/ui/actionbar/actionViewItems.js'; import { MarkdownString } from '../../../../base/common/htmlContent.js'; import { IManagedHoverTooltipMarkdownString } from '../../../../base/browser/ui/hover/hover.js'; +import { applyDragImage } from '../../../../base/browser/ui/dnd/dnd.js'; export class EditorCommandsContextActionRunner extends ActionRunner { @@ -177,7 +177,7 @@ export abstract class EditorTabsControl extends Themable implements IEditorTabsC } protected createEditorActionsToolBar(parent: HTMLElement, classes: string[]): void { - this.editorActionsToolbarContainer = document.createElement('div'); + this.editorActionsToolbarContainer = $('div'); this.editorActionsToolbarContainer.classList.add(...classes); parent.appendChild(this.editorActionsToolbarContainer); @@ -317,7 +317,7 @@ export abstract class EditorTabsControl extends Themable implements IEditorTabsC label = localize('draggedEditorGroup', "{0} (+{1})", label, this.groupView.count - 1); } - applyDragImage(e, label, 'monaco-editor-group-drag-image', this.getColor(listActiveSelectionBackground), this.getColor(listActiveSelectionForeground)); + applyDragImage(e, element, label); } return isNewWindowOperation; diff --git a/src/vs/workbench/browser/parts/editor/editorTitleControl.ts b/src/vs/workbench/browser/parts/editor/editorTitleControl.ts index 65ef9fca411f..00f7bf67590d 100644 --- a/src/vs/workbench/browser/parts/editor/editorTitleControl.ts +++ b/src/vs/workbench/browser/parts/editor/editorTitleControl.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import './media/editortitlecontrol.css'; -import { Dimension, clearNode } from '../../../../base/browser/dom.js'; +import { $, Dimension, clearNode } from '../../../../base/browser/dom.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { IThemeService, Themable } from '../../../../platform/theme/common/themeService.js'; import { BreadcrumbsControl, BreadcrumbsControlFactory } from './breadcrumbsControl.js'; @@ -82,8 +82,7 @@ export class EditorTitleControl extends Themable { } // Breadcrumbs container - const breadcrumbsContainer = document.createElement('div'); - breadcrumbsContainer.classList.add('breadcrumbs-below-tabs'); + const breadcrumbsContainer = $('.breadcrumbs-below-tabs'); this.parent.appendChild(breadcrumbsContainer); const breadcrumbsControlFactory = this.breadcrumbsControlDisposables.add(this.instantiationService.createInstance(BreadcrumbsControlFactory, breadcrumbsContainer, this.groupView, { diff --git a/src/vs/workbench/browser/parts/editor/media/editortabscontrol.css b/src/vs/workbench/browser/parts/editor/media/editortabscontrol.css index aa684a520bb4..37b90640ac59 100644 --- a/src/vs/workbench/browser/parts/editor/media/editortabscontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/editortabscontrol.css @@ -42,21 +42,3 @@ .monaco-workbench .part.editor > .content .editor-group-container > .title.tabs .monaco-icon-label::after { margin-right: 0; /* by default the icon label has a padding right and this isn't wanted when not showing tabs and not showing breadcrumbs */ } - -/* Drag and Drop */ - -.monaco-editor-group-drag-image { - display: inline-block; - padding: 1px 7px; - border-radius: 10px; - font-size: 12px; - position: absolute; - /* - * Browsers apply an effect to the drag image when the div becomes too - * large which makes them unreadable. Use max width so it does not happen - */ - max-width: 120px; - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; -} diff --git a/src/vs/workbench/browser/parts/editor/media/multieditortabscontrol.css b/src/vs/workbench/browser/parts/editor/media/multieditortabscontrol.css index 05e14022e489..31c863e9901f 100644 --- a/src/vs/workbench/browser/parts/editor/media/multieditortabscontrol.css +++ b/src/vs/workbench/browser/parts/editor/media/multieditortabscontrol.css @@ -345,7 +345,7 @@ .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink > .tab-label.tab-label-has-badge::after, .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fixed > .tab-label.tab-label-has-badge::after { - padding-right: 5px; /* with tab sizing shrink/fixed and badges, we want a right-padding because the close button is hidden */ + margin-right: 5px; /* with tab sizing shrink/fixed and badges, we want a right-margin because the close button is hidden. Use margin instead of padding to support animating the badge (https://github.com/microsoft/vscode/issues/242661) */ } .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink:not(.tab-actions-left):not(.close-action-off) .tab-label { @@ -359,6 +359,12 @@ .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fit .monaco-icon-label, .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-fit .monaco-icon-label > .monaco-icon-label-container { overflow-x: visible; /* fixes https://github.com/microsoft/vscode/issues/20182 by ensuring the horizontal overflow is visible */ + + scrollbar-width: none; /* Firefox */ + -ms-overflow-style: none; /* Internet Explorer 10+ */ + &::-webkit-scrollbar { + display: none; /* Chrome, Safari, and Opera */ + } } .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab.sizing-shrink > .monaco-icon-label > .monaco-icon-label-container, @@ -374,6 +380,18 @@ text-overflow: ellipsis; } +.monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab > .monaco-icon-label.italic > .monaco-icon-label-container { + /** + * Browser rendering engines seem to have trouble to render italic labels + * fully without clipping in case the parent container has `overflow: hidden` + * and/or `flex: none`. We added those styles to fix other issues so a pragmatic + * solution is to give the label container a bit more room to fully render. + * + * Refs: https://github.com/microsoft/vscode/issues/207409 + */ + padding-right: 1px; +} + /* Tab Actions */ .monaco-workbench .part.editor > .content .editor-group-container > .title .tabs-container > .tab > .tab-actions { diff --git a/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts b/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts index 708f4d9f6d56..04f6aefec26f 100644 --- a/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts +++ b/src/vs/workbench/browser/parts/editor/multiEditorTabsControl.ts @@ -27,12 +27,12 @@ import { ScrollbarVisibility } from '../../../../base/common/scrollable.js'; import { getOrSet } from '../../../../base/common/map.js'; import { IThemeService, registerThemingParticipant } from '../../../../platform/theme/common/themeService.js'; import { TAB_INACTIVE_BACKGROUND, TAB_ACTIVE_BACKGROUND, TAB_BORDER, EDITOR_DRAG_AND_DROP_BACKGROUND, TAB_UNFOCUSED_ACTIVE_BACKGROUND, TAB_UNFOCUSED_ACTIVE_BORDER, TAB_ACTIVE_BORDER, TAB_HOVER_BACKGROUND, TAB_HOVER_BORDER, TAB_UNFOCUSED_HOVER_BACKGROUND, TAB_UNFOCUSED_HOVER_BORDER, EDITOR_GROUP_HEADER_TABS_BACKGROUND, WORKBENCH_BACKGROUND, TAB_ACTIVE_BORDER_TOP, TAB_UNFOCUSED_ACTIVE_BORDER_TOP, TAB_ACTIVE_MODIFIED_BORDER, TAB_INACTIVE_MODIFIED_BORDER, TAB_UNFOCUSED_ACTIVE_MODIFIED_BORDER, TAB_UNFOCUSED_INACTIVE_MODIFIED_BORDER, TAB_UNFOCUSED_INACTIVE_BACKGROUND, TAB_HOVER_FOREGROUND, TAB_UNFOCUSED_HOVER_FOREGROUND, EDITOR_GROUP_HEADER_TABS_BORDER, TAB_LAST_PINNED_BORDER, TAB_SELECTED_BORDER_TOP } from '../../../common/theme.js'; -import { activeContrastBorder, contrastBorder, editorBackground, listActiveSelectionBackground, listActiveSelectionForeground } from '../../../../platform/theme/common/colorRegistry.js'; +import { activeContrastBorder, contrastBorder, editorBackground } from '../../../../platform/theme/common/colorRegistry.js'; import { ResourcesDropHandler, DraggedEditorIdentifier, DraggedEditorGroupIdentifier, extractTreeDropData, isWindowDraggedOver } from '../../dnd.js'; import { Color } from '../../../../base/common/color.js'; import { INotificationService } from '../../../../platform/notification/common/notification.js'; import { MergeGroupMode, IMergeGroupOptions } from '../../../services/editor/common/editorGroupsService.js'; -import { addDisposableListener, EventType, EventHelper, Dimension, scheduleAtNextAnimationFrame, findParentWithClass, clearNode, DragAndDropObserver, isMouseEvent, getWindow } from '../../../../base/browser/dom.js'; +import { addDisposableListener, EventType, EventHelper, Dimension, scheduleAtNextAnimationFrame, findParentWithClass, clearNode, DragAndDropObserver, isMouseEvent, getWindow, $ } from '../../../../base/browser/dom.js'; import { localize } from '../../../../nls.js'; import { IEditorGroupsView, EditorServiceImpl, IEditorGroupView, IInternalEditorOpenOptions, IEditorPartsView, prepareMoveCopyEditors } from './editor.js'; import { CloseEditorTabAction, UnpinEditorAction } from './editorActions.js'; @@ -57,7 +57,7 @@ import { StickyEditorGroupModel, UnstickyEditorGroupModel } from '../../../commo import { IReadonlyEditorGroupModel } from '../../../common/editor/editorGroupModel.js'; import { IHostService } from '../../../services/host/browser/host.js'; import { BugIndicatingError } from '../../../../base/common/errors.js'; -import { applyDragImage } from '../../../../base/browser/dnd.js'; +import { applyDragImage } from '../../../../base/browser/ui/dnd/dnd.js'; interface IEditorInputLabel { readonly editor: EditorInput; @@ -170,15 +170,14 @@ export class MultiEditorTabsControl extends EditorTabsControl { this.titleContainer = parent; // Tabs and Actions Container (are on a single row with flex side-by-side) - this.tabsAndActionsContainer = document.createElement('div'); - this.tabsAndActionsContainer.classList.add('tabs-and-actions-container'); + this.tabsAndActionsContainer = $('.tabs-and-actions-container'); this.titleContainer.appendChild(this.tabsAndActionsContainer); // Tabs Container - this.tabsContainer = document.createElement('div'); - this.tabsContainer.setAttribute('role', 'tablist'); - this.tabsContainer.draggable = true; - this.tabsContainer.classList.add('tabs-container'); + this.tabsContainer = $('.tabs-container', { + role: 'tablist', + draggable: true + }); this._register(Gesture.addTarget(this.tabsContainer)); this.tabSizingFixedDisposables = this._register(new DisposableStore()); @@ -803,25 +802,23 @@ export class MultiEditorTabsControl extends EditorTabsControl { private createTab(tabIndex: number, tabsContainer: HTMLElement, tabsScrollbar: ScrollableElement): HTMLElement { // Tab Container - const tabContainer = document.createElement('div'); - tabContainer.draggable = true; - tabContainer.setAttribute('role', 'tab'); - tabContainer.classList.add('tab'); + const tabContainer = $('.tab', { + draggable: true, + role: 'tab' + }); // Gesture Support this._register(Gesture.addTarget(tabContainer)); // Tab Border Top - const tabBorderTopContainer = document.createElement('div'); - tabBorderTopContainer.classList.add('tab-border-top-container'); + const tabBorderTopContainer = $('.tab-border-top-container'); tabContainer.appendChild(tabBorderTopContainer); // Tab Editor Label const editorLabel = this.tabResourceLabels.create(tabContainer, { hoverTargetOverride: tabContainer }); // Tab Actions - const tabActionsContainer = document.createElement('div'); - tabActionsContainer.classList.add('tab-actions'); + const tabActionsContainer = $('.tab-actions'); tabContainer.appendChild(tabActionsContainer); const that = this; @@ -837,17 +834,15 @@ export class MultiEditorTabsControl extends EditorTabsControl { } }); - const tabActionBarDisposable = combinedDisposable(tabActionBar, tabActionListener, toDisposable(insert(this.tabActionBars, tabActionBar))); + const tabActionBarDisposable = combinedDisposable(tabActionRunner, tabActionBar, tabActionListener, toDisposable(insert(this.tabActionBars, tabActionBar))); // Tab Fade Hider // Hides the tab fade to the right when tab action left and sizing shrink/fixed, ::after, ::before are already used - const tabShadowHider = document.createElement('div'); - tabShadowHider.classList.add('tab-fade-hider'); + const tabShadowHider = $('.tab-fade-hider'); tabContainer.appendChild(tabShadowHider); // Tab Border Bottom - const tabBorderBottomContainer = document.createElement('div'); - tabBorderBottomContainer.classList.add('tab-border-bottom-container'); + const tabBorderBottomContainer = $('.tab-border-bottom-container'); tabContainer.appendChild(tabBorderBottomContainer); // Eventing @@ -1088,7 +1083,7 @@ export class MultiEditorTabsControl extends EditorTabsControl { e.dataTransfer.effectAllowed = 'copyMove'; if (selectedEditors.length > 1) { const label = `${editor.getName()} + ${selectedEditors.length - 1}`; - applyDragImage(e, label, 'monaco-editor-group-drag-image', this.getColor(listActiveSelectionBackground), this.getColor(listActiveSelectionForeground)); + applyDragImage(e, tab, label); } else { e.dataTransfer.setDragImage(tab, 0, 0); // top left corner of dragged tab set to cursor position to make room for drop-border feedback } diff --git a/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts b/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts index b373fd8b0518..691a716cba62 100644 --- a/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts +++ b/src/vs/workbench/browser/parts/editor/sideBySideEditor.ts @@ -116,7 +116,7 @@ export class SideBySideEditor extends AbstractEditorWithViewState(SideBySideEditor.SIDE_BY_SIDE_LAYOUT_SETTING) === 'vertical' ? Orientation.VERTICAL : Orientation.HORIZONTAL; + private orientation: Orientation; private dimension = new Dimension(0, 0); private lastFocusedSide: Side.PRIMARY | Side.SECONDARY | undefined = undefined; @@ -134,6 +134,8 @@ export class SideBySideEditor extends AbstractEditorWithViewState(SideBySideEditor.SIDE_BY_SIDE_LAYOUT_SETTING) === 'vertical' ? Orientation.VERTICAL : Orientation.HORIZONTAL; + this.registerListeners(); } diff --git a/src/vs/workbench/browser/parts/editor/singleEditorTabsControl.ts b/src/vs/workbench/browser/parts/editor/singleEditorTabsControl.ts index a3b16b2d7fcc..d8620a08abd8 100644 --- a/src/vs/workbench/browser/parts/editor/singleEditorTabsControl.ts +++ b/src/vs/workbench/browser/parts/editor/singleEditorTabsControl.ts @@ -10,7 +10,7 @@ import { EditorTabsControl } from './editorTabsControl.js'; import { ResourceLabel, IResourceLabel } from '../../labels.js'; import { TAB_ACTIVE_FOREGROUND, TAB_UNFOCUSED_ACTIVE_FOREGROUND } from '../../../common/theme.js'; import { EventType as TouchEventType, GestureEvent, Gesture } from '../../../../base/browser/touch.js'; -import { addDisposableListener, EventType, EventHelper, Dimension, isAncestor, DragAndDropObserver, isHTMLElement } from '../../../../base/browser/dom.js'; +import { addDisposableListener, EventType, EventHelper, Dimension, isAncestor, DragAndDropObserver, isHTMLElement, $ } from '../../../../base/browser/dom.js'; import { CLOSE_EDITOR_COMMAND_ID, UNLOCK_GROUP_COMMAND_ID } from './editorCommands.js'; import { Color } from '../../../../base/common/color.js'; import { assertIsDefined, assertAllDefined } from '../../../../base/common/types.js'; @@ -46,8 +46,7 @@ export class SingleEditorTabsControl extends EditorTabsControl { // Gesture Support this._register(Gesture.addTarget(titleContainer)); - const labelContainer = document.createElement('div'); - labelContainer.classList.add('label-container'); + const labelContainer = $('.label-container'); titleContainer.appendChild(labelContainer); // Editor Label diff --git a/src/vs/workbench/browser/parts/globalCompositeBar.ts b/src/vs/workbench/browser/parts/globalCompositeBar.ts index c026e1581721..e927fbca8e3e 100644 --- a/src/vs/workbench/browser/parts/globalCompositeBar.ts +++ b/src/vs/workbench/browser/parts/globalCompositeBar.ts @@ -67,7 +67,7 @@ export class GlobalCompositeBar extends Disposable { ) { super(); - this.element = document.createElement('div'); + this.element = $('div'); const contextMenuAlignmentOptions = () => ({ anchorAlignment: configurationService.getValue('workbench.sideBar.location') === 'left' ? AnchorAlignment.RIGHT : AnchorAlignment.LEFT, anchorAxisAlignment: AnchorAxisAlignment.HORIZONTAL diff --git a/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts b/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts index 85356ca85d9d..103e37a3019c 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsCenter.ts @@ -14,7 +14,7 @@ import { IContextKeyService } from '../../../../platform/contextkey/common/conte import { INotificationsCenterController, NotificationActionRunner } from './notificationsCommands.js'; import { NotificationsList } from './notificationsList.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; -import { Dimension, isAncestorOfActiveElement } from '../../../../base/browser/dom.js'; +import { $, Dimension, isAncestorOfActiveElement } from '../../../../base/browser/dom.js'; import { widgetShadow } from '../../../../platform/theme/common/colorRegistry.js'; import { IEditorGroupsService } from '../../../services/editor/common/editorGroupsService.js'; import { localize } from '../../../../nls.js'; @@ -45,7 +45,7 @@ export class NotificationsCenter extends Themable implements INotificationsCente private notificationsList: NotificationsList | undefined; private _isVisible: boolean | undefined; private workbenchDimensions: Dimension | undefined; - private readonly notificationsCenterVisibleContextKey = NotificationsCenterVisibleContext.bindTo(this.contextKeyService); + private readonly notificationsCenterVisibleContextKey; private clearAllAction: ClearAllNotificationsAction | undefined; private configureDoNotDisturbAction: ConfigureDoNotDisturbAction | undefined; @@ -55,7 +55,7 @@ export class NotificationsCenter extends Themable implements INotificationsCente @IThemeService themeService: IThemeService, @IInstantiationService private readonly instantiationService: IInstantiationService, @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService, - @IContextKeyService private readonly contextKeyService: IContextKeyService, + @IContextKeyService contextKeyService: IContextKeyService, @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService, @IKeybindingService private readonly keybindingService: IKeybindingService, @INotificationService private readonly notificationService: INotificationService, @@ -149,22 +149,18 @@ export class NotificationsCenter extends Themable implements INotificationsCente private create(): void { // Container - this.notificationsCenterContainer = document.createElement('div'); - this.notificationsCenterContainer.classList.add('notifications-center'); + this.notificationsCenterContainer = $('.notifications-center'); // Header - this.notificationsCenterHeader = document.createElement('div'); - this.notificationsCenterHeader.classList.add('notifications-center-header'); + this.notificationsCenterHeader = $('.notifications-center-header'); this.notificationsCenterContainer.appendChild(this.notificationsCenterHeader); // Header Title - this.notificationsCenterTitle = document.createElement('span'); - this.notificationsCenterTitle.classList.add('notifications-center-header-title'); + this.notificationsCenterTitle = $('span.notifications-center-header-title'); this.notificationsCenterHeader.appendChild(this.notificationsCenterTitle); // Header Toolbar - const toolbarContainer = document.createElement('div'); - toolbarContainer.classList.add('notifications-center-header-toolbar'); + const toolbarContainer = $('.notifications-center-header-toolbar'); this.notificationsCenterHeader.appendChild(toolbarContainer); const actionRunner = this._register(this.instantiationService.createInstance(NotificationActionRunner)); diff --git a/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts b/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts index 04980171865f..d3fff55ef7c1 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsCommands.ts @@ -169,6 +169,7 @@ export function registerNotificationCommands(center: INotificationsCenterControl } actionRunner.run(primaryAction, notification); notification.close(); + actionRunner.dispose(); } }); diff --git a/src/vs/workbench/browser/parts/notifications/notificationsList.ts b/src/vs/workbench/browser/parts/notifications/notificationsList.ts index d0893c26d1d7..edb6062b7175 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsList.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsList.ts @@ -5,7 +5,7 @@ import './media/notificationsList.css'; import { localize } from '../../../../nls.js'; -import { getWindow, isAncestorOfActiveElement, trackFocus } from '../../../../base/browser/dom.js'; +import { $, getWindow, isAncestorOfActiveElement, trackFocus } from '../../../../base/browser/dom.js'; import { WorkbenchList } from '../../../../platform/list/browser/listService.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { IListAccessibilityProvider, IListOptions } from '../../../../base/browser/ui/list/listWidget.js'; @@ -60,8 +60,7 @@ export class NotificationsList extends Disposable { private createNotificationsList(): void { // List Container - this.listContainer = document.createElement('div'); - this.listContainer.classList.add('notifications-list-container'); + this.listContainer = $('.notifications-list-container'); const actionRunner = this._register(this.instantiationService.createInstance(NotificationActionRunner)); diff --git a/src/vs/workbench/browser/parts/notifications/notificationsStatus.ts b/src/vs/workbench/browser/parts/notifications/notificationsStatus.ts index 3e0ec62380d0..42778c61f741 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsStatus.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsStatus.ts @@ -97,7 +97,7 @@ export class NotificationsStatus extends Disposable { statusProperties, 'status.notifications', StatusbarAlignment.RIGHT, - -Number.MAX_VALUE /* towards the far end of the right hand side */ + Number.NEGATIVE_INFINITY /* last entry */ ); } else { this.notificationsCenterStatusItem.update(statusProperties); @@ -200,7 +200,7 @@ export class NotificationsStatus extends Disposable { }, 'status.message', StatusbarAlignment.LEFT, - -Number.MAX_VALUE /* far right on left hand side */ + Number.NEGATIVE_INFINITY /* last entry */ ); showHandle = null; }, showAfter); diff --git a/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts b/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts index f0dda3b44cbc..3a02bb871932 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsToasts.ts @@ -7,7 +7,7 @@ import './media/notificationsToasts.css'; import { localize } from '../../../../nls.js'; import { INotificationsModel, NotificationChangeType, INotificationChangeEvent, INotificationViewItem, NotificationViewItemContentChangeKind } from '../../../common/notifications.js'; import { IDisposable, dispose, toDisposable, DisposableStore } from '../../../../base/common/lifecycle.js'; -import { addDisposableListener, EventType, Dimension, scheduleAtNextAnimationFrame, isAncestorOfActiveElement, getWindow } from '../../../../base/browser/dom.js'; +import { addDisposableListener, EventType, Dimension, scheduleAtNextAnimationFrame, isAncestorOfActiveElement, getWindow, $ } from '../../../../base/browser/dom.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { NotificationsList } from './notificationsList.js'; import { Event, Emitter } from '../../../../base/common/event.js'; @@ -17,7 +17,7 @@ import { IThemeService, Themable } from '../../../../platform/theme/common/theme import { widgetShadow } from '../../../../platform/theme/common/colorRegistry.js'; import { IEditorGroupsService } from '../../../services/editor/common/editorGroupsService.js'; import { INotificationsToastController } from './notificationsCommands.js'; -import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; +import { IContextKey, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { Severity, NotificationsFilter, NotificationPriority } from '../../../../platform/notification/common/notification.js'; import { ScrollbarVisibility } from '../../../../base/common/scrollable.js'; import { ILifecycleService, LifecyclePhase } from '../../../services/lifecycle/common/lifecycle.js'; @@ -71,7 +71,7 @@ export class NotificationsToasts extends Themable implements INotificationsToast private readonly mapNotificationToToast = new Map(); private readonly mapNotificationToDisposable = new Map(); - private readonly notificationsToastsVisibleContextKey = NotificationsToastsVisibleContext.bindTo(this.contextKeyService); + private readonly notificationsToastsVisibleContextKey: IContextKey; private readonly addedToastsIntervalCounter = new IntervalCounter(NotificationsToasts.SPAM_PROTECTION.interval); @@ -82,12 +82,14 @@ export class NotificationsToasts extends Themable implements INotificationsToast @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService, @IThemeService themeService: IThemeService, @IEditorGroupsService private readonly editorGroupService: IEditorGroupsService, - @IContextKeyService private readonly contextKeyService: IContextKeyService, + @IContextKeyService contextKeyService: IContextKeyService, @ILifecycleService private readonly lifecycleService: ILifecycleService, @IHostService private readonly hostService: IHostService ) { super(themeService); + this.notificationsToastsVisibleContextKey = NotificationsToastsVisibleContext.bindTo(contextKeyService); + this.registerListeners(); } @@ -165,8 +167,7 @@ export class NotificationsToasts extends Themable implements INotificationsToast // Lazily create toasts containers let notificationsToastsContainer = this.notificationsToastsContainer; if (!notificationsToastsContainer) { - notificationsToastsContainer = this.notificationsToastsContainer = document.createElement('div'); - notificationsToastsContainer.classList.add('notifications-toasts'); + notificationsToastsContainer = this.notificationsToastsContainer = $('.notifications-toasts'); this.container.appendChild(notificationsToastsContainer); } @@ -175,8 +176,7 @@ export class NotificationsToasts extends Themable implements INotificationsToast notificationsToastsContainer.classList.add('visible'); // Container - const notificationToastContainer = document.createElement('div'); - notificationToastContainer.classList.add('notification-toast-container'); + const notificationToastContainer = $('.notification-toast-container'); const firstToast = notificationsToastsContainer.firstChild; if (firstToast) { @@ -186,8 +186,7 @@ export class NotificationsToasts extends Themable implements INotificationsToast } // Toast - const notificationToast = document.createElement('div'); - notificationToast.classList.add('notification-toast'); + const notificationToast = $('.notification-toast'); notificationToastContainer.appendChild(notificationToast); // Create toast with item and show diff --git a/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts b/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts index c1880ef981bd..ab770c9580bb 100644 --- a/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts +++ b/src/vs/workbench/browser/parts/notifications/notificationsViewer.ts @@ -45,12 +45,7 @@ export class NotificationsListDelegate implements IListVirtualDelegate { // Run action @@ -502,7 +489,7 @@ export class NotificationTemplateRenderer extends Disposable { notification.close(); } } - }(); + }()); const buttonToolbar = this.inputDisposables.add(new ButtonBar(this.template.buttonsContainer)); for (let i = 0; i < primaryActions.length; i++) { diff --git a/src/vs/workbench/browser/parts/paneCompositeBar.ts b/src/vs/workbench/browser/parts/paneCompositeBar.ts index 26693cb4be95..3c14098d65ff 100644 --- a/src/vs/workbench/browser/parts/paneCompositeBar.ts +++ b/src/vs/workbench/browser/parts/paneCompositeBar.ts @@ -29,7 +29,6 @@ import { IAction, Separator, SubmenuAction, toAction } from '../../../base/commo import { StringSHA1 } from '../../../base/common/hash.js'; import { GestureEvent } from '../../../base/browser/touch.js'; import { IPaneCompositePart } from './paneCompositePart.js'; -import { ITelemetryService } from '../../../platform/telemetry/common/telemetry.js'; import { IConfigurationService } from '../../../platform/configuration/common/configuration.js'; import { IViewsService } from '../../services/views/common/viewsService.js'; @@ -777,7 +776,6 @@ class ViewContainerActivityAction extends CompositeBarAction { private readonly part: Parts, private readonly paneCompositePart: IPaneCompositePart, @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService, - @ITelemetryService private readonly telemetryService: ITelemetryService, @IConfigurationService private readonly configurationService: IConfigurationService, @IActivityService private readonly activityService: IActivityService, ) { @@ -820,36 +818,22 @@ class ViewContainerActivityAction extends CompositeBarAction { if (sideBarVisible && activeViewlet?.getId() === this.compositeBarActionItem.id) { switch (focusBehavior) { case 'focus': - this.logAction('refocus'); this.paneCompositePart.openPaneComposite(this.compositeBarActionItem.id, focus); break; case 'toggle': default: // Hide sidebar if selected viewlet already visible - this.logAction('hide'); this.layoutService.setPartHidden(true, Parts.SIDEBAR_PART); break; } return; } - - this.logAction('show'); } await this.paneCompositePart.openPaneComposite(this.compositeBarActionItem.id, focus); return this.activate(); } - - private logAction(action: string) { - type ActivityBarActionClassification = { - owner: 'benibenj'; - comment: 'Event logged when an activity bar action is triggered.'; - viewletId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The view in the activity bar for which the action was performed.' }; - action: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The action that was performed. e.g. "hide", "show", or "refocus"' }; - }; - this.telemetryService.publicLog2<{ viewletId: String; action: String }, ActivityBarActionClassification>('activityBarAction', { viewletId: this.compositeBarActionItem.id, action }); - } } class PlaceHolderViewContainerActivityAction extends ViewContainerActivityAction { } diff --git a/src/vs/workbench/browser/parts/paneCompositePart.ts b/src/vs/workbench/browser/parts/paneCompositePart.ts index c8780871219d..d1d67e1ebef8 100644 --- a/src/vs/workbench/browser/parts/paneCompositePart.ts +++ b/src/vs/workbench/browser/parts/paneCompositePart.ts @@ -265,11 +265,9 @@ export abstract class AbstractPaneCompositePart extends CompositePart .items-container > .statusbar-item.right.last-visible-item { - margin-right: 7px; /* Add margin to the most right status bar item */ + padding-right: 7px; /* Add padding to the most right status bar item */ } /* Tweak appearance for items with background to improve hover feedback */ diff --git a/src/vs/workbench/browser/parts/statusbar/statusbarItem.ts b/src/vs/workbench/browser/parts/statusbar/statusbarItem.ts index 149ee4cbff52..26078d8b0b0f 100644 --- a/src/vs/workbench/browser/parts/statusbar/statusbarItem.ts +++ b/src/vs/workbench/browser/parts/statusbar/statusbarItem.ts @@ -13,7 +13,7 @@ import { WorkbenchActionExecutedEvent, WorkbenchActionExecutedClassification } f import { IThemeService } from '../../../../platform/theme/common/themeService.js'; import { ThemeColor } from '../../../../base/common/themables.js'; import { isThemeColor } from '../../../../editor/common/editorCommon.js'; -import { addDisposableListener, EventType, hide, show, append, EventHelper } from '../../../../base/browser/dom.js'; +import { addDisposableListener, EventType, hide, show, append, EventHelper, $ } from '../../../../base/browser/dom.js'; import { INotificationService } from '../../../../platform/notification/common/notification.js'; import { assertIsDefined } from '../../../../base/common/types.js'; import { Command } from '../../../../editor/common/languages.js'; @@ -66,10 +66,10 @@ export class StatusbarEntryItem extends Disposable { super(); // Label Container - this.labelContainer = document.createElement('a'); - this.labelContainer.tabIndex = -1; // allows screen readers to read title, but still prevents tab focus. - this.labelContainer.setAttribute('role', 'button'); - this.labelContainer.className = 'statusbar-item-label'; + this.labelContainer = $('a.statusbar-item-label', { + role: 'button', + tabIndex: -1 // allows screen readers to read title, but still prevents tab focus. + }); this._register(Gesture.addTarget(this.labelContainer)); // enable touch // Label (with support for progress) @@ -77,8 +77,7 @@ export class StatusbarEntryItem extends Disposable { this.container.appendChild(this.labelContainer); // Beak Container - this.beakContainer = document.createElement('div'); - this.beakContainer.className = 'status-bar-item-beak-container'; + this.beakContainer = $('.status-bar-item-beak-container'); this.container.appendChild(this.beakContainer); this.update(entry); @@ -253,8 +252,8 @@ export class StatusbarEntryItem extends Disposable { if (isThemeColor(color)) { colorResult = this.themeService.getColorTheme().getColor(color.id)?.toString(); - const listener = this.themeService.onDidColorThemeChange(theme => { - const colorValue = theme.getColor(color.id)?.toString(); + const listener = this.themeService.onDidColorThemeChange(e => { + const colorValue = e.theme.getColor(color.id)?.toString(); if (isBackground) { container.style.backgroundColor = colorValue ?? ''; diff --git a/src/vs/workbench/browser/parts/statusbar/statusbarModel.ts b/src/vs/workbench/browser/parts/statusbar/statusbarModel.ts index 82db371b426c..5f0264991d04 100644 --- a/src/vs/workbench/browser/parts/statusbar/statusbarModel.ts +++ b/src/vs/workbench/browser/parts/statusbar/statusbarModel.ts @@ -120,7 +120,7 @@ export class StatusbarViewModel extends Disposable { // Re-sort entries if this one was used // as reference from other entries - if (this._entries.some(otherEntry => isStatusbarEntryLocation(otherEntry.priority.primary) && otherEntry.priority.primary.id === entry.id)) { + if (this._entries.some(otherEntry => isStatusbarEntryLocation(otherEntry.priority.primary) && otherEntry.priority.primary.location.id === entry.id)) { this.sort(); } @@ -254,19 +254,19 @@ export class StatusbarViewModel extends Disposable { } private sort(): void { + const allEntryIds = new Set(this._entries.map(entry => entry.id)); // Split up entries into 2 buckets: - // - those with `priority: number` that can be compared - // - those with `priority: string` that must be sorted - // relative to another entry if possible + // - those with priority as number that can be compared or with a missing relative entry + // - those with a relative priority that must be sorted relative to another entry that exists const mapEntryWithNumberedPriorityToIndex = new Map(); const mapEntryWithRelativePriority = new Map>(); for (let i = 0; i < this._entries.length; i++) { const entry = this._entries[i]; - if (typeof entry.priority.primary === 'number') { + if (typeof entry.priority.primary === 'number' || !allEntryIds.has(entry.priority.primary.location.id)) { mapEntryWithNumberedPriorityToIndex.set(entry, i); } else { - const referenceEntryId = entry.priority.primary.id; + const referenceEntryId = entry.priority.primary.location.id; let entries = mapEntryWithRelativePriority.get(referenceEntryId); if (!entries) { @@ -290,15 +290,18 @@ export class StatusbarViewModel extends Disposable { } } - // Sort the entries with `priority: number` according to that + // Sort the entries with `priority: number` or referencing a missing entry accordingly const sortedEntriesWithNumberedPriority = Array.from(mapEntryWithNumberedPriorityToIndex.keys()); sortedEntriesWithNumberedPriority.sort((entryA, entryB) => { if (entryA.alignment === entryB.alignment) { // Sort by primary/secondary priority: higher values move towards the left - if (entryA.priority.primary !== entryB.priority.primary) { - return Number(entryB.priority.primary) - Number(entryA.priority.primary); + const entryAPrimaryPriority = typeof entryA.priority.primary === 'number' ? entryA.priority.primary : entryA.priority.primary.location.priority; + const entryBPrimaryPriority = typeof entryB.priority.primary === 'number' ? entryB.priority.primary : entryB.priority.primary.location.priority; + + if (entryAPrimaryPriority !== entryBPrimaryPriority) { + return entryBPrimaryPriority - entryAPrimaryPriority; } if (entryA.priority.secondary !== entryB.priority.secondary) { @@ -332,7 +335,9 @@ export class StatusbarViewModel extends Disposable { // Fill relative entries to LEFT if (relativeEntries) { - sortedEntries.push(...relativeEntries.filter(entry => isStatusbarEntryLocation(entry.priority.primary) && entry.priority.primary.alignment === StatusbarAlignment.LEFT)); + sortedEntries.push(...relativeEntries + .filter(entry => isStatusbarEntryLocation(entry.priority.primary) && entry.priority.primary.alignment === StatusbarAlignment.LEFT) + .sort((entryA, entryB) => entryB.priority.secondary - entryA.priority.secondary)); } // Fill referenced entry @@ -340,7 +345,9 @@ export class StatusbarViewModel extends Disposable { // Fill relative entries to RIGHT if (relativeEntries) { - sortedEntries.push(...relativeEntries.filter(entry => isStatusbarEntryLocation(entry.priority.primary) && entry.priority.primary.alignment === StatusbarAlignment.RIGHT)); + sortedEntries.push(...relativeEntries + .filter(entry => isStatusbarEntryLocation(entry.priority.primary) && entry.priority.primary.alignment === StatusbarAlignment.RIGHT) + .sort((entryA, entryB) => entryB.priority.secondary - entryA.priority.secondary)); } // Delete from map to mark as handled @@ -349,8 +356,13 @@ export class StatusbarViewModel extends Disposable { // Finally, just append all entries that reference another entry // that does not exist to the end of the list + // + // Note: this should really not happen because of our check in + // `allEntryIds`, but we play it safe here to really consume + // all entries. + // for (const [, entries] of mapEntryWithRelativePriority) { - sortedEntries.push(...entries.values()); + sortedEntries.push(...Array.from(entries.values()).sort((entryA, entryB) => entryB.priority.secondary - entryA.priority.secondary)); } } diff --git a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts index 6aee246ef2c4..2e6ce3bc4a2c 100644 --- a/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts +++ b/src/vs/workbench/browser/parts/statusbar/statusbarPart.ts @@ -16,7 +16,7 @@ import { IThemeService } from '../../../../platform/theme/common/themeService.js import { STATUS_BAR_BACKGROUND, STATUS_BAR_FOREGROUND, STATUS_BAR_NO_FOLDER_BACKGROUND, STATUS_BAR_ITEM_HOVER_BACKGROUND, STATUS_BAR_BORDER, STATUS_BAR_NO_FOLDER_FOREGROUND, STATUS_BAR_NO_FOLDER_BORDER, STATUS_BAR_ITEM_COMPACT_HOVER_BACKGROUND, STATUS_BAR_ITEM_FOCUS_BORDER, STATUS_BAR_FOCUS_BORDER } from '../../../common/theme.js'; import { IWorkspaceContextService, WorkbenchState } from '../../../../platform/workspace/common/workspace.js'; import { contrastBorder, activeContrastBorder } from '../../../../platform/theme/common/colorRegistry.js'; -import { EventHelper, addDisposableListener, EventType, clearNode, getWindow } from '../../../../base/browser/dom.js'; +import { EventHelper, addDisposableListener, EventType, clearNode, getWindow, isHTMLElement, $ } from '../../../../base/browser/dom.js'; import { createStyleSheet } from '../../../../base/browser/domStylesheets.js'; import { IStorageService } from '../../../../platform/storage/common/storage.js'; import { Parts, IWorkbenchLayoutService } from '../../../services/layout/browser/layoutService.js'; @@ -35,6 +35,7 @@ import { StatusbarEntryItem } from './statusbarItem.js'; import { StatusBarFocused } from '../../../common/contextkeys.js'; import { Emitter, Event } from '../../../../base/common/event.js'; import { IView } from '../../../../base/browser/ui/grid/grid.js'; +import { isManagedHoverTooltipHTMLElement, isManagedHoverTooltipMarkdownString } from '../../../../base/browser/ui/hover/hover.js'; export interface IStatusbarEntryContainer extends IDisposable { @@ -132,9 +133,9 @@ class StatusbarPart extends Part implements IStatusbarEntryContainer { private pendingEntries: IPendingStatusbarEntry[] = []; - private readonly viewModel = this._register(new StatusbarViewModel(this.storageService)); + private readonly viewModel: StatusbarViewModel; - readonly onDidChangeEntryVisibility = this.viewModel.onDidChangeEntryVisibility; + readonly onDidChangeEntryVisibility: Event<{ id: string; visible: boolean }>; private readonly _onWillDispose = this._register(new Emitter()); readonly onWillDispose = this._onWillDispose.event; @@ -145,14 +146,7 @@ class StatusbarPart extends Part implements IStatusbarEntryContainer { private leftItemsContainer: HTMLElement | undefined; private rightItemsContainer: HTMLElement | undefined; - private readonly hoverDelegate = this._register(this.instantiationService.createInstance(WorkbenchHoverDelegate, 'element', true, (_, focus?: boolean) => ( - { - persistence: { - hideOnKeyDown: true, - sticky: focus - } - } - ))); + private readonly hoverDelegate: WorkbenchHoverDelegate; private readonly compactEntriesDisposable = this._register(new MutableDisposable()); private readonly styleOverrides = new Set(); @@ -162,13 +156,42 @@ class StatusbarPart extends Part implements IStatusbarEntryContainer { @IInstantiationService private readonly instantiationService: IInstantiationService, @IThemeService themeService: IThemeService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, - @IStorageService private readonly storageService: IStorageService, + @IStorageService storageService: IStorageService, @IWorkbenchLayoutService layoutService: IWorkbenchLayoutService, @IContextMenuService private readonly contextMenuService: IContextMenuService, @IContextKeyService private readonly contextKeyService: IContextKeyService, ) { super(id, { hasTitle: false }, themeService, storageService, layoutService); + this.viewModel = this._register(new StatusbarViewModel(storageService)); + this.onDidChangeEntryVisibility = this.viewModel.onDidChangeEntryVisibility; + + this.hoverDelegate = this._register(this.instantiationService.createInstance(WorkbenchHoverDelegate, 'element', { + instantHover: true, + dynamicDelay(content) { + if ( + typeof content === 'function' || + isHTMLElement(content) || + (isManagedHoverTooltipMarkdownString(content) && typeof content.markdown === 'function') || + isManagedHoverTooltipHTMLElement(content) + ) { + // override the delay for content that is rich (e.g. html or long running) + // so that it appears more instantly. these hovers carry more important + // information and should not be delayed by preference. + return 500; + } + + return undefined; + } + }, (_, focus?: boolean) => ( + { + persistence: { + hideOnKeyDown: true, + sticky: focus + } + } + ))); + this.registerListeners(); } @@ -306,10 +329,8 @@ class StatusbarPart extends Part implements IStatusbarEntryContainer { } private doCreateStatusItem(id: string, alignment: StatusbarAlignment, ...extraClasses: string[]): HTMLElement { - const itemContainer = document.createElement('div'); - itemContainer.id = id; + const itemContainer = $('.statusbar-item', { id }); - itemContainer.classList.add('statusbar-item'); if (extraClasses) { itemContainer.classList.add(...extraClasses); } @@ -387,14 +408,12 @@ class StatusbarPart extends Part implements IStatusbarEntryContainer { StatusBarFocused.bindTo(scopedContextKeyService).set(true); // Left items container - this.leftItemsContainer = document.createElement('div'); - this.leftItemsContainer.classList.add('left-items', 'items-container'); + this.leftItemsContainer = $('.left-items.items-container'); this.element.appendChild(this.leftItemsContainer); this.element.tabIndex = 0; // Right items container - this.rightItemsContainer = document.createElement('div'); - this.rightItemsContainer.classList.add('right-items', 'items-container'); + this.rightItemsContainer = $('.right-items.items-container'); this.element.appendChild(this.rightItemsContainer); // Context menu support @@ -484,7 +503,7 @@ class StatusbarPart extends Part implements IStatusbarEntryContainer { isStatusbarEntryLocation(entry.priority.primary) && // entry references another entry as location entry.priority.primary.compact // entry wants to be compact ) { - const locationId = entry.priority.primary.id; + const locationId = entry.priority.primary.location.id; const location = mapIdToVisibleEntry.get(locationId); if (!location) { continue; // skip if location does not exist @@ -734,7 +753,7 @@ export class StatusbarService extends MultiWindowParts implements declare readonly _serviceBrand: undefined; - readonly mainPart = this._register(this.instantiationService.createInstance(MainStatusbarPart)); + readonly mainPart: MainStatusbarPart; private readonly _onDidCreateAuxiliaryStatusbarPart = this._register(new Emitter()); private readonly onDidCreateAuxiliaryStatusbarPart = this._onDidCreateAuxiliaryStatusbarPart.event; @@ -746,7 +765,10 @@ export class StatusbarService extends MultiWindowParts implements ) { super('workbench.statusBarService', themeService, storageService); + this.mainPart = this._register(this.instantiationService.createInstance(MainStatusbarPart)); this._register(this.registerPart(this.mainPart)); + + this.onDidChangeEntryVisibility = this.mainPart.onDidChangeEntryVisibility; } //#region Auxiliary Statusbar Parts @@ -754,12 +776,12 @@ export class StatusbarService extends MultiWindowParts implements createAuxiliaryStatusbarPart(container: HTMLElement): IAuxiliaryStatusbarPart { // Container - const statusbarPartContainer = document.createElement('footer'); - statusbarPartContainer.classList.add('part', 'statusbar'); - statusbarPartContainer.setAttribute('role', 'status'); + const statusbarPartContainer = $('footer.part.statusbar', { + 'role': 'status', + 'aria-live': 'off', + 'tabIndex': '0' + }); statusbarPartContainer.style.position = 'relative'; - statusbarPartContainer.setAttribute('aria-live', 'off'); - statusbarPartContainer.setAttribute('tabindex', '0'); container.appendChild(statusbarPartContainer); // Statusbar Part @@ -784,7 +806,7 @@ export class StatusbarService extends MultiWindowParts implements //#region Service Implementation - readonly onDidChangeEntryVisibility = this.mainPart.onDidChangeEntryVisibility; + readonly onDidChangeEntryVisibility: Event<{ id: string; visible: boolean }>; addEntry(entry: IStatusbarEntry, id: string, alignment: StatusbarAlignment, priorityOrLocation: number | IStatusbarEntryLocation | IStatusbarEntryPriority = 0): IStatusbarEntryAccessor { if (entry.showInAllWindows) { @@ -794,11 +816,12 @@ export class StatusbarService extends MultiWindowParts implements return this.mainPart.addEntry(entry, id, alignment, priorityOrLocation); } - private doAddEntryToAllWindows(entry: IStatusbarEntry, id: string, alignment: StatusbarAlignment, priorityOrLocation: number | IStatusbarEntryLocation | IStatusbarEntryPriority = 0): IStatusbarEntryAccessor { + private doAddEntryToAllWindows(originalEntry: IStatusbarEntry, id: string, alignment: StatusbarAlignment, priorityOrLocation: number | IStatusbarEntryLocation | IStatusbarEntryPriority = 0): IStatusbarEntryAccessor { const entryDisposables = new DisposableStore(); const accessors = new Set(); + let entry = originalEntry; function addEntry(part: StatusbarPart | AuxiliaryStatusbarPart): void { const partDisposables = new DisposableStore(); partDisposables.add(part.onWillDispose(() => partDisposables.dispose())); @@ -818,9 +841,11 @@ export class StatusbarService extends MultiWindowParts implements entryDisposables.add(this.onDidCreateAuxiliaryStatusbarPart(part => addEntry(part))); return { - update: (entry: IStatusbarEntry) => { + update: (updatedEntry: IStatusbarEntry) => { + entry = updatedEntry; + for (const update of accessors) { - update.update(entry); + update.update(updatedEntry); } }, dispose: () => entryDisposables.dispose() @@ -885,6 +910,8 @@ export class ScopedStatusbarService extends Disposable implements IStatusbarServ @IStatusbarService private readonly statusbarService: IStatusbarService ) { super(); + + this.onDidChangeEntryVisibility = this.statusbarEntryContainer.onDidChangeEntryVisibility; } createAuxiliaryStatusbarPart(container: HTMLElement): IAuxiliaryStatusbarPart { @@ -899,7 +926,7 @@ export class ScopedStatusbarService extends Disposable implements IStatusbarServ return this.statusbarEntryContainer; } - readonly onDidChangeEntryVisibility = this.statusbarEntryContainer.onDidChangeEntryVisibility; + readonly onDidChangeEntryVisibility: Event<{ id: string; visible: boolean }>; addEntry(entry: IStatusbarEntry, id: string, alignment: StatusbarAlignment, priorityOrLocation: number | IStatusbarEntryLocation | IStatusbarEntryPriority = 0): IStatusbarEntryAccessor { return this.statusbarEntryContainer.addEntry(entry, id, alignment, priorityOrLocation); diff --git a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css index 2f16f7a10370..e1a7a7e2f70f 100644 --- a/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css +++ b/src/vs/workbench/browser/parts/titlebar/media/titlebarpart.css @@ -231,6 +231,21 @@ zoom: var(--zoom-factor); /* helps to position the menu properly when counter zooming */ } +/* Resizer */ +.monaco-workbench.windows .part.titlebar > .titlebar-container > .resizer, +.monaco-workbench.linux .part.titlebar > .titlebar-container > .resizer { + -webkit-app-region: no-drag; + position: absolute; + top: 0; + width: 100%; + height: 4px; +} + +.monaco-workbench.windows.fullscreen .part.titlebar > .titlebar-container > .resizer, +.monaco-workbench.linux.fullscreen .part.titlebar > .titlebar-container > .resizer { + display: none; +} + /* App Icon */ .monaco-workbench .part.titlebar > .titlebar-container > .titlebar-left > .window-appicon { width: 35px; @@ -313,6 +328,42 @@ width: 70px; } +/* Window Control Icons */ +.monaco-workbench .part.titlebar .window-controls-container > .window-icon { + display: flex; + justify-content: center; + align-items: center; + height: 100%; + width: 46px; + font-size: 16px; + color: var(--vscode-titleBar-activeForeground); +} + +.monaco-workbench .part.titlebar.inactive .window-controls-container > .window-icon { + color: var(--vscode-titleBar-inactiveForeground); +} + +.monaco-workbench .part.titlebar .window-controls-container > .window-icon::before { + height: 16px; + line-height: 16px; +} + +.monaco-workbench .part.titlebar .window-controls-container > .window-icon:hover { + background-color: rgba(255, 255, 255, 0.1); +} + +.monaco-workbench .part.titlebar.light .window-controls-container > .window-icon:hover { + background-color: rgba(0, 0, 0, 0.1); +} + +.monaco-workbench .part.titlebar .window-controls-container > .window-icon.window-close:hover { + background-color: rgba(232, 17, 35, 0.9); +} + +.monaco-workbench .part.titlebar .window-controls-container .window-icon.window-close:hover { + color: white; +} + /* Action Tool Bar Controls */ .monaco-workbench .part.titlebar > .titlebar-container > .titlebar-right > .action-toolbar-container { display: none; diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts b/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts index e09172e4a7f2..058681cba270 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarActions.ts @@ -8,13 +8,15 @@ import { IConfigurationService } from '../../../../platform/configuration/common import { ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; import { LayoutSettings } from '../../../services/layout/browser/layoutService.js'; -import { Action2, MenuId, registerAction2 } from '../../../../platform/actions/common/actions.js'; +import { Action2, MenuId, MenuRegistry, registerAction2 } from '../../../../platform/actions/common/actions.js'; import { ContextKeyExpr, ContextKeyExpression, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { ACCOUNTS_ACTIVITY_ID, GLOBAL_ACTIVITY_ID } from '../../../common/activity.js'; import { IAction } from '../../../../base/common/actions.js'; import { IsAuxiliaryWindowFocusedContext, IsMainWindowFullscreenContext, TitleBarStyleContext, TitleBarVisibleContext } from '../../../common/contextkeys.js'; import { CustomTitleBarVisibility, TitleBarSetting, TitlebarStyle } from '../../../../platform/window/common/window.js'; import { isLinux, isNative } from '../../../../base/common/platform.js'; +import { Categories } from '../../../../platform/action/common/actionCommonCategories.js'; +import { IsMacNativeContext } from '../../../../platform/contextkey/common/contextkeys.js'; // --- Context Menu Actions --- // @@ -279,6 +281,43 @@ if (isLinux && isNative) { }); } +for (const menuId of [MenuId.TitleBarContext, MenuId.TitleBarTitleContext]) { + MenuRegistry.appendMenuItem(menuId, { + command: { + id: 'workbench.action.toggleMenuBar', + title: localize('miMenuBarNoMnemonic', "Menu Bar"), + toggled: ContextKeyExpr.and(IsMacNativeContext.toNegated(), ContextKeyExpr.notEquals('config.window.menuBarVisibility', 'hidden'), ContextKeyExpr.notEquals('config.window.menuBarVisibility', 'toggle'), ContextKeyExpr.notEquals('config.window.menuBarVisibility', 'compact')) + }, + when: ContextKeyExpr.and(IsAuxiliaryWindowFocusedContext.toNegated(), ContextKeyExpr.notEquals(TitleBarStyleContext.key, TitlebarStyle.NATIVE), IsMainWindowFullscreenContext.negate()), + group: '2_config', + order: 0 + }); +} + +registerAction2(class extends Action2 { + + constructor() { + super({ + id: 'workbench.action.toggleMenuBarInFullScreen', + title: localize2('menuBar', "Menu Bar"), + category: Categories.View, + toggled: ContextKeyExpr.equals('config.window.menuBarVisibility', 'visible'), + menu: [{ + id: MenuId.TitleBarContext, + group: '2_config', + order: 0, + when: ContextKeyExpr.and(IsAuxiliaryWindowFocusedContext.toNegated(), ContextKeyExpr.notEquals(TitleBarStyleContext.key, TitlebarStyle.NATIVE), IsMainWindowFullscreenContext, IsMacNativeContext.negate()), + }] + }); + } + + run(accessor: ServicesAccessor): void { + const configurationService = accessor.get(IConfigurationService); + const isVisible = configurationService.getValue('window.menuBarVisibility') === 'visible'; + configurationService.updateValue('window.menuBarVisibility', isVisible ? 'classic' : 'visible'); + } +}); + // --- Toolbar actions --- // export const ACCOUNTS_ACTIVITY_TILE_ACTION: IAction = { diff --git a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts index 8aa33c789709..87fa51d50d85 100644 --- a/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts +++ b/src/vs/workbench/browser/parts/titlebar/titlebarPart.ts @@ -8,7 +8,7 @@ import { localize, localize2 } from '../../../../nls.js'; import { MultiWindowParts, Part } from '../../part.js'; import { ITitleService } from '../../../services/title/browser/titleService.js'; import { getWCOTitlebarAreaRect, getZoomFactor, isWCOEnabled } from '../../../../base/browser/browser.js'; -import { MenuBarVisibility, getTitleBarStyle, getMenuBarVisibility, hasCustomTitlebar, hasNativeTitlebar, DEFAULT_CUSTOM_TITLEBAR_HEIGHT } from '../../../../platform/window/common/window.js'; +import { MenuBarVisibility, getTitleBarStyle, getMenuBarVisibility, hasCustomTitlebar, hasNativeTitlebar, DEFAULT_CUSTOM_TITLEBAR_HEIGHT, getWindowControlsStyle, WindowControlsStyle, TitlebarStyle } from '../../../../platform/window/common/window.js'; import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js'; import { StandardMouseEvent } from '../../../../base/browser/mouseEvent.js'; import { IConfigurationService, IConfigurationChangeEvent } from '../../../../platform/configuration/common/configuration.js'; @@ -89,7 +89,7 @@ export class BrowserTitleService extends MultiWindowParts i declare _serviceBrand: undefined; - readonly mainPart = this._register(this.createMainTitlebarPart()); + readonly mainPart: BrowserTitlebarPart; constructor( @IInstantiationService protected readonly instantiationService: IInstantiationService, @@ -98,6 +98,8 @@ export class BrowserTitleService extends MultiWindowParts i ) { super('workbench.titleService', themeService, storageService); + this.mainPart = this._register(this.createMainTitlebarPart()); + this.onMenubarVisibilityChange = this.mainPart.onMenubarVisibilityChange; this._register(this.registerPart(this.mainPart)); this.registerActions(); @@ -149,9 +151,7 @@ export class BrowserTitleService extends MultiWindowParts i //#region Auxiliary Titlebar Parts createAuxiliaryTitlebarPart(container: HTMLElement, editorGroupsContainer: IEditorGroupsContainer): IAuxiliaryTitlebarPart { - const titlebarPartContainer = document.createElement('div'); - titlebarPartContainer.classList.add('part', 'titlebar'); - titlebarPartContainer.setAttribute('role', 'none'); + const titlebarPartContainer = $('.part.titlebar', { role: 'none' }); titlebarPartContainer.style.position = 'relative'; container.insertBefore(titlebarPartContainer, container.firstChild); // ensure we are first element @@ -185,7 +185,7 @@ export class BrowserTitleService extends MultiWindowParts i //#region Service Implementation - readonly onMenubarVisibilityChange = this.mainPart.onMenubarVisibilityChange; + readonly onMenubarVisibilityChange: Event; private properties: ITitleProperties | undefined = undefined; @@ -249,6 +249,8 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { //#endregion protected rootContainer!: HTMLElement; + protected windowControlsContainer: HTMLElement | undefined; + protected dragRegion: HTMLElement | undefined; private title!: HTMLElement; @@ -267,7 +269,7 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { private readonly editorActionsChangeDisposable = this._register(new DisposableStore()); private actionToolBarElement!: HTMLElement; - private globalToolbarMenu = this._register(this.menuService.createMenu(MenuId.TitleBar, this.contextKeyService)); + private globalToolbarMenu: IMenu; private hasGlobalToolbarEntries = false; private layoutToolbarMenu: IMenu | undefined; @@ -279,7 +281,7 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { private readonly hoverDelegate: IHoverDelegate; private readonly titleDisposables = this._register(new DisposableStore()); - private titleBarStyle = getTitleBarStyle(this.configurationService); + private titleBarStyle: TitlebarStyle; private isInactive: boolean = false; private readonly isAuxiliary: boolean; @@ -309,6 +311,9 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { ) { super(id, { hasTitle: false }, themeService, storageService, layoutService); + this.titleBarStyle = getTitleBarStyle(this.configurationService); + this.globalToolbarMenu = this._register(this.menuService.createMenu(MenuId.TitleBar, this.contextKeyService)); + this.isAuxiliary = editorGroupsContainer !== 'main'; this.editorService = editorService.createScoped(editorGroupsContainer, this._store); this.editorGroupsContainer = editorGroupsContainer === 'main' ? editorGroupService.mainPart : editorGroupsContainer; @@ -484,8 +489,10 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { // for something, except for web where a custom menu being supported). not putting the // container helps with allowing to move the window when clicking very close to the // window control buttons. + } else if (getWindowControlsStyle(this.configurationService) === WindowControlsStyle.HIDDEN) { + // Linux/Windows: controls are explicitly disabled } else { - const windowControlsContainer = append(primaryWindowControlsLocation === 'left' ? this.leftContent : this.rightContent, $('div.window-controls-container')); + this.windowControlsContainer = append(primaryWindowControlsLocation === 'left' ? this.leftContent : this.rightContent, $('div.window-controls-container')); if (isWeb) { // Web: its possible to have control overlays on both sides, for example on macOS // with window controls on the left and PWA controls on the right. @@ -493,7 +500,7 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { } if (isWCOEnabled()) { - windowControlsContainer.classList.add('wco-enabled'); + this.windowControlsContainer.classList.add('wco-enabled'); } } } @@ -636,15 +643,6 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { } } - // --- Activity Actions - if (this.activityActionsEnabled) { - if (isAccountsActionVisible(this.storageService)) { - actions.primary.push(ACCOUNTS_ACTIVITY_TILE_ACTION); - } - - actions.primary.push(GLOBAL_ACTIVITY_TITLE_ACTION); - } - // --- Global Actions const globalToolbarActions = this.globalToolbarMenu.getActions(); this.hasGlobalToolbarEntries = globalToolbarActions.length > 0; @@ -653,7 +651,7 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { actions ); - // --- Layout Actions (always at the end) + // --- Layout Actions if (this.layoutToolbarMenu) { fillInActionBarActions( this.layoutToolbarMenu.getActions(), @@ -662,6 +660,15 @@ export class BrowserTitlebarPart extends Part implements ITitlebarPart { ); } + // --- Activity Actions (always at the end) + if (this.activityActionsEnabled) { + if (isAccountsActionVisible(this.storageService)) { + actions.primary.push(ACCOUNTS_ACTIVITY_TILE_ACTION); + } + + actions.primary.push(GLOBAL_ACTIVITY_TITLE_ACTION); + } + this.actionToolBar.setActions(prepareActions(actions.primary), prepareActions(actions.secondary)); }; diff --git a/src/vs/workbench/browser/parts/titlebar/windowTitle.ts b/src/vs/workbench/browser/parts/titlebar/windowTitle.ts index b80ede705399..9d901f2ffe88 100644 --- a/src/vs/workbench/browser/parts/titlebar/windowTitle.ts +++ b/src/vs/workbench/browser/parts/titlebar/windowTitle.ts @@ -30,10 +30,11 @@ import { IContextKeyService } from '../../../../platform/contextkey/common/conte import { getWindowById } from '../../../../base/browser/dom.js'; import { CodeWindow } from '../../../../base/browser/window.js'; import { IDecorationsService } from '../../../services/decorations/common/decorations.js'; +import { IAccessibilityService } from '../../../../platform/accessibility/common/accessibility.js'; const enum WindowSettingNames { titleSeparator = 'window.titleSeparator', - title = 'window.title' + title = 'window.title', } export const defaultWindowTitle = (() => { @@ -98,7 +99,8 @@ export class WindowTitle extends Disposable { @IUserDataProfileService private readonly userDataProfileService: IUserDataProfileService, @IProductService private readonly productService: IProductService, @IViewsService private readonly viewsService: IViewsService, - @IDecorationsService private readonly decorationsService: IDecorationsService + @IDecorationsService private readonly decorationsService: IDecorationsService, + @IAccessibilityService private readonly accessibilityService: IAccessibilityService ) { super(); @@ -128,6 +130,7 @@ export class WindowTitle extends Disposable { this.titleUpdater.schedule(); } })); + this._register(this.accessibilityService.onDidChangeScreenReaderOptimized(() => this.titleUpdater.schedule())); } private onConfigurationChanged(event: IConfigurationChangeEvent): void { @@ -372,6 +375,10 @@ export class WindowTitle extends Disposable { titleTemplate = defaultWindowTitle; } + if (!this.titleIncludesEditorState && this.accessibilityService.isScreenReaderOptimized() && this.configurationService.getValue('accessibility.windowTitleOptimized')) { + titleTemplate += '${separator}${activeEditorState}'; + } + let separator = this.configurationService.getValue(WindowSettingNames.titleSeparator); if (typeof separator !== 'string') { separator = defaultWindowTitleSeparator; @@ -401,6 +408,9 @@ export class WindowTitle extends Disposable { } isCustomTitleFormat(): boolean { + if (this.accessibilityService.isScreenReaderOptimized() || this.titleIncludesEditorState) { + return true; + } const title = this.configurationService.inspect(WindowSettingNames.title); const titleSeparator = this.configurationService.inspect(WindowSettingNames.titleSeparator); diff --git a/src/vs/workbench/browser/parts/views/treeView.ts b/src/vs/workbench/browser/parts/views/treeView.ts index 23b256ad2265..00060e5bf167 100644 --- a/src/vs/workbench/browser/parts/views/treeView.ts +++ b/src/vs/workbench/browser/parts/views/treeView.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + import { DataTransfers, IDragAndDropData } from '../../../../base/browser/dnd.js'; import * as DOM from '../../../../base/browser/dom.js'; import * as cssJs from '../../../../base/browser/cssValue.js'; @@ -94,12 +95,11 @@ export class TreeViewPane extends ViewPane { @IInstantiationService instantiationService: IInstantiationService, @IOpenerService openerService: IOpenerService, @IThemeService themeService: IThemeService, - @ITelemetryService telemetryService: ITelemetryService, @INotificationService notificationService: INotificationService, @IHoverService hoverService: IHoverService, @IAccessibleViewInformationService accessibleViewService: IAccessibleViewInformationService, ) { - super({ ...(options as IViewPaneOptions), titleMenuId: MenuId.ViewTitle, donotForwardArgs: false }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService, hoverService, accessibleViewService); + super({ ...(options as IViewPaneOptions), titleMenuId: MenuId.ViewTitle, donotForwardArgs: false }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, hoverService, accessibleViewService); const { treeView } = (Registry.as(Extensions.ViewsRegistry).getView(options.id)); this.treeView = treeView; this._register(this.treeView.onDidChangeActions(() => this.updateActions(), this)); @@ -1265,7 +1265,7 @@ class TreeRenderer extends Disposable implements ITreeRenderer this.rerender())); this._register(this.themeService.onDidColorThemeChange(() => this.rerender())); this._register(checkboxStateHandler.onDidChangeCheckboxState(items => { diff --git a/src/vs/workbench/browser/parts/views/viewPane.ts b/src/vs/workbench/browser/parts/views/viewPane.ts index d01b9fd373f7..87acf448f1d1 100644 --- a/src/vs/workbench/browser/parts/views/viewPane.ts +++ b/src/vs/workbench/browser/parts/views/viewPane.ts @@ -16,7 +16,6 @@ import { ActionsOrientation, IActionViewItem, prepareActions } from '../../../.. import { Registry } from '../../../../platform/registry/common/platform.js'; import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js'; -import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; import { IThemeService } from '../../../../platform/theme/common/themeService.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; import { IPaneOptions, Pane, IPaneStyles } from '../../../../base/browser/ui/splitview/paneview.js'; @@ -83,13 +82,6 @@ export interface IFilterViewPaneOptions extends IViewPaneOptions { export const VIEWPANE_FILTER_ACTION = new Action('viewpane.action.filter'); -type WelcomeActionClassification = { - owner: 'joaomoreno'; - viewId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The view ID in which the welcome view button was clicked.' }; - uri: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The URI of the command ran by the result of clicking the button.' }; - comment: 'This is used to know when users click on the welcome view buttons.'; -}; - const viewPaneContainerExpandedIcon = registerIcon('view-pane-container-expanded', Codicon.chevronDown, nls.localize('viewPaneContainerExpandedIcon', 'Icon for an expanded view pane container.')); const viewPaneContainerCollapsedIcon = registerIcon('view-pane-container-collapsed', Codicon.chevronRight, nls.localize('viewPaneContainerCollapsedIcon', 'Icon for a collapsed view pane container.')); @@ -125,7 +117,6 @@ class ViewWelcomeController { private readonly delegate: IViewWelcomeDelegate, @IInstantiationService private instantiationService: IInstantiationService, @IOpenerService protected openerService: IOpenerService, - @ITelemetryService protected telemetryService: ITelemetryService, @IContextKeyService private contextKeyService: IContextKeyService, @ILifecycleService lifecycleService: ILifecycleService ) { @@ -257,7 +248,6 @@ class ViewWelcomeController { const button = new Button(buttonContainer, { title: node.title, supportIcons: true, secondary: renderSecondaryButtons && buttonsCount > 0 ? true : false, ...defaultButtonStyles, }); button.label = node.label; button.onDidClick(_ => { - this.telemetryService.publicLog2<{ viewId: string; uri: string }, WelcomeActionClassification>('views.welcomeAction', { viewId: this.delegate.id, uri: node.href }); this.openerService.open(node.href, { allowCommands: true }); }, null, this.renderDisposables); this.renderDisposables.add(button); @@ -381,7 +371,6 @@ export abstract class ViewPane extends Pane implements IView { @IInstantiationService protected instantiationService: IInstantiationService, @IOpenerService protected openerService: IOpenerService, @IThemeService protected themeService: IThemeService, - @ITelemetryService protected telemetryService: ITelemetryService, @IHoverService protected readonly hoverService: IHoverService, protected readonly accessibleViewInformationService?: IAccessibleViewInformationService ) { @@ -760,11 +749,10 @@ export abstract class FilterViewPane extends ViewPane { @IInstantiationService instantiationService: IInstantiationService, @IOpenerService openerService: IOpenerService, @IThemeService themeService: IThemeService, - @ITelemetryService telemetryService: ITelemetryService, @IHoverService hoverService: IHoverService, accessibleViewService?: IAccessibleViewInformationService ) { - super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService, hoverService, accessibleViewService); + super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, hoverService, accessibleViewService); const childInstantiationService = this._register(instantiationService.createChild(new ServiceCollection([IContextKeyService, this.scopedContextKeyService]))); this.filterWidget = this._register(childInstantiationService.createInstance(FilterWidget, options.filterOptions)); } diff --git a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts index b4f069c65f6c..eaa4e9aae1ac 100644 --- a/src/vs/workbench/browser/parts/views/viewPaneContainer.ts +++ b/src/vs/workbench/browser/parts/views/viewPaneContainer.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { addDisposableListener, Dimension, DragAndDropObserver, EventType, getWindow, isAncestor } from '../../../../base/browser/dom.js'; +import { $, addDisposableListener, Dimension, DragAndDropObserver, EventType, getWindow, isAncestor } from '../../../../base/browser/dom.js'; import { StandardMouseEvent } from '../../../../base/browser/mouseEvent.js'; import { EventType as TouchEventType, Gesture } from '../../../../base/browser/touch.js'; import { IActionViewItem } from '../../../../base/browser/ui/actionbar/actionbar.js'; @@ -104,9 +104,9 @@ class ViewPaneDropOverlay extends Themable { } private create(): void { + // Container - this.container = document.createElement('div'); - this.container.id = ViewPaneDropOverlay.OVERLAY_ID; + this.container = $('div', { id: ViewPaneDropOverlay.OVERLAY_ID }); this.container.style.top = '0px'; // Parent @@ -118,8 +118,7 @@ class ViewPaneDropOverlay extends Themable { })); // Overlay - this.overlay = document.createElement('div'); - this.overlay.classList.add('pane-overlay-indicator'); + this.overlay = $('.pane-overlay-indicator'); this.container.appendChild(this.overlay); // Overlay Event Handling diff --git a/src/vs/workbench/browser/workbench.contribution.ts b/src/vs/workbench/browser/workbench.contribution.ts index ca6f3d7a34d1..5e785e81b938 100644 --- a/src/vs/workbench/browser/workbench.contribution.ts +++ b/src/vs/workbench/browser/workbench.contribution.ts @@ -682,7 +682,7 @@ const registry = Registry.as(ConfigurationExtensions.Con localize('focusedView', "`${focusedView}`: the name of the view that is currently focused."), localize('activeRepositoryName', "`${activeRepositoryName}`: the name of the active repository (e.g. vscode)."), localize('activeRepositoryBranchName', "`${activeRepositoryBranchName}`: the name of the active branch in the active repository (e.g. main)."), - localize('activeEditorState', "`${activeEditorState}`: provides information about the state of the active editor (e.g. modified)."), + localize('activeEditorState', "`${activeEditorState}`: provides information about the state of the active editor (e.g. modified). This will be appended by default when in screen reader mode with {0} enabled.", '`accessibility.windowTitleOptimized`'), localize('separator', "`${separator}`: a conditional separator (\" - \") that only shows when surrounded by variables with values or static text.") ].join('\n- '); // intentionally concatenated to not produce a string that is too long for translations diff --git a/src/vs/workbench/browser/workbench.ts b/src/vs/workbench/browser/workbench.ts index 3d4375d13b22..267da8cf7dbe 100644 --- a/src/vs/workbench/browser/workbench.ts +++ b/src/vs/workbench/browser/workbench.ts @@ -136,7 +136,7 @@ export class Workbench extends Layout { // Default Hover Delegate must be registered before creating any workbench/layout components // as these possibly will use the default hover delegate - setHoverDelegateFactory((placement, enableInstantHover) => instantiationService.createInstance(WorkbenchHoverDelegate, placement, enableInstantHover, {})); + setHoverDelegateFactory((placement, enableInstantHover) => instantiationService.createInstance(WorkbenchHoverDelegate, placement, { instantHover: enableInstantHover }, {})); setBaseLayerHoverDelegate(hoverService); // Layout diff --git a/src/vs/workbench/common/contextkeys.ts b/src/vs/workbench/common/contextkeys.ts index 8e3f6a2f3c74..d11f9b168aa1 100644 --- a/src/vs/workbench/common/contextkeys.ts +++ b/src/vs/workbench/common/contextkeys.ts @@ -15,8 +15,6 @@ import { Schemas } from '../../base/common/network.js'; import { EditorInput } from './editor/editorInput.js'; import { IEditorResolverService } from '../services/editor/common/editorResolverService.js'; import { DEFAULT_EDITOR_ASSOCIATION } from './editor.js'; -import { isLinux } from '../../base/common/platform.js'; -import product from '../../platform/product/common/product.js'; //#region < --- Workbench --- > @@ -111,7 +109,7 @@ export const StatusBarFocused = new RawContextKey('statusBarFocused', f //#region < --- Title Bar --- > -export const TitleBarStyleContext = new RawContextKey('titleBarStyle', isLinux && product.quality === 'stable' ? 'native' : 'custom', localize('titleBarStyle', "Style of the window title bar")); +export const TitleBarStyleContext = new RawContextKey('titleBarStyle', 'custom', localize('titleBarStyle', "Style of the window title bar")); export const TitleBarVisibleContext = new RawContextKey('titleBarVisible', false, localize('titleBarVisible', "Whether the title bar is visible")); //#endregion diff --git a/src/vs/workbench/common/editor/diffEditorInput.ts b/src/vs/workbench/common/editor/diffEditorInput.ts index 8ed7a6695287..8f2c0a1696ec 100644 --- a/src/vs/workbench/common/editor/diffEditorInput.ts +++ b/src/vs/workbench/common/editor/diffEditorInput.ts @@ -17,17 +17,17 @@ import { shorten } from '../../../base/common/labels.js'; import { isResolvedEditorModel } from '../../../platform/editor/common/editor.js'; interface IDiffEditorInputLabels { - name: string; + readonly name: string; - shortDescription: string | undefined; - mediumDescription: string | undefined; - longDescription: string | undefined; + readonly shortDescription: string | undefined; + readonly mediumDescription: string | undefined; + readonly longDescription: string | undefined; - forceDescription: boolean; + readonly forceDescription: boolean; - shortTitle: string; - mediumTitle: string; - longTitle: string; + readonly shortTitle: string; + readonly mediumTitle: string; + readonly longTitle: string; } /** @@ -59,7 +59,7 @@ export class DiffEditorInput extends SideBySideEditorInput implements IDiffEdito private cachedModel: DiffEditorModel | undefined = undefined; - private readonly labels = this.computeLabels(); + private readonly labels: IDiffEditorInputLabels; constructor( preferredName: string | undefined, @@ -70,6 +70,8 @@ export class DiffEditorInput extends SideBySideEditorInput implements IDiffEdito @IEditorService editorService: IEditorService ) { super(preferredName, preferredDescription, original, modified, editorService); + + this.labels = this.computeLabels(); } private computeLabels(): IDiffEditorInputLabels { diff --git a/src/vs/workbench/common/editor/resourceEditorInput.ts b/src/vs/workbench/common/editor/resourceEditorInput.ts index 778f956ad627..360b534bdc9f 100644 --- a/src/vs/workbench/common/editor/resourceEditorInput.ts +++ b/src/vs/workbench/common/editor/resourceEditorInput.ts @@ -64,6 +64,7 @@ export abstract class AbstractResourceEditorInput extends EditorInput implements this._register(this.fileService.onDidChangeFileSystemProviderRegistrations(e => this.onLabelEvent(e.scheme))); this._register(this.fileService.onDidChangeFileSystemProviderCapabilities(e => this.onLabelEvent(e.scheme))); this._register(this.customEditorLabelService.onDidChange(() => this.updateLabel())); + this._register(this.filesConfigurationService.onDidChangeReadonly(() => this._onDidChangeCapabilities.fire())); } private onLabelEvent(scheme: string): void { diff --git a/src/vs/workbench/common/editor/sideBySideEditorInput.ts b/src/vs/workbench/common/editor/sideBySideEditorInput.ts index 5b20850a87e8..3a4cb208b304 100644 --- a/src/vs/workbench/common/editor/sideBySideEditorInput.ts +++ b/src/vs/workbench/common/editor/sideBySideEditorInput.ts @@ -60,7 +60,7 @@ export class SideBySideEditorInput extends EditorInput implements ISideBySideEdi return undefined; } - private hasIdenticalSides = this.primary.matches(this.secondary); + private hasIdenticalSides: boolean; constructor( protected readonly preferredName: string | undefined, @@ -71,6 +71,8 @@ export class SideBySideEditorInput extends EditorInput implements ISideBySideEdi ) { super(); + this.hasIdenticalSides = this.primary.matches(this.secondary); + this.registerListeners(); } diff --git a/src/vs/workbench/common/notifications.ts b/src/vs/workbench/common/notifications.ts index 31cef956ceb2..6a690593672a 100644 --- a/src/vs/workbench/common/notifications.ts +++ b/src/vs/workbench/common/notifications.ts @@ -29,7 +29,7 @@ export interface INotificationsModel { //#endregion - //#region Notifications as Status + //#region Notifications as Status readonly statusMessage: IStatusMessageViewItem | undefined; diff --git a/src/vs/workbench/common/theme.ts b/src/vs/workbench/common/theme.ts index 0ddf956f715d..52e7ba54e435 100644 --- a/src/vs/workbench/common/theme.ts +++ b/src/vs/workbench/common/theme.ts @@ -565,11 +565,12 @@ export const PROFILE_BADGE_FOREGROUND = registerColor('profileBadge.foreground', hcLight: Color.white }, localize('profileBadgeForeground', "Profile badge foreground color. The profile badge shows on top of the settings gear icon in the activity bar.")); + // < --- Remote --- > -export const STATUS_BAR_REMOTE_ITEM_BACKGROUND = registerColor('statusBarItem.remoteBackground', ACTIVITY_BAR_BADGE_BACKGROUND, localize('statusBarItemHostBackground', "Background color for the remote indicator on the status bar.")); +export const STATUS_BAR_REMOTE_ITEM_BACKGROUND = registerColor('statusBarItem.remoteBackground', ACTIVITY_BAR_BADGE_BACKGROUND, localize('statusBarItemRemoteBackground', "Background color for the remote indicator on the status bar.")); -export const STATUS_BAR_REMOTE_ITEM_FOREGROUND = registerColor('statusBarItem.remoteForeground', ACTIVITY_BAR_BADGE_FOREGROUND, localize('statusBarItemHostForeground', "Foreground color for the remote indicator on the status bar.")); +export const STATUS_BAR_REMOTE_ITEM_FOREGROUND = registerColor('statusBarItem.remoteForeground', ACTIVITY_BAR_BADGE_FOREGROUND, localize('statusBarItemRemoteForeground', "Foreground color for the remote indicator on the status bar.")); export const STATUS_BAR_REMOTE_ITEM_HOVER_FOREGROUND = registerColor('statusBarItem.remoteHoverForeground', STATUS_BAR_ITEM_HOVER_FOREGROUND, localize('statusBarRemoteItemHoverForeground', "Foreground color for the remote indicator on the status bar when hovering.")); @@ -800,11 +801,11 @@ export const WINDOW_ACTIVE_BORDER = registerColor('window.activeBorder', { light: null, hcDark: contrastBorder, hcLight: contrastBorder -}, localize('windowActiveBorder', "The color used for the border of the window when it is active. Only supported in the macOS and Linux desktop client when using the custom title bar.")); +}, localize('windowActiveBorder', "The color used for the border of the window when it is active on macOS or Linux. Requires custom title bar style and custom or hidden window controls on Linux.")); export const WINDOW_INACTIVE_BORDER = registerColor('window.inactiveBorder', { dark: null, light: null, hcDark: contrastBorder, hcLight: contrastBorder -}, localize('windowInactiveBorder', "The color used for the border of the window when it is inactive. Only supported in the macOS and Linux desktop client when using the custom title bar.")); +}, localize('windowInactiveBorder', "The color used for the border of the window when it is inactive on macOS or Linux. Requires custom title bar style and custom or hidden window controls on Linux.")); diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts index 19866e023387..6a03116343f4 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibilityConfiguration.ts @@ -728,7 +728,12 @@ const configuration: IConfigurationNode = { enum: ['none', 'input', 'lastExecution'], default: 'input', description: localize('replEditor.autoFocusAppendedCell', "Control whether focus should automatically be sent to the REPL when code is executed."), - } + }, + 'accessibility.windowTitleOptimized': { + 'type': 'boolean', + 'default': true, + 'markdownDescription': localize('accessibility.windowTitleOptimized', "Controls whether the {0} should be optimized for screen readers when in screen reader mode. When enabled, the window title will have {1} appended to the end.", '`#window.title#`', '`activeEditorState`') + }, } }; diff --git a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts index fde082ed5d56..6e000778220c 100644 --- a/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts +++ b/src/vs/workbench/contrib/accessibility/browser/accessibleView.ts @@ -24,7 +24,7 @@ import { IModelService } from '../../../../editor/common/services/model.js'; import { AccessibilityHelpNLS } from '../../../../editor/common/standaloneStrings.js'; import { CodeActionController } from '../../../../editor/contrib/codeAction/browser/codeActionController.js'; import { localize } from '../../../../nls.js'; -import { AccessibleViewProviderId, AccessibleViewType, AccessibleContentProvider, ExtensionContentProvider, IAccessibleViewService, IAccessibleViewSymbol } from '../../../../platform/accessibility/browser/accessibleView.js'; +import { AccessibleViewProviderId, AccessibleViewType, AccessibleContentProvider, ExtensionContentProvider, IAccessibleViewService, IAccessibleViewSymbol, isIAccessibleViewContentProvider } from '../../../../platform/accessibility/browser/accessibleView.js'; import { ACCESSIBLE_VIEW_SHOWN_STORAGE_PREFIX, IAccessibilityService } from '../../../../platform/accessibility/common/accessibility.js'; import { getFlatActionBarActions } from '../../../../platform/actions/browser/menuEntryActionViewItem.js'; import { WorkbenchToolBar } from '../../../../platform/actions/browser/toolbar.js'; @@ -170,7 +170,7 @@ export class AccessibleView extends Disposable implements ITextModelContentProvi } })); this._register(this._configurationService.onDidChangeConfiguration(e => { - if (this._currentProvider instanceof AccessibleContentProvider && e.affectsConfiguration(this._currentProvider.verbositySettingKey)) { + if (isIAccessibleViewContentProvider(this._currentProvider) && e.affectsConfiguration(this._currentProvider.verbositySettingKey)) { if (this._accessiblityHelpIsShown.get()) { this.show(this._currentProvider); } @@ -345,7 +345,7 @@ export class AccessibleView extends Disposable implements ITextModelContentProvi if (!this._currentProvider) { return false; } - return this._currentProvider instanceof AccessibleContentProvider ? this._configurationService.getValue(this._currentProvider.verbositySettingKey) === true : this._storageService.getBoolean(`${ACCESSIBLE_VIEW_SHOWN_STORAGE_PREFIX}${this._currentProvider.id}`, StorageScope.APPLICATION, false); + return isIAccessibleViewContentProvider(this._currentProvider) ? this._configurationService.getValue(this._currentProvider.verbositySettingKey) === true : this._storageService.getBoolean(`${ACCESSIBLE_VIEW_SHOWN_STORAGE_PREFIX}${this._currentProvider.id}`, StorageScope.APPLICATION, false); } goToSymbol(): void { @@ -502,7 +502,7 @@ export class AccessibleView extends Disposable implements ITextModelContentProvi } disableHint(): void { - if (!(this._currentProvider instanceof AccessibleContentProvider)) { + if (!isIAccessibleViewContentProvider(this._currentProvider)) { return; } this._configurationService.updateValue(this._currentProvider?.verbositySettingKey, false); diff --git a/src/vs/workbench/contrib/accessibility/browser/editorAccessibilityHelp.ts b/src/vs/workbench/contrib/accessibility/browser/editorAccessibilityHelp.ts index 0bffd4614283..5f993a6a6d86 100644 --- a/src/vs/workbench/contrib/accessibility/browser/editorAccessibilityHelp.ts +++ b/src/vs/workbench/contrib/accessibility/browser/editorAccessibilityHelp.ts @@ -20,6 +20,8 @@ import { NEW_UNTITLED_FILE_COMMAND_ID } from '../../files/browser/fileConstants. import { IAccessibleViewService, IAccessibleViewContentProvider, AccessibleViewProviderId, IAccessibleViewOptions, AccessibleViewType } from '../../../../platform/accessibility/browser/accessibleView.js'; import { AccessibilityVerbositySettingId } from './accessibilityConfiguration.js'; import { ctxHasEditorModification, ctxHasRequestInProgress } from '../../chat/browser/chatEditing/chatEditingEditorContextKeys.js'; +import { IAccessibilityService } from '../../../../platform/accessibility/common/accessibility.js'; +import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; export class EditorAccessibilityHelpContribution extends Disposable { static ID: 'editorAccessibilityHelpContribution'; @@ -50,7 +52,9 @@ class EditorAccessibilityHelpProvider extends Disposable implements IAccessibleV constructor( private readonly _editor: ICodeEditor, @IKeybindingService private readonly _keybindingService: IKeybindingService, - @IContextKeyService private readonly _contextKeyService: IContextKeyService + @IContextKeyService private readonly _contextKeyService: IContextKeyService, + @IAccessibilityService private readonly accessibilityService: IAccessibilityService, + @IConfigurationService private readonly _configurationService: IConfigurationService, ) { super(); } @@ -72,7 +76,11 @@ class EditorAccessibilityHelpProvider extends Disposable implements IAccessibleV content.push(AccessibilityHelpNLS.editableEditor); } } - content.push(AccessibilityHelpNLS.activeEditorState); + if (this.accessibilityService.isScreenReaderOptimized() && this._configurationService.getValue('accessibility.windowTitleOptimized')) { + content.push(AccessibilityHelpNLS.defaultWindowTitleIncludesEditorState); + } else { + content.push(AccessibilityHelpNLS.defaultWindowTitleExcludingEditorState); + } content.push(AccessibilityHelpNLS.toolbar); const chatEditInfo = getChatEditInfo(this._keybindingService, this._contextKeyService, this._editor); diff --git a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts index 20566aac91b3..267b9be32f59 100644 --- a/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts +++ b/src/vs/workbench/contrib/bulkEdit/browser/preview/bulkEditPane.ts @@ -30,7 +30,6 @@ import { ILabelService } from '../../../../../platform/label/common/label.js'; import { IOpenEvent, WorkbenchAsyncDataTree } from '../../../../../platform/list/browser/listService.js'; import { IOpenerService } from '../../../../../platform/opener/common/opener.js'; import { IStorageService, StorageScope, StorageTarget } from '../../../../../platform/storage/common/storage.js'; -import { ITelemetryService } from '../../../../../platform/telemetry/common/telemetry.js'; import { defaultButtonStyles } from '../../../../../platform/theme/browser/defaultStyles.js'; import { IThemeService } from '../../../../../platform/theme/common/themeService.js'; import { ResourceLabels } from '../../../../browser/labels.js'; @@ -89,24 +88,17 @@ export class BulkEditPane extends ViewPane { @IConfigurationService configurationService: IConfigurationService, @IOpenerService openerService: IOpenerService, @IThemeService themeService: IThemeService, - @ITelemetryService telemetryService: ITelemetryService, @IHoverService hoverService: IHoverService, ) { super( { ...options, titleMenuId: MenuId.BulkEditTitle }, - keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, _instaService, openerService, themeService, telemetryService, hoverService + keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, _instaService, openerService, themeService, hoverService ); this.element.classList.add('bulk-edit-panel', 'show-file-icons'); this._ctxHasCategories = BulkEditPane.ctxHasCategories.bindTo(contextKeyService); this._ctxGroupByFile = BulkEditPane.ctxGroupByFile.bindTo(contextKeyService); this._ctxHasCheckedChanges = BulkEditPane.ctxHasCheckedChanges.bindTo(contextKeyService); - // telemetry - type BulkEditPaneOpened = { - owner: 'aiday-mar'; - comment: 'Report when the bulk edit pane has been opened'; - }; - this.telemetryService.publicLog2<{}, BulkEditPaneOpened>('views.bulkEditPane'); } override dispose(): void { diff --git a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.ts b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.ts index 75da6466a36d..0afedce845a5 100644 --- a/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.ts +++ b/src/vs/workbench/contrib/callHierarchy/browser/callHierarchyPeek.ts @@ -96,7 +96,7 @@ export class CallHierarchyTreePeekWidget extends peekView.PeekViewWidget { this.create(); this._peekViewService.addExclusiveWidget(editor, this); this._applyTheme(themeService.getColorTheme()); - this._disposables.add(themeService.onDidColorThemeChange(this._applyTheme, this)); + this._disposables.add(themeService.onDidColorThemeChange(e => this._applyTheme(e.theme))); this._disposables.add(this._previewDisposable); } diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp.ts b/src/vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp.ts index 84531e48c77c..2ef911af34d7 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatAccessibilityHelp.ts @@ -12,18 +12,17 @@ import { AccessibleContentProvider, AccessibleViewProviderId, AccessibleViewType import { IAccessibleViewImplementation } from '../../../../../platform/accessibility/browser/accessibleViewRegistry.js'; import { ContextKeyExpr } from '../../../../../platform/contextkey/common/contextkey.js'; import { IKeybindingService } from '../../../../../platform/keybinding/common/keybinding.js'; -import { ActiveAuxiliaryContext } from '../../../../common/contextkeys.js'; import { AccessibilityVerbositySettingId } from '../../../accessibility/browser/accessibilityConfiguration.js'; import { INLINE_CHAT_ID } from '../../../inlineChat/common/inlineChat.js'; -import { ChatAgentLocation } from '../../common/chatAgents.js'; -import { ChatContextKeys } from '../../common/chatContextKeys.js'; +import { ChatContextKeyExprs, ChatContextKeys } from '../../common/chatContextKeys.js'; +import { ChatAgentLocation, ChatMode } from '../../common/constants.js'; import { IChatWidgetService } from '../chat.js'; export class PanelChatAccessibilityHelp implements IAccessibleViewImplementation { readonly priority = 107; readonly name = 'panelChat'; readonly type = AccessibleViewType.Help; - readonly when = ContextKeyExpr.and(ChatContextKeys.location.isEqualTo(ChatAgentLocation.Panel), ChatContextKeys.inQuickChat.negate(), ActiveAuxiliaryContext.isEqualTo('workbench.panel.chat'), ContextKeyExpr.or(ChatContextKeys.inChatSession, ChatContextKeys.isResponse, ChatContextKeys.isRequest)); + readonly when = ContextKeyExpr.and(ChatContextKeys.location.isEqualTo(ChatAgentLocation.Panel), ChatContextKeys.inQuickChat.negate(), ChatContextKeys.chatMode.isEqualTo(ChatMode.Chat), ContextKeyExpr.or(ChatContextKeys.inChatSession, ChatContextKeys.isResponse, ChatContextKeys.isRequest)); getProvider(accessor: ServicesAccessor) { const codeEditor = accessor.get(ICodeEditorService).getActiveCodeEditor() || accessor.get(ICodeEditorService).getFocusedCodeEditor(); return getChatAccessibilityHelpProvider(accessor, codeEditor ?? undefined, 'panelChat'); @@ -45,7 +44,7 @@ export class EditsChatAccessibilityHelp implements IAccessibleViewImplementation readonly priority = 119; readonly name = 'editsView'; readonly type = AccessibleViewType.Help; - readonly when = ContextKeyExpr.and(ActiveAuxiliaryContext.isEqualTo('workbench.panel.chatEditing'), ChatContextKeys.inChatInput); + readonly when = ContextKeyExpr.and(ChatContextKeyExprs.inEditingMode, ChatContextKeys.inChatInput); getProvider(accessor: ServicesAccessor) { const codeEditor = accessor.get(ICodeEditorService).getActiveCodeEditor() || accessor.get(ICodeEditorService).getFocusedCodeEditor(); return getChatAccessibilityHelpProvider(accessor, codeEditor ?? undefined, 'editsView'); @@ -79,7 +78,7 @@ export function getAccessibilityHelpText(type: 'panelChat' | 'inlineChat' | 'qui content.push(localize('chatEditing.expectation', 'When a request is made, a progress indicator will play while the edits are being applied.')); content.push(localize('chatEditing.review', 'Once the edits are applied, a sound will play to indicate the document has been opened and is ready for review. The sound can be disabled with accessibility.signals.chatEditModifiedFile.')); content.push(localize('chatEditing.sections', 'Navigate between edits in the editor with navigate previous{0} and next{1}', '', '')); - content.push(localize('chatEditing.acceptHunk', 'In the editor, Accept{0}, Reject{1}, or Toggle the Diff{2} for the current Change.', '', '', '')); + content.push(localize('chatEditing.acceptHunk', 'In the editor, Accept{0}, Reject{1}, or Toggle the Diff{2} for the current Change.', '', '', '')); content.push(localize('chatEditing.helpfulCommands', 'When in the edits view, some helpful commands include:')); content.push(localize('workbench.action.chat.undoEdits', '- Undo Edits{0}.', '')); content.push(localize('workbench.action.chat.editing.attachFiles', '- Attach Files{0}.', '')); @@ -89,7 +88,6 @@ export function getAccessibilityHelpText(type: 'panelChat' | 'inlineChat' | 'qui content.push(localize('chatEditing.acceptAllFiles', '- Accept All Edits{0}.', '')); content.push(localize('chatEditing.discardAllFiles', '- Discard All Edits{0}.', '')); content.push(localize('chatEditing.openFileInDiff', '- Open File in Diff{0}.', '')); - content.push(localize('chatEditing.addFileToWorkingSet', '- Add File to Working Set{0}.', '')); content.push(localize('chatEditing.viewChanges', '- View Changes{0}.', '')); } else { diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts index fff8f932e20b..e416d5d651f7 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts @@ -6,23 +6,27 @@ import { toAction } from '../../../../../base/common/actions.js'; import { coalesce } from '../../../../../base/common/arrays.js'; import { Codicon } from '../../../../../base/common/codicons.js'; -import { fromNowByDay } from '../../../../../base/common/date.js'; +import { fromNowByDay, safeIntl } from '../../../../../base/common/date.js'; import { Event } from '../../../../../base/common/event.js'; +import { MarkdownString } from '../../../../../base/common/htmlContent.js'; import { KeyCode, KeyMod } from '../../../../../base/common/keyCodes.js'; import { Disposable, DisposableStore, markAsSingleton } from '../../../../../base/common/lifecycle.js'; +import { language } from '../../../../../base/common/platform.js'; import { ThemeIcon } from '../../../../../base/common/themables.js'; import { URI } from '../../../../../base/common/uri.js'; import { ICodeEditor } from '../../../../../editor/browser/editorBrowser.js'; import { EditorAction2 } from '../../../../../editor/browser/editorExtensions.js'; import { Position } from '../../../../../editor/common/core/position.js'; import { SuggestController } from '../../../../../editor/contrib/suggest/browser/suggestController.js'; -import { ILocalizedString, localize, localize2 } from '../../../../../nls.js'; +import { localize, localize2 } from '../../../../../nls.js'; import { IActionViewItemService } from '../../../../../platform/actions/browser/actionViewItemService.js'; import { DropdownWithPrimaryActionViewItem } from '../../../../../platform/actions/browser/dropdownWithPrimaryActionViewItem.js'; import { Action2, MenuId, MenuItemAction, MenuRegistry, registerAction2, SubmenuItemAction } from '../../../../../platform/actions/common/actions.js'; import { ICommandService } from '../../../../../platform/commands/common/commands.js'; -import { ContextKeyExpr, ContextKeyExpression, IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js'; +import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; +import { ContextKeyExpr } from '../../../../../platform/contextkey/common/contextkey.js'; import { IsLinuxContext, IsWindowsContext } from '../../../../../platform/contextkey/common/contextkeys.js'; +import { IDialogService } from '../../../../../platform/dialogs/common/dialogs.js'; import { IInstantiationService, ServicesAccessor } from '../../../../../platform/instantiation/common/instantiation.js'; import { KeybindingWeight } from '../../../../../platform/keybinding/common/keybindingsRegistry.js'; import { IOpenerService } from '../../../../../platform/opener/common/opener.js'; @@ -30,29 +34,35 @@ import product from '../../../../../platform/product/common/product.js'; import { IQuickInputButton, IQuickInputService, IQuickPickItem, IQuickPickSeparator } from '../../../../../platform/quickinput/common/quickInput.js'; import { ToggleTitleBarConfigAction } from '../../../../browser/parts/titlebar/titlebarActions.js'; import { IWorkbenchContribution } from '../../../../common/contributions.js'; +import { IViewDescriptorService, ViewContainerLocation } from '../../../../common/views.js'; import { IEditorGroupsService } from '../../../../services/editor/common/editorGroupsService.js'; import { ACTIVE_GROUP, IEditorService } from '../../../../services/editor/common/editorService.js'; import { IHostService } from '../../../../services/host/browser/host.js'; +import { IWorkbenchLayoutService, Parts } from '../../../../services/layout/browser/layoutService.js'; import { IViewsService } from '../../../../services/views/common/viewsService.js'; import { EXTENSIONS_CATEGORY, IExtensionsWorkbenchService } from '../../../extensions/common/extensions.js'; -import { ChatAgentLocation, IChatAgentService } from '../../common/chatAgents.js'; +import { IChatAgentService } from '../../common/chatAgents.js'; import { ChatContextKeys } from '../../common/chatContextKeys.js'; +import { ChatEntitlement, ChatSentiment, IChatEntitlementService } from '../../common/chatEntitlementService.js'; import { extractAgentAndCommand } from '../../common/chatParserTypes.js'; import { IChatDetail, IChatService } from '../../common/chatService.js'; import { IChatRequestViewModel, IChatResponseViewModel, isRequestVM } from '../../common/chatViewModel.js'; import { IChatWidgetHistoryService } from '../../common/chatWidgetHistoryService.js'; +import { ChatMode } from '../../common/constants.js'; import { CopilotUsageExtensionFeatureId } from '../../common/languageModelStats.js'; import { ILanguageModelToolsService } from '../../common/languageModelToolsService.js'; -import { ChatViewId, IChatWidget, IChatWidgetService, showChatView } from '../chat.js'; +import { ChatViewId, EditsViewId, IChatWidget, IChatWidgetService, showChatView, showCopilotView } from '../chat.js'; import { IChatEditorOptions } from '../chatEditor.js'; import { ChatEditorInput } from '../chatEditorInput.js'; -import { IChatQuotasService, OPEN_CHAT_QUOTA_EXCEEDED_DIALOG, quotaToButtonMessage } from '../chatQuotasService.js'; import { ChatViewPane } from '../chatViewPane.js'; import { convertBufferToScreenshotVariable } from '../contrib/screenshot.js'; import { clearChatEditor } from './chatClear.js'; export const CHAT_CATEGORY = localize2('chat.category', 'Chat'); + export const CHAT_OPEN_ACTION_ID = 'workbench.action.chat.open'; +export const CHAT_SETUP_ACTION_ID = 'workbench.action.chat.triggerSetup'; +const TOGGLE_CHAT_ACTION_ID = 'workbench.action.chat.toggle'; export interface IChatViewOpenOptions { /** @@ -83,269 +93,302 @@ export interface IChatViewOpenRequestEntry { response: string; } -class OpenChatGlobalAction extends Action2 { +export const OPEN_CHAT_QUOTA_EXCEEDED_DIALOG = 'workbench.action.chat.openQuotaExceededDialog'; - static readonly TITLE = localize2('openChat', "Open Chat"); +export function registerChatActions() { + registerAction2(class OpenChatGlobalAction extends Action2 { - constructor() { - super({ - id: CHAT_OPEN_ACTION_ID, - title: OpenChatGlobalAction.TITLE, - icon: Codicon.copilot, - f1: true, - category: CHAT_CATEGORY, - keybinding: { - weight: KeybindingWeight.WorkbenchContrib, - primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KeyI, - mac: { - primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KeyI + constructor() { + super({ + id: CHAT_OPEN_ACTION_ID, + title: localize2('openChat', "Open Chat"), + icon: Codicon.copilot, + f1: true, + category: CHAT_CATEGORY, + precondition: ChatContextKeys.Setup.hidden.toNegated(), + keybinding: { + weight: KeybindingWeight.WorkbenchContrib, + primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KeyI, + mac: { + primary: KeyMod.CtrlCmd | KeyMod.WinCtrl | KeyCode.KeyI + } + }, + menu: { + id: MenuId.ChatTitleBarMenu, + group: 'a_open', + order: 1 } - }, - menu: { - id: MenuId.ChatTitleBarMenu, - group: 'a_open', - order: 1 - } - }); - } + }); + } - override async run(accessor: ServicesAccessor, opts?: string | IChatViewOpenOptions): Promise { - opts = typeof opts === 'string' ? { query: opts } : opts; + override async run(accessor: ServicesAccessor, opts?: string | IChatViewOpenOptions): Promise { + opts = typeof opts === 'string' ? { query: opts } : opts; - const chatService = accessor.get(IChatService); - const toolsService = accessor.get(ILanguageModelToolsService); - const viewsService = accessor.get(IViewsService); - const hostService = accessor.get(IHostService); + const chatService = accessor.get(IChatService); + const toolsService = accessor.get(ILanguageModelToolsService); + const viewsService = accessor.get(IViewsService); + const hostService = accessor.get(IHostService); - const chatWidget = await showChatView(viewsService); - if (!chatWidget) { - return; - } - if (opts?.previousRequests?.length && chatWidget.viewModel) { - for (const { request, response } of opts.previousRequests) { - chatService.addCompleteRequest(chatWidget.viewModel.sessionId, request, undefined, 0, { message: response }); + const chatWidget = await showChatView(viewsService); + if (!chatWidget) { + return; } - } - if (opts?.attachScreenshot) { - const screenshot = await hostService.getScreenshot(); - if (screenshot) { - chatWidget.attachmentModel.addContext(convertBufferToScreenshotVariable(screenshot)); + if (opts?.previousRequests?.length && chatWidget.viewModel) { + for (const { request, response } of opts.previousRequests) { + chatService.addCompleteRequest(chatWidget.viewModel.sessionId, request, undefined, 0, { message: response }); + } } - } - if (opts?.query) { - if (opts.isPartialQuery) { - chatWidget.setInput(opts.query); - } else { - chatWidget.acceptInput(opts.query); + if (opts?.attachScreenshot) { + const screenshot = await hostService.getScreenshot(); + if (screenshot) { + chatWidget.attachmentModel.addContext(convertBufferToScreenshotVariable(screenshot)); + } } - } - if (opts?.toolIds && opts.toolIds.length > 0) { - for (const toolId of opts.toolIds) { - const tool = toolsService.getTool(toolId); - if (tool) { - chatWidget.attachmentModel.addContext({ - id: tool.id, - name: tool.displayName, - fullName: tool.displayName, - value: undefined, - icon: ThemeIcon.isThemeIcon(tool.icon) ? tool.icon : undefined, - isTool: true - }); + if (opts?.query) { + if (opts.isPartialQuery) { + chatWidget.setInput(opts.query); + } else { + chatWidget.acceptInput(opts.query); + } + } + if (opts?.toolIds && opts.toolIds.length > 0) { + for (const toolId of opts.toolIds) { + const tool = toolsService.getTool(toolId); + if (tool) { + chatWidget.attachmentModel.addContext({ + id: tool.id, + name: tool.displayName, + fullName: tool.displayName, + value: undefined, + icon: ThemeIcon.isThemeIcon(tool.icon) ? tool.icon : undefined, + isTool: true + }); + } } } - } - chatWidget.focusInput(); - } -} + chatWidget.focusInput(); + } + }); -class ChatHistoryAction extends Action2 { - constructor() { - super({ - id: `workbench.action.chat.history`, - title: localize2('chat.history.label', "Show Chats..."), - menu: { - id: MenuId.ViewTitle, - when: ContextKeyExpr.equals('view', ChatViewId), - group: 'navigation', - order: 2 - }, - category: CHAT_CATEGORY, - icon: Codicon.history, - f1: true, - precondition: ChatContextKeys.enabled - }); - } + registerAction2(class ToggleChatAction extends Action2 { + constructor() { + super({ + id: TOGGLE_CHAT_ACTION_ID, + title: localize2('toggleChat', "Toggle Chat"), + category: CHAT_CATEGORY + }); + } - async run(accessor: ServicesAccessor) { - const chatService = accessor.get(IChatService); - const quickInputService = accessor.get(IQuickInputService); - const viewsService = accessor.get(IViewsService); - const editorService = accessor.get(IEditorService); + async run(accessor: ServicesAccessor) { + const layoutService = accessor.get(IWorkbenchLayoutService); + const viewsService = accessor.get(IViewsService); + const viewDescriptorService = accessor.get(IViewDescriptorService); - const showPicker = () => { - const openInEditorButton: IQuickInputButton = { - iconClass: ThemeIcon.asClassName(Codicon.file), - tooltip: localize('interactiveSession.history.editor', "Open in Editor"), - }; - const deleteButton: IQuickInputButton = { - iconClass: ThemeIcon.asClassName(Codicon.x), - tooltip: localize('interactiveSession.history.delete', "Delete"), - }; - const renameButton: IQuickInputButton = { - iconClass: ThemeIcon.asClassName(Codicon.pencil), - tooltip: localize('chat.history.rename', "Rename"), - }; + const chatLocation = viewDescriptorService.getViewLocationById(ChatViewId); + const editsLocation = viewDescriptorService.getViewLocationById(EditsViewId); - interface IChatPickerItem extends IQuickPickItem { - chat: IChatDetail; + if (viewsService.isViewVisible(ChatViewId) || (chatLocation === editsLocation && viewsService.isViewVisible(EditsViewId))) { + this.updatePartVisibility(layoutService, chatLocation, false); + } else { + this.updatePartVisibility(layoutService, chatLocation, true); + (await showCopilotView(viewsService, layoutService))?.focusInput(); } + } - const getPicks = () => { - const items = chatService.getHistory(); - items.sort((a, b) => (b.lastMessageDate ?? 0) - (a.lastMessageDate ?? 0)); - - let lastDate: string | undefined = undefined; - const picks = items.flatMap((i): [IQuickPickSeparator | undefined, IChatPickerItem] => { - const timeAgoStr = fromNowByDay(i.lastMessageDate, true, true); - const separator: IQuickPickSeparator | undefined = timeAgoStr !== lastDate ? { - type: 'separator', label: timeAgoStr, - } : undefined; - lastDate = timeAgoStr; - return [ - separator, - { - label: i.title, - description: i.isActive ? `(${localize('currentChatLabel', 'current')})` : '', - chat: i, - buttons: i.isActive ? [renameButton] : [ - renameButton, - openInEditorButton, - deleteButton, - ] - } - ]; - }); + private updatePartVisibility(layoutService: IWorkbenchLayoutService, location: ViewContainerLocation | null, visible: boolean): void { + let part: Parts.PANEL_PART | Parts.SIDEBAR_PART | Parts.AUXILIARYBAR_PART | undefined; + switch (location) { + case ViewContainerLocation.Panel: + part = Parts.PANEL_PART; + break; + case ViewContainerLocation.Sidebar: + part = Parts.SIDEBAR_PART; + break; + case ViewContainerLocation.AuxiliaryBar: + part = Parts.AUXILIARYBAR_PART; + break; + } - return coalesce(picks); - }; + if (part) { + layoutService.setPartHidden(!visible, part); + } + } + }); - const store = new DisposableStore(); - const picker = store.add(quickInputService.createQuickPick({ useSeparators: true })); - picker.placeholder = localize('interactiveSession.history.pick', "Switch to chat"); - const picks = getPicks(); - picker.items = picks; - store.add(picker.onDidTriggerItemButton(async context => { - if (context.button === openInEditorButton) { - const options: IChatEditorOptions = { target: { sessionId: context.item.chat.sessionId }, pinned: true }; - editorService.openEditor({ resource: ChatEditorInput.getNewEditorUri(), options }, ACTIVE_GROUP); - picker.hide(); - } else if (context.button === deleteButton) { - chatService.removeHistoryEntry(context.item.chat.sessionId); - picker.items = getPicks(); - } else if (context.button === renameButton) { - const title = await quickInputService.input({ title: localize('newChatTitle', "New chat title"), value: context.item.chat.title }); - if (title) { - chatService.setChatSessionTitle(context.item.chat.sessionId, title); - } + registerAction2(class ChatHistoryAction extends Action2 { + constructor() { + super({ + id: `workbench.action.chat.history`, + title: localize2('chat.history.label', "Show Chats..."), + menu: { + id: MenuId.ViewTitle, + when: ContextKeyExpr.equals('view', ChatViewId), + group: 'navigation', + order: 2 + }, + category: CHAT_CATEGORY, + icon: Codicon.history, + f1: true, + precondition: ChatContextKeys.enabled + }); + } - // The quick input hides the picker, it gets disposed, so we kick it off from scratch - showPicker(); - } - })); - store.add(picker.onDidAccept(async () => { - try { - const item = picker.selectedItems[0]; - const sessionId = item.chat.sessionId; - const view = await viewsService.openView(ChatViewId) as ChatViewPane; - view.loadSession(sessionId); - } finally { - picker.hide(); + async run(accessor: ServicesAccessor) { + const chatService = accessor.get(IChatService); + const quickInputService = accessor.get(IQuickInputService); + const viewsService = accessor.get(IViewsService); + const editorService = accessor.get(IEditorService); + + const showPicker = () => { + const openInEditorButton: IQuickInputButton = { + iconClass: ThemeIcon.asClassName(Codicon.file), + tooltip: localize('interactiveSession.history.editor', "Open in Editor"), + }; + const deleteButton: IQuickInputButton = { + iconClass: ThemeIcon.asClassName(Codicon.x), + tooltip: localize('interactiveSession.history.delete', "Delete"), + }; + const renameButton: IQuickInputButton = { + iconClass: ThemeIcon.asClassName(Codicon.pencil), + tooltip: localize('chat.history.rename', "Rename"), + }; + + interface IChatPickerItem extends IQuickPickItem { + chat: IChatDetail; } - })); - store.add(picker.onDidHide(() => store.dispose())); - picker.show(); - }; - showPicker(); - } -} + const getPicks = () => { + const items = chatService.getHistory(); + items.sort((a, b) => (b.lastMessageDate ?? 0) - (a.lastMessageDate ?? 0)); + + let lastDate: string | undefined = undefined; + const picks = items.flatMap((i): [IQuickPickSeparator | undefined, IChatPickerItem] => { + const timeAgoStr = fromNowByDay(i.lastMessageDate, true, true); + const separator: IQuickPickSeparator | undefined = timeAgoStr !== lastDate ? { + type: 'separator', label: timeAgoStr, + } : undefined; + lastDate = timeAgoStr; + return [ + separator, + { + label: i.title, + description: i.isActive ? `(${localize('currentChatLabel', 'current')})` : '', + chat: i, + buttons: i.isActive ? [renameButton] : [ + renameButton, + openInEditorButton, + deleteButton, + ] + } + ]; + }); -class OpenChatEditorAction extends Action2 { - constructor() { - super({ - id: `workbench.action.openChat`, - title: localize2('interactiveSession.open', "Open Editor"), - f1: true, - category: CHAT_CATEGORY, - precondition: ChatContextKeys.enabled - }); - } + return coalesce(picks); + }; + + const store = new DisposableStore(); + const picker = store.add(quickInputService.createQuickPick({ useSeparators: true })); + picker.placeholder = localize('interactiveSession.history.pick', "Switch to chat"); + const picks = getPicks(); + picker.items = picks; + store.add(picker.onDidTriggerItemButton(async context => { + if (context.button === openInEditorButton) { + const options: IChatEditorOptions = { target: { sessionId: context.item.chat.sessionId }, pinned: true }; + editorService.openEditor({ resource: ChatEditorInput.getNewEditorUri(), options }, ACTIVE_GROUP); + picker.hide(); + } else if (context.button === deleteButton) { + chatService.removeHistoryEntry(context.item.chat.sessionId); + picker.items = getPicks(); + } else if (context.button === renameButton) { + const title = await quickInputService.input({ title: localize('newChatTitle', "New chat title"), value: context.item.chat.title }); + if (title) { + chatService.setChatSessionTitle(context.item.chat.sessionId, title); + } - async run(accessor: ServicesAccessor) { - const editorService = accessor.get(IEditorService); - await editorService.openEditor({ resource: ChatEditorInput.getNewEditorUri(), options: { pinned: true } satisfies IChatEditorOptions }); - } -} + // The quick input hides the picker, it gets disposed, so we kick it off from scratch + showPicker(); + } + })); + store.add(picker.onDidAccept(async () => { + try { + const item = picker.selectedItems[0]; + const sessionId = item.chat.sessionId; + const view = await viewsService.openView(ChatViewId) as ChatViewPane; + view.loadSession(sessionId); + } finally { + picker.hide(); + } + })); + store.add(picker.onDidHide(() => store.dispose())); + picker.show(); + }; + showPicker(); + } + }); -class ChatAddAction extends Action2 { - constructor() { - super({ - id: 'workbench.action.chat.addParticipant', - title: localize2('chatWith', "Chat with Extension"), - icon: Codicon.mention, - f1: false, - category: CHAT_CATEGORY, - menu: { - id: MenuId.ChatInput, - when: ChatContextKeys.location.isEqualTo(ChatAgentLocation.Panel), - group: 'navigation', - order: 1 - } - }); - } + registerAction2(class OpenChatEditorAction extends Action2 { + constructor() { + super({ + id: `workbench.action.openChat`, + title: localize2('interactiveSession.open', "Open Editor"), + f1: true, + category: CHAT_CATEGORY, + precondition: ChatContextKeys.enabled + }); + } - override async run(accessor: ServicesAccessor, ...args: any[]): Promise { - const widgetService = accessor.get(IChatWidgetService); - const context: { widget?: IChatWidget } | undefined = args[0]; - const widget = context?.widget ?? widgetService.lastFocusedWidget; - if (!widget) { - return; + async run(accessor: ServicesAccessor) { + const editorService = accessor.get(IEditorService); + await editorService.openEditor({ resource: ChatEditorInput.getNewEditorUri(), options: { pinned: true } satisfies IChatEditorOptions }); } + }); + - const hasAgentOrCommand = extractAgentAndCommand(widget.parsedInput); - if (hasAgentOrCommand?.agentPart || hasAgentOrCommand?.commandPart) { - return; + registerAction2(class ChatAddAction extends Action2 { + constructor() { + super({ + id: 'workbench.action.chat.addParticipant', + title: localize2('chatWith', "Chat with Extension"), + icon: Codicon.mention, + f1: false, + category: CHAT_CATEGORY, + menu: { + id: MenuId.ChatInput, + when: ChatContextKeys.chatMode.isEqualTo(ChatMode.Chat), + group: 'navigation', + order: 1 + } + }); } - const suggestCtrl = SuggestController.get(widget.inputEditor); - if (suggestCtrl) { - const curText = widget.inputEditor.getValue(); - const newValue = curText ? `@ ${curText}` : '@'; - if (!curText.startsWith('@')) { - widget.inputEditor.setValue(newValue); + override async run(accessor: ServicesAccessor, ...args: any[]): Promise { + const widgetService = accessor.get(IChatWidgetService); + const context: { widget?: IChatWidget } | undefined = args[0]; + const widget = context?.widget ?? widgetService.lastFocusedWidget; + if (!widget) { + return; } - widget.inputEditor.setPosition(new Position(1, 2)); - suggestCtrl.triggerSuggest(undefined, true); - } - } -} + const hasAgentOrCommand = extractAgentAndCommand(widget.parsedInput); + if (hasAgentOrCommand?.agentPart || hasAgentOrCommand?.commandPart) { + return; + } -MenuRegistry.appendMenuItem(MenuId.ViewTitle, { - command: { - id: 'update.showCurrentReleaseNotes', - title: localize2('chat.releaseNotes.label', "Show Release Notes"), - }, - when: ContextKeyExpr.equals('view', ChatViewId) -}); + const suggestCtrl = SuggestController.get(widget.inputEditor); + if (suggestCtrl) { + const curText = widget.inputEditor.getValue(); + const newValue = curText ? `@ ${curText}` : '@'; + if (!curText.startsWith('@')) { + widget.inputEditor.setValue(newValue); + } -export function registerChatActions() { - registerAction2(OpenChatGlobalAction); - registerAction2(ChatHistoryAction); - registerAction2(OpenChatEditorAction); - registerAction2(ChatAddAction); + widget.inputEditor.setPosition(new Position(1, 2)); + suggestCtrl.triggerSuggest(undefined, true); + } + } + }); registerAction2(class ClearChatInputHistoryAction extends Action2 { constructor() { @@ -461,35 +504,35 @@ export function registerChatActions() { } }); - function registerOpenLinkAction(id: string, title: ILocalizedString, url: string, order: number, contextKey: ContextKeyExpression = ChatContextKeys.enabled): void { - registerAction2(class extends Action2 { - constructor() { - super({ - id, - title, - category: CHAT_CATEGORY, - f1: true, - precondition: contextKey, - menu: { - id: MenuId.ChatTitleBarMenu, - group: 'y_manage', - order, - when: contextKey - } - }); - } - - override async run(accessor: ServicesAccessor): Promise { - const openerService = accessor.get(IOpenerService); - openerService.open(URI.parse(url)); - } - }); - } + const nonEnterpriseCopilotUsers = ContextKeyExpr.and(ChatContextKeys.enabled, ContextKeyExpr.notEquals(`config.${defaultChat.completionsAdvancedSetting}.authProvider`, defaultChat.enterpriseProviderId)); + registerAction2(class extends Action2 { + constructor() { + super({ + id: 'workbench.action.chat.manageSettings', + title: localize2('manageCopilot', "Manage Copilot"), + category: CHAT_CATEGORY, + f1: true, + precondition: ContextKeyExpr.and( + ContextKeyExpr.or( + ChatContextKeys.Entitlement.limited, + ChatContextKeys.Entitlement.pro + ), + nonEnterpriseCopilotUsers + ), + menu: { + id: MenuId.ChatTitleBarMenu, + group: 'y_manage', + order: 1, + when: nonEnterpriseCopilotUsers + } + }); + } - const nonEnterpriseCopilotUsers = ContextKeyExpr.and(ChatContextKeys.enabled, ContextKeyExpr.notEquals(`config.${defaultChat.providerSetting}`, defaultChat.enterpriseProviderId)); - registerOpenLinkAction('workbench.action.chat.managePlan', localize2('managePlan', "Manage Copilot Plan"), defaultChat.managePlanUrl, 1, nonEnterpriseCopilotUsers); - registerOpenLinkAction('workbench.action.chat.manageSettings', localize2('manageSettings', "Manage Copilot Settings"), defaultChat.manageSettingsUrl, 2, nonEnterpriseCopilotUsers); - registerOpenLinkAction('workbench.action.chat.learnMore', localize2('learnMore', "Learn More"), defaultChat.documentationUrl, 3); + override async run(accessor: ServicesAccessor): Promise { + const openerService = accessor.get(IOpenerService); + openerService.open(URI.parse(defaultChat.manageSettingsUrl)); + } + }); registerAction2(class ShowExtensionsUsingCopilot extends Action2 { @@ -499,6 +542,7 @@ export function registerChatActions() { title: localize2('showCopilotUsageExtensions', "Show Extensions using Copilot"), f1: true, category: EXTENSIONS_CATEGORY, + precondition: ChatContextKeys.enabled }); } @@ -514,7 +558,7 @@ export function registerChatActions() { super({ id: 'workbench.action.chat.configureCodeCompletions', title: localize2('configureCompletions', "Configure Code Completions..."), - precondition: ChatContextKeys.enabled, + precondition: ChatContextKeys.Setup.installed, menu: { id: MenuId.ChatTitleBarMenu, group: 'f_completions', @@ -525,7 +569,59 @@ export function registerChatActions() { override async run(accessor: ServicesAccessor): Promise { const commandService = accessor.get(ICommandService); - commandService.executeCommand('github.copilot.toggleStatusMenu'); + commandService.executeCommand(defaultChat.completionsMenuCommand); + } + }); + + registerAction2(class ShowLimitReachedDialogAction extends Action2 { + + constructor() { + super({ + id: OPEN_CHAT_QUOTA_EXCEEDED_DIALOG, + title: localize('upgradeChat', "Upgrade to Copilot Pro") + }); + } + + override async run(accessor: ServicesAccessor) { + const chatEntitlementService = accessor.get(IChatEntitlementService); + const commandService = accessor.get(ICommandService); + const dialogService = accessor.get(IDialogService); + + const dateFormatter = safeIntl.DateTimeFormat(language, { year: 'numeric', month: 'long', day: 'numeric' }); + + let message: string; + const { chatQuotaExceeded, completionsQuotaExceeded } = chatEntitlementService.quotas; + if (chatQuotaExceeded && !completionsQuotaExceeded) { + message = localize('chatQuotaExceeded', "You've run out of free chat messages. You still have free code completions available in the Copilot Free plan. These limits will reset on {0}.", dateFormatter.format(chatEntitlementService.quotas.quotaResetDate)); + } else if (completionsQuotaExceeded && !chatQuotaExceeded) { + message = localize('completionsQuotaExceeded', "You've run out of free code completions. You still have free chat messages available in the Copilot Free plan. These limits will reset on {0}.", dateFormatter.format(chatEntitlementService.quotas.quotaResetDate)); + } else { + message = localize('chatAndCompletionsQuotaExceeded', "You've reached the limit of the Copilot Free plan. These limits will reset on {0}.", dateFormatter.format(chatEntitlementService.quotas.quotaResetDate)); + } + + const upgradeToPro = localize('upgradeToPro', "Upgrade to Copilot Pro (your first 30 days are free) for:\n- Unlimited code completions\n- Unlimited chat messages\n- Access to additional models"); + + await dialogService.prompt({ + type: 'none', + message: localize('copilotFree', "Copilot Limit Reached"), + cancelButton: { + label: localize('dismiss', "Dismiss"), + run: () => { /* noop */ } + }, + buttons: [ + { + label: localize('upgradePro', "Upgrade to Copilot Pro"), + run: () => commandService.executeCommand('workbench.action.chat.upgradePlan', 'chat-dialog') + }, + ], + custom: { + icon: Codicon.copilotWarningLarge, + markdownDetails: [ + { markdown: new MarkdownString(message, true) }, + { markdown: new MarkdownString(upgradeToPro, true) } + ] + } + }); } }); } @@ -546,7 +642,8 @@ const defaultChat = { manageSettingsUrl: product.defaultChatAgent?.manageSettingsUrl ?? '', managePlanUrl: product.defaultChatAgent?.managePlanUrl ?? '', enterpriseProviderId: product.defaultChatAgent?.enterpriseProviderId ?? '', - providerSetting: product.defaultChatAgent?.providerSetting ?? '', + completionsAdvancedSetting: product.defaultChatAgent?.completionsAdvancedSetting ?? '', + completionsMenuCommand: product.defaultChatAgent?.completionsMenuCommand ?? '', }; // Add next to the command center if command center is disabled @@ -593,14 +690,12 @@ export class CopilotTitleBarMenuRendering extends Disposable implements IWorkben constructor( @IActionViewItemService actionViewItemService: IActionViewItemService, @IChatAgentService agentService: IChatAgentService, - @IChatQuotasService chatQuotasService: IChatQuotasService, @IInstantiationService instantiationService: IInstantiationService, - @IContextKeyService contextKeyService: IContextKeyService, + @IChatEntitlementService chatEntitlementService: IChatEntitlementService, + @IConfigurationService configurationService: IConfigurationService, ) { super(); - const contextKeySet = new Set([ChatContextKeys.Setup.signedOut.key]); - const disposable = actionViewItemService.register(MenuId.CommandCenter, MenuId.ChatTitleBarMenu, (action, options) => { if (!(action instanceof SubmenuItemAction)) { return undefined; @@ -612,31 +707,36 @@ export class CopilotTitleBarMenuRendering extends Disposable implements IWorkben run() { } }); - const chatExtensionInstalled = agentService.getAgents().some(agent => agent.isDefault); - const { chatQuotaExceeded, completionsQuotaExceeded } = chatQuotasService.quotas; - const signedOut = contextKeyService.getContextKeyValue(ChatContextKeys.Setup.signedOut.key) ?? false; + const chatExtensionInstalled = chatEntitlementService.sentiment === ChatSentiment.Installed; + const { chatQuotaExceeded, completionsQuotaExceeded } = chatEntitlementService.quotas; + const signedOut = chatEntitlementService.entitlement === ChatEntitlement.Unknown; + const setupFromDialog = configurationService.getValue('chat.experimental.setupFromDialog'); let primaryActionId: string; let primaryActionTitle: string; let primaryActionIcon: ThemeIcon; - if (!chatExtensionInstalled) { - primaryActionId = 'workbench.action.chat.triggerSetup'; + if (!chatExtensionInstalled && !setupFromDialog) { + primaryActionId = CHAT_SETUP_ACTION_ID; primaryActionTitle = localize('triggerChatSetup', "Use AI Features with Copilot for Free..."); primaryActionIcon = Codicon.copilot; - } else { - if (signedOut) { - primaryActionId = CHAT_OPEN_ACTION_ID; - primaryActionTitle = localize('signInToChatSetup', "Sign in to Use Copilot..."); - primaryActionIcon = Codicon.copilotWarning; - } else if (chatQuotaExceeded || completionsQuotaExceeded) { - primaryActionId = OPEN_CHAT_QUOTA_EXCEEDED_DIALOG; - primaryActionTitle = quotaToButtonMessage({ chatQuotaExceeded, completionsQuotaExceeded }); - primaryActionIcon = Codicon.copilotWarning; + } else if (chatExtensionInstalled && signedOut) { + primaryActionId = TOGGLE_CHAT_ACTION_ID; + primaryActionTitle = localize('signInToChatSetup', "Sign in to Use Copilot..."); + primaryActionIcon = Codicon.copilotNotConnected; + } else if (chatExtensionInstalled && (chatQuotaExceeded || completionsQuotaExceeded)) { + primaryActionId = OPEN_CHAT_QUOTA_EXCEEDED_DIALOG; + if (chatQuotaExceeded && !completionsQuotaExceeded) { + primaryActionTitle = localize('chatQuotaExceededButton', "Monthly chat messages limit reached. Click for details."); + } else if (completionsQuotaExceeded && !chatQuotaExceeded) { + primaryActionTitle = localize('completionsQuotaExceededButton', "Monthly code completions limit reached. Click for details."); } else { - primaryActionId = CHAT_OPEN_ACTION_ID; - primaryActionTitle = OpenChatGlobalAction.TITLE.value; - primaryActionIcon = Codicon.copilot; + primaryActionTitle = localize('chatAndCompletionsQuotaExceededButton', "Copilot Free plan limit reached. Click for details."); } + primaryActionIcon = Codicon.copilotWarning; + } else { + primaryActionId = TOGGLE_CHAT_ACTION_ID; + primaryActionTitle = localize('toggleChat', "Toggle Chat"); + primaryActionIcon = Codicon.copilot; } return instantiationService.createInstance(DropdownWithPrimaryActionViewItem, instantiationService.createInstance(MenuItemAction, { id: primaryActionId, @@ -644,12 +744,18 @@ export class CopilotTitleBarMenuRendering extends Disposable implements IWorkben icon: primaryActionIcon, }, undefined, undefined, undefined, undefined), dropdownAction, action.actions, '', { ...options, skipTelemetry: true }); }, Event.any( - agentService.onDidChangeAgents, - chatQuotasService.onDidChangeQuotas, - Event.filter(contextKeyService.onDidChangeContext, e => e.affectsSome(contextKeySet)) + chatEntitlementService.onDidChangeSentiment, + chatEntitlementService.onDidChangeQuotaExceeded, + chatEntitlementService.onDidChangeEntitlement, + Event.filter(configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('chat.experimental.setupFromDialog')) )); // Reduces flicker a bit on reload/restart markAsSingleton(disposable); } } + +export function getEditsViewId(accessor: ServicesAccessor): string { + const chatService = accessor.get(IChatService); + return chatService.unifiedViewEnabled ? ChatViewId : EditsViewId; +} diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatAttachPromptAction.ts b/src/vs/workbench/contrib/chat/browser/actions/chatAttachPromptAction.ts deleted file mode 100644 index f733f7382c7e..000000000000 --- a/src/vs/workbench/contrib/chat/browser/actions/chatAttachPromptAction.ts +++ /dev/null @@ -1,114 +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 { CHAT_CATEGORY } from './chatActions.js'; -import { localize2 } from '../../../../../nls.js'; -import { assertDefined } from '../../../../../base/common/types.js'; -import { IChatWidget, showChatView, showEditsView } from '../chat.js'; -import { Action2 } from '../../../../../platform/actions/common/actions.js'; -import { ILabelService } from '../../../../../platform/label/common/label.js'; -import { IOpenerService } from '../../../../../platform/opener/common/opener.js'; -import { IViewsService } from '../../../../services/views/common/viewsService.js'; -import { ServicesAccessor } from '../../../../../editor/browser/editorExtensions.js'; -import { IQuickInputService } from '../../../../../platform/quickinput/common/quickInput.js'; -import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; -import { ISelectPromptOptions, showSelectPromptDialog } from './chatAttachPromptAction/showPromptSelectionDialog.js'; -import { IPromptsService } from '../../common/promptSyntax/service/types.js'; - -/** - * Action ID for the `Attach Prompt` action. - */ -export const ATTACH_PROMPT_ACTION_ID = 'workbench.action.chat.attach.prompt'; - -/** - * Options for the {@link AttachPromptAction} action. - */ -export interface IChatAttachPromptActionOptions extends Pick< - ISelectPromptOptions, 'resource' | 'widget' -> { } - -/** - * Action to attach a prompt to a chat widget input. - */ -export class AttachPromptAction extends Action2 { - constructor() { - super({ - id: ATTACH_PROMPT_ACTION_ID, - title: localize2('workbench.action.chat.attach.prompt.label', "Use Prompt"), - f1: false, - category: CHAT_CATEGORY, - }); - } - - public override async run( - accessor: ServicesAccessor, - options: IChatAttachPromptActionOptions, - ): Promise { - const labelService = accessor.get(ILabelService); - const viewsService = accessor.get(IViewsService); - const openerService = accessor.get(IOpenerService); - const promptsService = accessor.get(IPromptsService); - const initService = accessor.get(IInstantiationService); - const quickInputService = accessor.get(IQuickInputService); - - const selectionResult = await showSelectPromptDialog({ - ...options, - initService, - labelService, - openerService, - promptsService, - quickInputService, - }); - - // no prompt selected, nothing to do - if (!selectionResult) { - return; - } - - const { selected, altOption } = selectionResult; - - // reveal appropriate chat widget - const widget = await revealChatWidget(options, altOption, viewsService); - - widget - .attachmentModel - .promptInstructions - .add(selected.value); - - widget.focusInput(); - } -} - -/** - * Reveals a chat widget based on the provided {@link IChatAttachPromptActionOptions.widget widget} - * reference. If no widget reference is provided, the function will reveal a `chat panel` by default - * (either a last focused, or a new one), but if the {@link altOption} is set to `true`, a `chat edits` - * panel will be revealed instead (likewise either a last focused, or a new one). - */ -const revealChatWidget = async ( - options: IChatAttachPromptActionOptions, - altOption: boolean, - viewsService: IViewsService, -): Promise => { - const { widget } = options; - - // if no widget reference is present, the command was triggered from outside of - // an active chat input, so we reveal a chat widget window based on the `alt` - // key modifier state when a prompt was selected from the picker UI dialog - if (!widget) { - const widget = (altOption) - ? await showEditsView(viewsService) - : await showChatView(viewsService); - - assertDefined( - widget, - 'Revealed chat widget must be defined.', - ); - - return widget; - } - - return widget; -}; diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatAttachPromptAction/chatAttachPromptAction.ts b/src/vs/workbench/contrib/chat/browser/actions/chatAttachPromptAction/chatAttachPromptAction.ts new file mode 100644 index 000000000000..a5c08661755e --- /dev/null +++ b/src/vs/workbench/contrib/chat/browser/actions/chatAttachPromptAction/chatAttachPromptAction.ts @@ -0,0 +1,66 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CHAT_CATEGORY } from '../chatActions.js'; +import { localize2 } from '../../../../../../nls.js'; +import { Action2 } from '../../../../../../platform/actions/common/actions.js'; +import { IPromptsService } from '../../../common/promptSyntax/service/types.js'; +import { ILabelService } from '../../../../../../platform/label/common/label.js'; +import { IOpenerService } from '../../../../../../platform/opener/common/opener.js'; +import { IViewsService } from '../../../../../services/views/common/viewsService.js'; +import { ServicesAccessor } from '../../../../../../editor/browser/editorExtensions.js'; +import { ISelectPromptOptions, askToSelectPrompt } from './dialogs/askToSelectPrompt.js'; +import { IQuickInputService } from '../../../../../../platform/quickinput/common/quickInput.js'; +import { ChatContextKeys } from '../../../common/chatContextKeys.js'; + +/** + * Action ID for the `Attach Prompt` action. + */ +export const ATTACH_PROMPT_ACTION_ID = 'workbench.action.chat.attach.prompt'; + +/** + * Options for the {@link AttachPromptAction} action. + */ +export interface IChatAttachPromptActionOptions extends Pick< + ISelectPromptOptions, 'resource' | 'widget' | 'viewsService' +> { } + +/** + * Action to attach a prompt to a chat widget input. + */ +export class AttachPromptAction extends Action2 { + constructor() { + super({ + id: ATTACH_PROMPT_ACTION_ID, + title: localize2('workbench.action.chat.attach.prompt.label', "Use Prompt"), + f1: false, + precondition: ChatContextKeys.enabled, + category: CHAT_CATEGORY, + }); + } + + public override async run( + accessor: ServicesAccessor, + options: IChatAttachPromptActionOptions, + ): Promise { + const labelService = accessor.get(ILabelService); + const viewsService = accessor.get(IViewsService); + const openerService = accessor.get(IOpenerService); + const promptsService = accessor.get(IPromptsService); + const quickInputService = accessor.get(IQuickInputService); + + // find all prompt files in the user workspace + const promptFiles = await promptsService.listPromptFiles(); + + await askToSelectPrompt({ + ...options, + promptFiles, + labelService, + viewsService, + openerService, + quickInputService, + }); + } +} diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatAttachPromptAction/dialogs/askToSelectPrompt.ts b/src/vs/workbench/contrib/chat/browser/actions/chatAttachPromptAction/dialogs/askToSelectPrompt.ts new file mode 100644 index 000000000000..79d7ceccd250 --- /dev/null +++ b/src/vs/workbench/contrib/chat/browser/actions/chatAttachPromptAction/dialogs/askToSelectPrompt.ts @@ -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. + *--------------------------------------------------------------------------------------------*/ + +import { localize } from '../../../../../../../nls.js'; +import { URI } from '../../../../../../../base/common/uri.js'; +import { assert } from '../../../../../../../base/common/assert.js'; +import { IChatWidget, showChatView, showEditsView } from '../../../chat.js'; +import { IChatAttachPromptActionOptions } from '../chatAttachPromptAction.js'; +import { IPromptPath } from '../../../../common/promptSyntax/service/types.js'; +import { DisposableStore } from '../../../../../../../base/common/lifecycle.js'; +import { dirname, extUri } from '../../../../../../../base/common/resources.js'; +import { DOCUMENTATION_URL } from '../../../../common/promptSyntax/constants.js'; +import { isLinux, isWindows } from '../../../../../../../base/common/platform.js'; +import { ILabelService } from '../../../../../../../platform/label/common/label.js'; +import { IOpenerService } from '../../../../../../../platform/opener/common/opener.js'; +import { IViewsService } from '../../../../../../services/views/common/viewsService.js'; +import { assertDefined, WithUriValue } from '../../../../../../../base/common/types.js'; +import { getCleanPromptName } from '../../../../../../../platform/prompts/common/constants.js'; +import { IQuickInputService, IQuickPickItem } from '../../../../../../../platform/quickinput/common/quickInput.js'; + +/** + * Options for the {@link askToSelectPrompt} function. + */ +export interface ISelectPromptOptions { + /** + * Prompt resource `URI` to attach to the chat input, if any. + * If provided the resource will be pre-selected in the prompt picker dialog, + * otherwise the dialog will show the prompts list without any pre-selection. + */ + readonly resource?: URI; + + /** + * Target chat widget reference to attach the prompt to. If not provided, the command + * attaches the prompt to a `chat panel` widget by default (either the last focused, + * or a new one). If the `alt` (`option` on mac) key was pressed when the prompt is + * selected, the `edits` widget is used instead (likewise, either the last focused, + * or a new one). + */ + readonly widget?: IChatWidget; + + /** + * List of prompt files to show in the selection dialog. + */ + readonly promptFiles: readonly IPromptPath[]; + + readonly labelService: ILabelService; + readonly viewsService: IViewsService; + readonly openerService: IOpenerService; + readonly quickInputService: IQuickInputService; +} + +/** + * A special quick pick item that links to the documentation. + */ +const DOCS_OPTION: WithUriValue = { + type: 'item', + label: localize( + 'commands.prompts.use.select-dialog.docs-label', + 'Learn how to create reusable prompts', + ), + description: DOCUMENTATION_URL, + tooltip: DOCUMENTATION_URL, + value: URI.parse(DOCUMENTATION_URL), +}; + +/** + * Shows the prompt selection dialog to the user that allows to select a prompt file(s). + * + * If {@link ISelectPromptOptions.resource resource} is provided, the dialog will have + * the resource pre-selected in the prompts list. + */ +export const askToSelectPrompt = async ( + options: ISelectPromptOptions, +): Promise => { + const { promptFiles, resource, quickInputService, labelService } = options; + + const fileOptions = promptFiles.map((promptFile) => { + return createPickItem(promptFile, labelService); + }); + + /** + * Add a link to the documentation to the end of prompts list. + */ + fileOptions.push(DOCS_OPTION); + + // if a resource is provided, create an `activeItem` for it to pre-select + // it in the UI, and sort the list so the active item appears at the top + let activeItem: WithUriValue | undefined; + if (resource) { + activeItem = fileOptions.find((file) => { + return extUri.isEqual(file.value, resource); + }); + + // if no item for the `resource` was found, it means that the resource is not + // in the list of prompt files, so add a new item for it; this ensures that + // the currently active prompt file is always available in the selection dialog, + // even if it is not included in the prompts list otherwise(from location setting) + if (!activeItem) { + activeItem = createPickItem({ + uri: resource, + // "user" prompts are always registered in the prompts list, hence it + // should be safe to assume that `resource` is not "user" prompt here + type: 'local', + }, labelService); + fileOptions.push(activeItem); + } + + fileOptions.sort((file1, file2) => { + if (extUri.isEqual(file1.value, resource)) { + return -1; + } + + if (extUri.isEqual(file2.value, resource)) { + return 1; + } + + return 0; + }); + } + + /** + * If still no active item present, fall back to the first item in the list. + * This can happen only if command was invoked not from a focused prompt file + * (hence the `resource` is not provided in the options). + * + * Fixes the two main cases: + * - when no prompt files found it, pre-selects the documentation link + * - when there is only a single prompt file, pre-selects it + */ + if (!activeItem) { + activeItem = fileOptions[0]; + } + + // otherwise show the prompt file selection dialog + const { openerService } = options; + + const quickPick = quickInputService.createQuickPick>(); + quickPick.activeItems = activeItem ? [activeItem] : []; + quickPick.placeholder = createPlaceholderText(options); + quickPick.canAcceptInBackground = true; + quickPick.matchOnDescription = true; + quickPick.items = fileOptions; + + return await new Promise(resolve => { + const disposables = new DisposableStore(); + + let lastActiveWidget = options.widget; + disposables.add({ + dispose() { + quickPick.dispose(); + resolve(); + + // if something was attached, focus on the target chat input + lastActiveWidget?.focusInput(); + }, + }); + + disposables.add(quickPick.onDidAccept(async (event) => { + const { selectedItems } = quickPick; + const { alt, ctrlCmd } = quickPick.keyMods; + + // sanity check to confirm our expectations + assert( + selectedItems.length === 1, + `Only one item can be accepted, got '${selectedItems.length}'.`, + ); + + // whether user selected the docs link option + const docsSelected = (selectedItems[0] === DOCS_OPTION); + + // if `super` key was pressed, open the selected prompt file(s) + if (ctrlCmd || docsSelected) { + return await openFiles(selectedItems, openerService); + } + + // otherwise attach the selected prompt to a chat input + lastActiveWidget = await attachFiles(selectedItems, options, alt); + + // if user submitted their selection, close the dialog + if (!event.inBackground) { + disposables.dispose(); + } + })); + + disposables.add(quickPick.onDidHide( + disposables.dispose.bind(disposables), + )); + + quickPick.show(); + }); +}; + +/** + * Creates a quick pick item for a prompt. + */ +const createPickItem = ( + promptFile: IPromptPath, + labelService: ILabelService, +): WithUriValue => { + const { uri, type } = promptFile; + const fileWithoutExtension = getCleanPromptName(uri); + + // if a "user" prompt, don't show its filesystem path in + // the user interface, but do that for all the "local" ones + const description = (type === 'user') + ? localize( + 'user-prompt.capitalized', + 'User prompt', + ) + : labelService.getUriLabel(dirname(uri), { relative: true }); + + const tooltip = (type === 'user') + ? description + : uri.fsPath; + + return { + type: 'item', + label: fileWithoutExtension, + description, + tooltip, + value: uri, + id: uri.toString(), + }; +}; + +/** + * Creates a placeholder text to show in the prompt selection dialog. + */ +const createPlaceholderText = (options: ISelectPromptOptions): string => { + const { widget } = options; + + let text = localize( + 'commands.prompts.use.select-dialog.placeholder', + 'Select a prompt to use', + ); + + // if no widget reference is provided, add the note about `options` + // and `cmd` modifiers users can use to alter the command behavior + if (!widget) { + const altOptionkey = (isWindows || isLinux) ? 'Alt' : 'Option'; + + const altOptionModifierNote = localize( + 'commands.prompts.use.select-dialog.alt-modifier-note', + '{0}-key to use in Edits', + altOptionkey, + ); + + const cmdCtrlkey = (isWindows || isLinux) ? 'Ctrl' : 'Cmd'; + const superModifierNote = localize( + 'commands.prompts.use.select-dialog.super-modifier-note', + '{0}-key to open in editor', + cmdCtrlkey, + ); + + text += localize( + 'commands.prompts.use.select-dialog.modifier-notes', + ' (hold {0} or {1})', + altOptionModifierNote, + superModifierNote, + ); + } + + return text; +}; + +/** + * Opens provided files in the editor. + */ +const openFiles = async ( + files: readonly WithUriValue[], + openerService: IOpenerService, +) => { + for (const file of files) { + await openerService.open(file.value); + } +}; + +/** + * Attaches provided files to a chat input. + */ +const attachFiles = async ( + files: readonly WithUriValue[], + options: ISelectPromptOptions, + altOption: boolean, +): Promise => { + const widget = await getChatWidgetObject(options, altOption); + + for (const file of files) { + widget + .attachmentModel + .promptInstructions + .add(file.value); + } + + return widget; +}; + +/** + * Gets a chat widget based on the provided {@link IChatAttachPromptActionOptions.widget widget} + * reference. If no widget reference is provided, the function will reveal a `chat panel` by default + * (either a last focused, or a new one), but if the {@link altOption} is set to `true`, a `chat edits` + * panel will be revealed instead (likewise either a last focused, or a new one). + * + * @throws if failed to reveal a chat widget. + */ +const getChatWidgetObject = async ( + options: IChatAttachPromptActionOptions, + altOption: boolean, +): Promise => { + const { widget, viewsService } = options; + + // if no widget reference is present, the command was triggered from outside of + // an active chat input, so we reveal a chat widget window based on the `alt` + // key modifier state when a prompt was selected from the picker UI dialog + if (!widget) { + const widget = (altOption) + ? await showEditsView(viewsService) + : await showChatView(viewsService); + + assertDefined( + widget, + 'Revealed chat widget must be defined.', + ); + + return widget; + } + + return widget; +}; diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatAttachPromptAction/showPromptSelectionDialog.ts b/src/vs/workbench/contrib/chat/browser/actions/chatAttachPromptAction/showPromptSelectionDialog.ts deleted file mode 100644 index 2c71d30d3dd1..000000000000 --- a/src/vs/workbench/contrib/chat/browser/actions/chatAttachPromptAction/showPromptSelectionDialog.ts +++ /dev/null @@ -1,199 +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 { IChatWidget } from '../../chat.js'; -import { localize } from '../../../../../../nls.js'; -import { URI } from '../../../../../../base/common/uri.js'; -import { WithUriValue } from '../../../../../../base/common/types.js'; -import { dirname, extUri } from '../../../../../../base/common/resources.js'; -import { DOCUMENTATION_URL } from '../../../common/promptSyntax/constants.js'; -import { isLinux, isWindows } from '../../../../../../base/common/platform.js'; -import { ILabelService } from '../../../../../../platform/label/common/label.js'; -import { IOpenerService } from '../../../../../../platform/opener/common/opener.js'; -import { IPromptPath, IPromptsService } from '../../../common/promptSyntax/service/types.js'; -import { getCleanPromptName } from '../../../../../../platform/prompts/common/constants.js'; -import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; -import { IPickOptions, IQuickInputService, IQuickPickItem } from '../../../../../../platform/quickinput/common/quickInput.js'; - -/** - * Options for the {@link showSelectPromptDialog} function. - */ -export interface ISelectPromptOptions { - /** - * Prompt resource `URI` to attach to the chat input, if any. - * If provided the resource will be pre-selected in the prompt picker dialog, - * otherwise the dialog will show the prompts list without any pre-selection. - */ - resource?: URI; - - /** - * Target chat widget reference to attach the prompt to. If not provided, the command - * attaches the prompt to a `chat panel` widget by default (either the last focused, - * or a new one). If the `alt` (`option` on mac) key was pressed when the prompt is - * selected, the `edits` widget is used instead (likewise, either the last focused, - * or a new one). - */ - widget?: IChatWidget; - - labelService: ILabelService; - openerService: IOpenerService; - promptsService: IPromptsService; - initService: IInstantiationService; - quickInputService: IQuickInputService; -} - -/** - * Result of user interaction with the prompt selection dialog. - */ -interface IPromptSelectionResult { - /** - * Selected prompt item in the dialog. - */ - selected: WithUriValue; - - /** - * Whether the `alt` (`option` on mac) key was pressed when - * the prompt selection was made. - */ - altOption: boolean; -} - -/** - * Creates a quick pick item for a prompt. - */ -const createPickItem = ( - { uri }: IPromptPath, - labelService: ILabelService, -): WithUriValue => { - const fileWithoutExtension = getCleanPromptName(uri); - - return { - type: 'item', - label: fileWithoutExtension, - description: labelService.getUriLabel(dirname(uri), { relative: true }), - tooltip: uri.fsPath, - value: uri, - }; -}; - -/** - * Creates a placeholder text to show in the prompt selection dialog. - */ -const createPlaceholderText = (widget?: IChatWidget): string => { - let text = localize('selectPromptPlaceholder', 'Select a prompt to use'); - - // if no widget reference is provided, add the note about - // the `alt`/`option` key modifier users can use - if (!widget) { - const key = (isWindows || isLinux) ? 'alt' : 'option'; - - text += ' ' + localize('selectPromptPlaceholder.holdAltOption', '(hold `{0}` to use in Edits)', key); - } - - return text; -}; - -/** - * Shows a prompt selection dialog to the user and waits for a selection. - * - * If {@link ISelectPromptOptions.resource resource} is provided, the dialog will have - * the resource pre-selected in the prompts list. - */ -export const showSelectPromptDialog = async ( - options: ISelectPromptOptions, -): Promise => { - const { resource, labelService, promptsService } = options; - - // find all prompt instruction files in the user workspace - // and present them to the user so they can select one - const files = await promptsService.listPromptFiles() - .then((promptFiles) => { - return promptFiles.map((promptFile) => { - return createPickItem(promptFile, labelService); - }); - }); - - const { quickInputService, openerService } = options; - - // if not prompt files found, render the "how to add" message - // to the user with a link to the documentation - if (files.length === 0) { - const docsQuickPick: WithUriValue = { - type: 'item', - label: localize('noPromptFilesFoundTooltipLabel', 'Learn how to create reusable prompts'), - description: DOCUMENTATION_URL, - tooltip: DOCUMENTATION_URL, - value: URI.parse(DOCUMENTATION_URL), - }; - - const result = await quickInputService.pick( - [docsQuickPick], - { - placeHolder: localize('noPromptFilesFoundLabel', 'No prompts found.'), - canPickMany: false, - }); - - if (!result) { - return null; - } - - await openerService.open(result.value); - - return null; - } - - // if a resource is provided, create an `activeItem` for it to pre-select - // it in the UI, and sort the list so the active item appears at the top - let activeItem: WithUriValue | undefined; - if (resource) { - activeItem = files.find((file) => { - return extUri.isEqual(file.value, resource); - }); - - files.sort((file1, file2) => { - if (extUri.isEqual(file1.value, resource)) { - return -1; - } - - if (extUri.isEqual(file2.value, resource)) { - return 1; - } - - return 0; - }); - } - - // otherwise show the prompt file selection dialog - const { widget } = options; - const pickOptions: IPickOptions> = { - placeHolder: createPlaceholderText(widget), - activeItem, - canPickMany: false, - matchOnDescription: true, - }; - - // keep track of whether the `alt` (`option` on mac) key is - // pressed when a prompt item is selected in the dialog - let altOption = false; - if (!widget) { - pickOptions.onKeyMods = (keyMods) => { - if (keyMods.alt) { - altOption = true; - } - }; - } - - const maybeSelectedFile = await quickInputService.pick(files, pickOptions); - - // if user cancels the dialog, return `null` instead - if (!maybeSelectedFile) { - return null; - } - - return { - selected: maybeSelectedFile, - altOption, - }; -}; diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatClearActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatClearActions.ts index 71d64cc9a65e..bd98444aefcd 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatClearActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatClearActions.ts @@ -11,25 +11,44 @@ import { AccessibilitySignal, IAccessibilitySignalService } from '../../../../.. import { Action2, MenuId, registerAction2 } from '../../../../../platform/actions/common/actions.js'; import { ContextKeyExpr } from '../../../../../platform/contextkey/common/contextkey.js'; import { IDialogService } from '../../../../../platform/dialogs/common/dialogs.js'; +import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; import { KeybindingWeight } from '../../../../../platform/keybinding/common/keybindingsRegistry.js'; import { ActiveEditorContext } from '../../../../common/contextkeys.js'; import { IViewsService } from '../../../../services/views/common/viewsService.js'; import { isChatViewTitleActionContext } from '../../common/chatActions.js'; -import { ChatAgentLocation } from '../../common/chatAgents.js'; -import { ChatContextKeys } from '../../common/chatContextKeys.js'; +import { ChatContextKeyExprs, ChatContextKeys } from '../../common/chatContextKeys.js'; import { hasAppliedChatEditsContextKey, hasUndecidedChatEditingResourceContextKey, IChatEditingSession, WorkingSetEntryState } from '../../common/chatEditingService.js'; +import { ChatAgentLocation, ChatMode } from '../../common/constants.js'; import { ChatViewId, EditsViewId, IChatWidget, IChatWidgetService } from '../chat.js'; import { EditingSessionAction } from '../chatEditing/chatEditingActions.js'; import { ctxIsGlobalEditingSession } from '../chatEditing/chatEditingEditorContextKeys.js'; import { ChatEditorInput } from '../chatEditorInput.js'; import { ChatViewPane } from '../chatViewPane.js'; -import { CHAT_CATEGORY } from './chatActions.js'; +import { CHAT_CATEGORY, getEditsViewId, IChatViewOpenOptions } from './chatActions.js'; import { clearChatEditor } from './chatClear.js'; export const ACTION_ID_NEW_CHAT = `workbench.action.chat.newChat`; export const ACTION_ID_NEW_EDIT_SESSION = `workbench.action.chat.newEditSession`; export const ChatDoneActionId = 'workbench.action.chat.done'; +export interface INewEditSessionActionContext { + /** + * An initial prompt to write to the chat. + */ + inputValue?: string; + + /** + * Selects opening in agent mode or not. If not set, the current mode is used. + * This is ignored when coming from a chat view title context. + */ + agentMode?: boolean; + + /** + * Whether the inputValue is partial and should wait for further user input. If false or not set, the prompt is sent immediately. + */ + isPartialQuery?: boolean; +} + export function registerNewChatActions() { registerAction2(class NewChatEditorAction extends Action2 { constructor() { @@ -76,7 +95,9 @@ export function registerNewChatActions() { }, { id: MenuId.ViewTitle, - when: ContextKeyExpr.equals('view', ChatViewId), + when: ContextKeyExpr.and( + ChatContextKeys.location.isEqualTo(ChatAgentLocation.Panel), + ChatContextKeys.inUnifiedChat.negate()), group: 'navigation', order: -1 }] @@ -118,7 +139,7 @@ export function registerNewChatActions() { }, { id: MenuId.ViewTitle, - when: ContextKeyExpr.equals('view', EditsViewId), + when: ChatContextKeyExprs.inEditsOrUnified, group: 'navigation', order: -1 }], @@ -128,7 +149,7 @@ export function registerNewChatActions() { mac: { primary: KeyMod.WinCtrl | KeyCode.KeyL }, - when: ContextKeyExpr.and(ChatContextKeys.inChatSession, ChatContextKeys.location.isEqualTo(ChatAgentLocation.EditingSession)) + when: ContextKeyExpr.and(ChatContextKeys.inChatSession, ChatContextKeyExprs.inEditingMode) } }); } @@ -175,34 +196,56 @@ export function registerNewChatActions() { } async runEditingSessionAction(accessor: ServicesAccessor, editingSession: IChatEditingSession, chatWidget: IChatWidget, ...args: any[]) { - const context = args[0]; + const context: INewEditSessionActionContext | undefined = args[0]; const accessibilitySignalService = accessor.get(IAccessibilitySignalService); const widgetService = accessor.get(IChatWidgetService); const dialogService = accessor.get(IDialogService); const viewsService = accessor.get(IViewsService); + const instaSevice = accessor.get(IInstantiationService); + if (!(await this._handleCurrentEditingSession(editingSession, dialogService))) { return; } - if (isChatViewTitleActionContext(context)) { + + const isChatViewTitleAction = isChatViewTitleActionContext(context); + + let widget: IChatWidget | undefined; + if (isChatViewTitleAction) { // Is running in the Chat view title - announceChatCleared(accessibilitySignalService); - const widget = widgetService.getWidgetBySessionId(context.sessionId); - if (widget) { - await editingSession.stop(true); - widget.clear(); - widget.attachmentModel.clear(); - widget.focusInput(); - } + widget = widgetService.getWidgetBySessionId(context.sessionId); } else { // Is running from f1 or keybinding - const chatView = await viewsService.openView(EditsViewId) as ChatViewPane; - const widget = chatView.widget; + const view = instaSevice.invokeFunction(getEditsViewId); + const chatView = await viewsService.openView(view); + widget = chatView?.widget; + } - announceChatCleared(accessibilitySignalService); - await editingSession.stop(true); - widget.clear(); - widget.attachmentModel.clear(); - widget.focusInput(); + announceChatCleared(accessibilitySignalService); + + if (!widget) { + return; + } + + await editingSession.stop(true); + widget.clear(); + widget.attachmentModel.clear(); + widget.input.relatedFiles?.clear(); + widget.focusInput(); + + if (!context) { + return; + } + + if (!isChatViewTitleAction && typeof context.agentMode === 'boolean') { + widget.input.setChatMode(context.agentMode ? ChatMode.Agent : ChatMode.Edit); + } + + if (context.inputValue) { + if (context.isPartialQuery) { + widget.setInput(context.inputValue); + } else { + widget.acceptInput(context.inputValue); + } } } }); @@ -217,7 +260,7 @@ export function registerNewChatActions() { f1: false, menu: [{ id: MenuId.ChatEditingWidgetToolbar, - when: ContextKeyExpr.and(hasUndecidedChatEditingResourceContextKey.negate(), hasAppliedChatEditsContextKey, ChatContextKeys.editingParticipantRegistered, ChatContextKeys.location.isEqualTo(ChatAgentLocation.EditingSession)), + when: ContextKeyExpr.and(hasUndecidedChatEditingResourceContextKey.negate(), hasAppliedChatEditsContextKey, ChatContextKeys.editingParticipantRegistered, ChatContextKeyExprs.inEditsOrUnified), group: 'navigation', order: 0 }] @@ -241,7 +284,12 @@ export function registerNewChatActions() { // Is running from f1 or keybinding const viewsService = accessor.get(IViewsService); - const chatView = await viewsService.openView(EditsViewId) as ChatViewPane; + const viewId = getEditsViewId(accessor); + const chatView = await viewsService.openView(viewId); + if (!chatView) { + return; + } + const widget = chatView.widget; announceChatCleared(accessibilitySignalService); @@ -263,7 +311,7 @@ export function registerNewChatActions() { f1: true, menu: [{ id: MenuId.ViewTitle, - when: ContextKeyExpr.equals('view', EditsViewId), + when: ChatContextKeyExprs.inEditingMode, group: 'navigation', order: -3 }] @@ -286,7 +334,7 @@ export function registerNewChatActions() { f1: true, menu: [{ id: MenuId.ViewTitle, - when: ContextKeyExpr.equals('view', EditsViewId), + when: ChatContextKeyExprs.inEditingMode, group: 'navigation', order: -2 }] @@ -306,14 +354,19 @@ export function registerNewChatActions() { category: CHAT_CATEGORY, icon: Codicon.goToEditingSession, f1: true, + precondition: ChatContextKeys.Setup.hidden.toNegated(), menu: [{ id: MenuId.ViewTitle, - when: ContextKeyExpr.and(ContextKeyExpr.equals('view', ChatViewId), ChatContextKeys.editingParticipantRegistered, + when: ContextKeyExpr.and( + ContextKeyExpr.equals('view', ChatViewId), + ChatContextKeys.editingParticipantRegistered, ContextKeyExpr.equals(`view.${EditsViewId}.visible`, false), ContextKeyExpr.or( ContextKeyExpr.and(ContextKeyExpr.equals(`workbench.panel.chat.defaultViewContainerLocation`, true), ContextKeyExpr.equals(`workbench.panel.chatEditing.defaultViewContainerLocation`, false)), ContextKeyExpr.and(ContextKeyExpr.equals(`workbench.panel.chat.defaultViewContainerLocation`, false), ContextKeyExpr.equals(`workbench.panel.chatEditing.defaultViewContainerLocation`, true)), - )), + ), + ChatContextKeys.inUnifiedChat.negate() + ), group: 'navigation', order: 1 }, { @@ -337,10 +390,21 @@ export function registerNewChatActions() { }); } - async run(accessor: ServicesAccessor, ...args: any[]) { + async run(accessor: ServicesAccessor, opts?: string | IChatViewOpenOptions) { + opts = typeof opts === 'string' ? { query: opts } : opts; const viewsService = accessor.get(IViewsService); - const chatView = await viewsService.openView(EditsViewId) as ChatViewPane; - chatView.widget.focusInput(); + const chatView = await viewsService.openView(EditsViewId) + ?? await viewsService.openView(ChatViewId); + + if (opts?.query) { + if (opts.isPartialQuery) { + chatView?.widget.setInput(opts.query); + } else { + chatView?.widget.acceptInput(opts.query); + } + } + + chatView?.widget.focusInput(); } }); } diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts index baf3f39e7c0b..7e6a74fba210 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatCodeblockActions.ts @@ -29,10 +29,10 @@ import { IEditorService } from '../../../../services/editor/common/editorService import { accessibleViewInCodeBlock } from '../../../accessibility/browser/accessibilityConfiguration.js'; import { reviewEdits } from '../../../inlineChat/browser/inlineChatController.js'; import { ITerminalEditorService, ITerminalGroupService, ITerminalService } from '../../../terminal/browser/terminal.js'; -import { ChatAgentLocation } from '../../common/chatAgents.js'; import { ChatContextKeys } from '../../common/chatContextKeys.js'; import { ChatCopyKind, IChatService } from '../../common/chatService.js'; import { IChatResponseViewModel, isResponseVM } from '../../common/chatViewModel.js'; +import { ChatAgentLocation } from '../../common/constants.js'; import { IChatCodeBlockContextProviderService, IChatWidgetService } from '../chat.js'; import { DefaultChatTextEditor, ICodeBlockActionContext, ICodeCompareBlockActionContext } from '../codeBlockPart.js'; import { CHAT_CATEGORY } from './chatActions.js'; @@ -450,8 +450,9 @@ export function registerChatCodeBlockActions() { const focused = !widget.inputEditor.hasWidgetFocus() && widget.getFocus(); const focusedResponse = isResponseVM(focused) ? focused : undefined; - const currentResponse = curCodeBlockInfo ? - curCodeBlockInfo.element : + const elementId = curCodeBlockInfo?.elementId; + const element = elementId ? widget.viewModel?.getItems().find(item => item.id === elementId) : undefined; + const currentResponse = element ?? (focusedResponse ?? widget.viewModel?.getItems().reverse().find((item): item is IChatResponseViewModel => isResponseVM(item))); if (!currentResponse || !isResponseVM(currentResponse)) { return; @@ -531,8 +532,9 @@ function getContextFromEditor(editor: ICodeEditor, accessor: ServicesAccessor): return; } + const element = widget?.viewModel?.getItems().find(item => item.id === codeBlockInfo.elementId); return { - element: codeBlockInfo.element, + element, codeBlockIndex: codeBlockInfo.codeBlockIndex, code: editor.getValue(), languageId: editor.getModel()!.getLanguageId(), diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts index de0ad326f514..ed203d960c1e 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts @@ -5,6 +5,7 @@ import { CancellationToken } from '../../../../../base/common/cancellation.js'; import { Codicon } from '../../../../../base/common/codicons.js'; +import { ResolvedKeybinding } from '../../../../../base/common/keybindings.js'; import { KeyCode, KeyMod } from '../../../../../base/common/keyCodes.js'; import { Schemas } from '../../../../../base/common/network.js'; import { isElectron } from '../../../../../base/common/platform.js'; @@ -25,6 +26,7 @@ import { ICommandService } from '../../../../../platform/commands/common/command import { ContextKeyExpr, IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js'; import { IFileService } from '../../../../../platform/files/common/files.js'; import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; +import { IKeybindingService } from '../../../../../platform/keybinding/common/keybinding.js'; import { KeybindingWeight } from '../../../../../platform/keybinding/common/keybindingsRegistry.js'; import { ILabelService } from '../../../../../platform/label/common/label.js'; import { AnythingQuickAccessProviderRunOptions } from '../../../../../platform/quickinput/common/quickAccess.js'; @@ -46,27 +48,31 @@ import { isSearchTreeFileMatch, isSearchTreeMatch } from '../../../search/browse import { SearchView } from '../../../search/browser/searchView.js'; import { ISymbolQuickPickItem, SymbolsQuickAccessProvider } from '../../../search/browser/symbolsQuickAccess.js'; import { SearchContext } from '../../../search/common/constants.js'; -import { ChatAgentLocation, IChatAgentService } from '../../common/chatAgents.js'; -import { ChatContextKeys } from '../../common/chatContextKeys.js'; +import { IChatAgentService } from '../../common/chatAgents.js'; +import { ChatContextKeyExprs, ChatContextKeys } from '../../common/chatContextKeys.js'; import { IChatEditingService } from '../../common/chatEditingService.js'; -import { IChatRequestVariableEntry } from '../../common/chatModel.js'; +import { IChatRequestVariableEntry, IDiagnosticVariableEntryFilterData } from '../../common/chatModel.js'; import { ChatRequestAgentPart } from '../../common/chatParserTypes.js'; import { IChatVariablesService } from '../../common/chatVariables.js'; +import { ChatAgentLocation } from '../../common/constants.js'; import { ILanguageModelToolsService } from '../../common/languageModelToolsService.js'; import { IChatWidget, IChatWidgetService, IQuickChatService, showChatView, showEditsView } from '../chat.js'; import { imageToHash, isImage } from '../chatPasteProviders.js'; import { isQuickChat } from '../chatWidget.js'; -import { createFolderQuickPick } from '../contrib/chatDynamicVariables.js'; +import { createFolderQuickPick, createMarkersQuickPick } from '../contrib/chatDynamicVariables.js'; import { convertBufferToScreenshotVariable, ScreenshotVariableId } from '../contrib/screenshot.js'; import { resizeImage } from '../imageUtils.js'; +import { COMMAND_ID as USE_PROMPT_COMMAND_ID } from '../promptSyntax/contributions/usePromptCommand.js'; import { CHAT_CATEGORY } from './chatActions.js'; -import { ATTACH_PROMPT_ACTION_ID, AttachPromptAction, IChatAttachPromptActionOptions } from './chatAttachPromptAction.js'; +import { ATTACH_PROMPT_ACTION_ID, AttachPromptAction, IChatAttachPromptActionOptions } from './chatAttachPromptAction/chatAttachPromptAction.js'; export function registerChatContextActions() { registerAction2(AttachContextAction); registerAction2(AttachFileToChatAction); + registerAction2(AttachFolderToChatAction); registerAction2(AttachSelectionToChatAction); registerAction2(AttachFileToEditingSessionAction); + registerAction2(AttachFolderToEditingSessionAction); registerAction2(AttachSelectionToEditingSessionAction); } @@ -75,7 +81,7 @@ export function registerChatContextActions() { */ type IAttachmentQuickPickItem = ICommandVariableQuickPickItem | IQuickAccessQuickPickItem | IToolQuickPickItem | IImageQuickPickItem | IOpenEditorsQuickPickItem | ISearchResultsQuickPickItem | - IScreenShotQuickPickItem | IRelatedFilesQuickPickItem | IPromptInstructionsQuickPickItem | IFolderQuickPickItem; + IScreenShotQuickPickItem | IRelatedFilesQuickPickItem | IReusablePromptQuickPickItem | IFolderQuickPickItem | IDiagnosticsQuickPickItem; /** * These are the types that we can get out of the quick pick @@ -103,6 +109,12 @@ function isIFolderSearchResultQuickPickItem(obj: unknown): obj is IFolderResultQ && (obj as IFolderResultQuickPickItem).kind === 'folder-search-result'); } +function isIDiagnosticsQuickPickItemWithFilter(obj: unknown): obj is IDiagnosticsQuickPickItemWithFilter { + return ( + typeof obj === 'object' + && (obj as IDiagnosticsQuickPickItemWithFilter).kind === 'diagnostic-filter'); +} + function isIQuickPickItemWithResource(obj: unknown): obj is IQuickPickItemWithResource { return ( typeof obj === 'object' @@ -138,12 +150,12 @@ function isRelatedFileQuickPickItem(obj: unknown): obj is IRelatedFilesQuickPick /** * Checks is a provided object is a prompt instructions quick pick item. */ -function isPromptInstructionsQuickPickItem(obj: unknown): obj is IPromptInstructionsQuickPickItem { +function isPromptInstructionsQuickPickItem(obj: unknown): obj is IReusablePromptQuickPickItem { if (!obj || typeof obj !== 'object') { return false; } - return ('kind' in obj && obj.kind === 'prompt-instructions'); + return ('kind' in obj && obj.kind === 'reusable-prompt'); } interface IRelatedFilesQuickPickItem extends IQuickPickItem { @@ -210,24 +222,42 @@ interface IScreenShotQuickPickItem extends IQuickPickItem { icon?: ThemeIcon; } +interface IDiagnosticsQuickPickItem extends IQuickPickItem { + kind: 'diagnostic'; + id: string; + icon?: ThemeIcon; +} + +interface IDiagnosticsQuickPickItemWithFilter extends IQuickPickItem { + kind: 'diagnostic-filter'; + id: string; + filter: IDiagnosticVariableEntryFilterData; + icon?: ThemeIcon; +} + /** - * Quick pick item for prompt instructions attachment. + * Quick pick item for reusable prompt attachment. */ -interface IPromptInstructionsQuickPickItem extends IQuickPickItem { +const REUSABLE_PROMPT_PICK_ID = 'reusable-prompt'; +interface IReusablePromptQuickPickItem extends IQuickPickItem { /** - * Unique kind identifier of the prompt instructions - * attachment quick pick item. + * The ID of the quick pick item. */ - kind: 'prompt-instructions'; + id: typeof REUSABLE_PROMPT_PICK_ID; /** - * The id of the qucik pick item. + * Unique kind identifier of the reusable prompt attachment. */ - id: string; + kind: typeof REUSABLE_PROMPT_PICK_ID; + + /** + * Keybinding of the command. + */ + keybinding?: ResolvedKeybinding; } -abstract class AttachFileAction extends Action2 { - getFiles(accessor: ServicesAccessor, ...args: any[]): URI[] { +abstract class AttachResourceAction extends Action2 { + getResources(accessor: ServicesAccessor, ...args: any[]): URI[] { const editorService = accessor.get(IEditorService); const contexts = Array.isArray(args[1]) ? args[1] : [args[0]]; @@ -253,7 +283,7 @@ abstract class AttachFileAction extends Action2 { } } -class AttachFileToChatAction extends AttachFileAction { +class AttachFileToChatAction extends AttachResourceAction { static readonly ID = 'workbench.action.chat.attachFile'; @@ -263,22 +293,18 @@ class AttachFileToChatAction extends AttachFileAction { title: localize2('workbench.action.chat.attachFile.label', "Add File to Chat"), category: CHAT_CATEGORY, f1: false, - precondition: ContextKeyExpr.and(ChatContextKeys.enabled, ContextKeyExpr.or(ActiveEditorContext.isEqualTo(TEXT_FILE_EDITOR_ID), TextCompareEditorActiveContext)), menu: [{ - id: MenuId.ChatTitleBarMenu, - group: 'b_chat_context', - order: 15, - }, { id: MenuId.SearchContext, group: 'z_chat', - order: 1 + order: 1, + when: ContextKeyExpr.and(ChatContextKeys.enabled, ContextKeyExpr.or(ActiveEditorContext.isEqualTo(TEXT_FILE_EDITOR_ID), TextCompareEditorActiveContext)), }] }); } override async run(accessor: ServicesAccessor, ...args: any[]): Promise { const variablesService = accessor.get(IChatVariablesService); - const files = this.getFiles(accessor, ...args); + const files = this.getResources(accessor, ...args); if (files.length) { (await showChatView(accessor.get(IViewsService)))?.focusInput(); @@ -289,6 +315,32 @@ class AttachFileToChatAction extends AttachFileAction { } } +class AttachFolderToChatAction extends AttachResourceAction { + + static readonly ID = 'workbench.action.chat.attachFolder'; + + constructor() { + super({ + id: AttachFolderToChatAction.ID, + title: localize2('workbench.action.chat.attachFolder.label', "Add Folder to Chat"), + category: CHAT_CATEGORY, + f1: false, + }); + } + + override async run(accessor: ServicesAccessor, ...args: any[]): Promise { + const variablesService = accessor.get(IChatVariablesService); + const folders = this.getResources(accessor, ...args); + + if (folders.length) { + (await showChatView(accessor.get(IViewsService)))?.focusInput(); + for (const folder of folders) { + variablesService.attachContext('folder', folder, ChatAgentLocation.Panel); + } + } + } +} + class AttachSelectionToChatAction extends Action2 { static readonly ID = 'workbench.action.chat.attachSelection'; @@ -299,16 +351,6 @@ class AttachSelectionToChatAction extends Action2 { title: localize2('workbench.action.chat.attachSelection.label', "Add Selection to Chat"), category: CHAT_CATEGORY, f1: false, - precondition: ContextKeyExpr.and(ChatContextKeys.enabled, ContextKeyExpr.or(ActiveEditorContext.isEqualTo(TEXT_FILE_EDITOR_ID), TextCompareEditorActiveContext)), - menu: [{ - id: MenuId.ChatTitleBarMenu, - group: 'b_chat_context', - order: 10, - }, { - id: MenuId.SearchContext, - group: 'z_chat', - order: 2 - }] }); } @@ -354,7 +396,7 @@ class AttachSelectionToChatAction extends Action2 { } } -class AttachFileToEditingSessionAction extends AttachFileAction { +class AttachFileToEditingSessionAction extends AttachResourceAction { static readonly ID = 'workbench.action.edits.attachFile'; @@ -364,22 +406,21 @@ class AttachFileToEditingSessionAction extends AttachFileAction { title: localize2('workbench.action.edits.attachFile.label', "Add File to {0}", 'Copilot Edits'), category: CHAT_CATEGORY, f1: false, - precondition: ContextKeyExpr.and(ChatContextKeys.enabled, ContextKeyExpr.or(ActiveEditorContext.isEqualTo(TEXT_FILE_EDITOR_ID), TextCompareEditorActiveContext)), menu: [{ - id: MenuId.ChatTitleBarMenu, - group: 'c_edits_context', - order: 15, - }, { id: MenuId.SearchContext, group: 'z_chat', - order: 2 + order: 2, + when: ContextKeyExpr.and( + ChatContextKeys.enabled, + ContextKeyExpr.or(ActiveEditorContext.isEqualTo(TEXT_FILE_EDITOR_ID), TextCompareEditorActiveContext), + ChatContextKeyExprs.unifiedChatEnabled.negate()), }] }); } override async run(accessor: ServicesAccessor, ...args: any[]): Promise { const variablesService = accessor.get(IChatVariablesService); - const files = this.getFiles(accessor, ...args); + const files = this.getResources(accessor, ...args); if (files.length) { (await showEditsView(accessor.get(IViewsService)))?.focusInput(); @@ -390,6 +431,35 @@ class AttachFileToEditingSessionAction extends AttachFileAction { } } +class AttachFolderToEditingSessionAction extends AttachResourceAction { + + static readonly ID = 'workbench.action.edits.attachFolder'; + + constructor() { + super({ + id: AttachFolderToEditingSessionAction.ID, + title: localize2('workbench.action.edits.attachFolder.label', "Add Folder to {0}", 'Copilot Edits'), + category: CHAT_CATEGORY, + f1: false, + precondition: ContextKeyExpr.and( + ChatContextKeys.enabled, + ChatContextKeyExprs.unifiedChatEnabled.negate()), + }); + } + + override async run(accessor: ServicesAccessor, ...args: any[]): Promise { + const variablesService = accessor.get(IChatVariablesService); + const folders = this.getResources(accessor, ...args); + + if (folders.length) { + (await showEditsView(accessor.get(IViewsService)))?.focusInput(); + for (const folder of folders) { + variablesService.attachContext('folder', folder, ChatAgentLocation.EditingSession); + } + } + } +} + class AttachSelectionToEditingSessionAction extends Action2 { static readonly ID = 'workbench.action.edits.attachSelection'; @@ -400,12 +470,11 @@ class AttachSelectionToEditingSessionAction extends Action2 { title: localize2('workbench.action.edits.attachSelection.label', "Add Selection to {0}", 'Copilot Edits'), category: CHAT_CATEGORY, f1: false, - precondition: ContextKeyExpr.and(ChatContextKeys.enabled, ContextKeyExpr.or(ActiveEditorContext.isEqualTo(TEXT_FILE_EDITOR_ID), TextCompareEditorActiveContext)), - menu: { - id: MenuId.ChatTitleBarMenu, - group: 'c_edits_context', - order: 10, - } + precondition: ContextKeyExpr.and( + ChatContextKeys.enabled, + ContextKeyExpr.or(ActiveEditorContext.isEqualTo(TEXT_FILE_EDITOR_ID), TextCompareEditorActiveContext), + ChatContextKeyExprs.unifiedChatEnabled.negate() + ) }); } @@ -430,38 +499,26 @@ export class AttachContextAction extends Action2 { static readonly ID = 'workbench.action.chat.attachContext'; - // used to enable/disable the keybinding and defined menu containment - protected static _cdt = ContextKeyExpr.or( - ContextKeyExpr.and(ChatContextKeys.location.isEqualTo(ChatAgentLocation.Panel)), - ContextKeyExpr.and(ChatContextKeys.location.isEqualTo(ChatAgentLocation.Editor)), - ContextKeyExpr.and(ChatContextKeys.location.isEqualTo(ChatAgentLocation.Notebook)), - ContextKeyExpr.and(ChatContextKeys.location.isEqualTo(ChatAgentLocation.Terminal)), - ); - constructor(desc: Readonly = { id: AttachContextAction.ID, - title: localize2('workbench.action.chat.attachContext.label', "Attach Context"), + title: localize2('workbench.action.chat.attachContext.label.2', "Add Context"), icon: Codicon.attach, category: CHAT_CATEGORY, - precondition: ContextKeyExpr.or(AttachContextAction._cdt, ContextKeyExpr.and(ChatContextKeys.location.isEqualTo(ChatAgentLocation.EditingSession))), keybinding: { - when: ContextKeyExpr.and(ChatContextKeys.location.notEqualsTo(ChatAgentLocation.EditingSession), ChatContextKeys.inChatInput), + when: ContextKeyExpr.and( + ChatContextKeys.location.notEqualsTo(ChatAgentLocation.EditingSession), + ChatContextKeys.inChatInput, + ChatContextKeyExprs.inNonUnifiedPanel), primary: KeyMod.CtrlCmd | KeyCode.Slash, weight: KeybindingWeight.EditorContrib }, menu: [ { - when: AttachContextAction._cdt, - id: MenuId.ChatInput, + when: ChatContextKeyExprs.inNonUnifiedPanel, + id: MenuId.ChatInputAttachmentToolbar, group: 'navigation', order: 2 - }, - { - when: ContextKeyExpr.and(ChatContextKeys.location.isEqualTo(ChatAgentLocation.Panel).negate(), AttachContextAction._cdt), - id: MenuId.ChatExecute, - group: 'navigation', - order: 1 - }, + } ] }) { super(desc); @@ -499,6 +556,15 @@ export class AttachContextAction extends Action2 { isFile: false, isDirectory: true, }); + } else if (isIDiagnosticsQuickPickItemWithFilter(pick)) { + toAttach.push({ + id: pick.id, + name: pick.label, + value: pick.filter, + kind: 'diagnostic', + icon: pick.icon, + ...pick.filter, + }); } else if (isIQuickPickItemWithResource(pick) && pick.resource) { if (/\.(png|jpg|jpeg|bmp|gif|tiff)$/i.test(pick.resource.path)) { // checks if the file is an image @@ -589,7 +655,13 @@ export class AttachContextAction extends Action2 { }, [])); const selectedFiles = await quickInputService.pick(itemsPromise, { placeHolder: localize('relatedFiles', 'Add related files to your working set'), canPickMany: true }); for (const file of selectedFiles ?? []) { - chatEditingService.getEditingSession(chatSessionId)?.addFileToWorkingSet(file.value); + toAttach.push({ + id: this._getFileContextId({ resource: file.value }), + value: file.value, + name: file.label, + isFile: true, + isOmitted: false + }); } } else if (isScreenshotQuickPickItem(pick)) { const blob = await hostService.getScreenshot(); @@ -597,7 +669,7 @@ export class AttachContextAction extends Action2 { toAttach.push(convertBufferToScreenshotVariable(blob)); } } else if (isPromptInstructionsQuickPickItem(pick)) { - const options: IChatAttachPromptActionOptions = { widget }; + const options: IChatAttachPromptActionOptions = { widget, viewsService }; await commandService.executeCommand(ATTACH_PROMPT_ACTION_ID, options); } else { // Anything else is an attachment @@ -663,13 +735,14 @@ export class AttachContextAction extends Action2 { const fileService = accessor.get(IFileService); const textModelService = accessor.get(ITextModelService); const instantiationService = accessor.get(IInstantiationService); + const keybindingService = accessor.get(IKeybindingService); const context: { widget?: IChatWidget; showFilesOnly?: boolean; placeholder?: string } | undefined = args[0]; const widget = context?.widget ?? widgetService.lastFocusedWidget; if (!widget) { return; } - const chatEditingService = widget.location === ChatAgentLocation.EditingSession ? accessor.get(IChatEditingService) : undefined; + const chatEditingService = widget.location === ChatAgentLocation.EditingSession || widget.isUnifiedPanelWidget ? accessor.get(IChatEditingService) : undefined; const quickPickItems: IAttachmentQuickPickItem[] = []; if (extensionService.extensions.some(ext => isProposedApiEnabled(ext, 'chatReferenceBinaryData'))) { @@ -750,6 +823,13 @@ export class AttachContextAction extends Action2 { id: 'folder', }); + quickPickItems.push({ + kind: 'diagnostic', + label: localize('chatContext.diagnstic', 'Problem...'), + iconClass: ThemeIcon.asClassName(Codicon.error), + id: 'diagnostic' + }); + if (widget.location === ChatAgentLocation.Notebook) { quickPickItems.push({ kind: 'command', @@ -767,7 +847,7 @@ export class AttachContextAction extends Action2 { } if (context?.showFilesOnly) { - if (chatEditingService?.hasRelatedFilesProviders() && (widget.getInput() || (getEditingSession(chatEditingService, widget)?.workingSet.size))) { + if (chatEditingService?.hasRelatedFilesProviders() && (widget.getInput() || widget.attachmentModel.fileAttachments.length > 0)) { quickPickItems.unshift({ kind: 'related-files', id: 'related-files', @@ -793,14 +873,17 @@ export class AttachContextAction extends Action2 { } } - // if the `prompt instructions` feature is enabled, add - // the `Instructions` attachment type to the list + // if the `reusable prompts` feature is enabled, add + // the appropriate attachment type to the list if (widget.attachmentModel.promptInstructions.featureEnabled) { + const keybinding = keybindingService.lookupKeybinding(USE_PROMPT_COMMAND_ID, contextKeyService); + quickPickItems.push({ - kind: 'prompt-instructions', - id: 'prompt-instructions', - label: localize('promptWithEllipsis', 'Prompt...'), + id: REUSABLE_PROMPT_PICK_ID, + kind: REUSABLE_PROMPT_PICK_ID, + label: localize('chatContext.attach.prompt.label', 'Prompt...'), iconClass: ThemeIcon.asClassName(Codicon.bookmark), + keybinding, }); } @@ -824,16 +907,37 @@ export class AttachContextAction extends Action2 { }), clipboardService, editorService, labelService, viewsService, chatEditingService, hostService, fileService, textModelService, instantiationService, '', context?.placeholder); } + private async _showDiagnosticsPick(instantiationService: IInstantiationService, onBackgroundAccept: (item: IChatContextQuickPickItem[]) => void): Promise { + const convert = (item: IDiagnosticVariableEntryFilterData): IDiagnosticsQuickPickItemWithFilter => ({ + kind: 'diagnostic-filter', + id: IDiagnosticVariableEntryFilterData.id(item), + label: IDiagnosticVariableEntryFilterData.label(item), + icon: IDiagnosticVariableEntryFilterData.icon, + filter: item, + }); + + const filter = await instantiationService.invokeFunction(accessor => + createMarkersQuickPick(accessor, 'problem', items => onBackgroundAccept(items.map(convert)))); + return filter && convert(filter); + } + private _show(quickInputService: IQuickInputService, commandService: ICommandService, widget: IChatWidget, quickChatService: IQuickChatService, quickPickItems: (IChatContextQuickPickItem | QuickPickItem)[] | undefined, clipboardService: IClipboardService, editorService: IEditorService, labelService: ILabelService, viewsService: IViewsService, chatEditingService: IChatEditingService | undefined, hostService: IHostService, fileService: IFileService, textModelService: ITextModelService, instantiationService: IInstantiationService, query: string = '', placeholder?: string) { + const attach = (isBackgroundAccept: boolean, ...items: IChatContextQuickPickItem[]) => { + this._attachContext(widget, quickInputService, commandService, clipboardService, editorService, labelService, viewsService, chatEditingService, hostService, fileService, textModelService, isBackgroundAccept, ...items); + }; + const providerOptions: AnythingQuickAccessProviderRunOptions = { - handleAccept: async (item: IChatContextQuickPickItem, isBackgroundAccept: boolean) => { + handleAccept: async (inputItem: IChatContextQuickPickItem, isBackgroundAccept: boolean) => { + let item: IChatContextQuickPickItem | undefined = inputItem; if ('kind' in item && item.kind === 'folder') { - const folderItem = await this._showFolders(instantiationService); - if (!folderItem) { - this._show(quickInputService, commandService, widget, quickChatService, quickPickItems, clipboardService, editorService, labelService, viewsService, chatEditingService, hostService, fileService, textModelService, instantiationService, '', placeholder); - return; - } - item = folderItem; + item = await this._showFolders(instantiationService); + } else if ('kind' in item && item.kind === 'diagnostic') { + item = await this._showDiagnosticsPick(instantiationService, i => attach(true, ...i)); + } + + if (!item) { + this._show(quickInputService, commandService, widget, quickChatService, quickPickItems, clipboardService, editorService, labelService, viewsService, chatEditingService, hostService, fileService, textModelService, instantiationService, '', placeholder); + return; } if ('prefix' in item) { @@ -842,7 +946,7 @@ export class AttachContextAction extends Action2 { if (!clipboardService) { return; } - this._attachContext(widget, quickInputService, commandService, clipboardService, editorService, labelService, viewsService, chatEditingService, hostService, fileService, textModelService, isBackgroundAccept, item); + attach(isBackgroundAccept, item); if (isQuickChat(widget)) { quickChatService.open(); } @@ -917,13 +1021,21 @@ export class AttachContextAction extends Action2 { registerAction2(class AttachFilesAction extends AttachContextAction { constructor() { super({ - id: 'workbench.action.chat.editing.attachFiles', - title: localize2('workbench.action.chat.editing.attachFiles.label', "Add Files to Copilot Edits"), + id: 'workbench.action.chat.editing.attachContext', + title: localize2('workbench.action.chat.editing.attachContext.label', "Add Context to Copilot Edits"), + shortTitle: localize2('workbench.action.chat.editing.attachContext.shortLabel', "Add Context..."), f1: false, category: CHAT_CATEGORY, - precondition: ChatContextKeys.location.isEqualTo(ChatAgentLocation.EditingSession), + menu: { + when: ChatContextKeyExprs.inEditsOrUnified, + id: MenuId.ChatInputAttachmentToolbar, + group: 'navigation', + order: 3 + }, + icon: Codicon.attach, + precondition: ChatContextKeyExprs.inEditsOrUnified, keybinding: { - when: ContextKeyExpr.and(ChatContextKeys.inChatInput, ChatContextKeys.location.isEqualTo(ChatAgentLocation.EditingSession)), + when: ContextKeyExpr.and(ChatContextKeys.inChatInput, ChatContextKeyExprs.inEditsOrUnified), primary: KeyMod.CtrlCmd | KeyCode.Slash, weight: KeybindingWeight.EditorContrib } @@ -937,11 +1049,4 @@ registerAction2(class AttachFilesAction extends AttachContextAction { } }); -function getEditingSession(chatEditingService: IChatEditingService, chatWidget: IChatWidget) { - if (!chatWidget.viewModel?.sessionId) { - return; - } - return chatEditingService.getEditingSession(chatWidget.viewModel.sessionId); -} - registerAction2(AttachPromptAction); diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatDeveloperActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatDeveloperActions.ts index 4d9afa63ee41..aa3b3db690bb 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatDeveloperActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatDeveloperActions.ts @@ -8,6 +8,7 @@ import { ServicesAccessor } from '../../../../../editor/browser/editorExtensions import { localize2 } from '../../../../../nls.js'; import { Categories } from '../../../../../platform/action/common/actionCommonCategories.js'; import { Action2, registerAction2 } from '../../../../../platform/actions/common/actions.js'; +import { ChatContextKeys } from '../../common/chatContextKeys.js'; import { IChatWidgetService } from '../chat.js'; export function registerChatDeveloperActions() { @@ -24,7 +25,8 @@ class LogChatInputHistoryAction extends Action2 { title: localize2('workbench.action.chat.logInputHistory.label', "Log Chat Input History"), icon: Codicon.attach, category: Categories.Developer, - f1: true + f1: true, + precondition: ChatContextKeys.enabled }); } diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatExecuteActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatExecuteActions.ts index 59befe2e8344..e101b5f1a04b 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatExecuteActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatExecuteActions.ts @@ -5,25 +5,25 @@ import { Codicon } from '../../../../../base/common/codicons.js'; import { KeyCode, KeyMod } from '../../../../../base/common/keyCodes.js'; -import { URI } from '../../../../../base/common/uri.js'; import { ServicesAccessor } from '../../../../../editor/browser/editorExtensions.js'; import { localize, localize2 } from '../../../../../nls.js'; -import { Action2, MenuId, MenuRegistry, registerAction2 } from '../../../../../platform/actions/common/actions.js'; +import { Action2, MenuId, registerAction2 } from '../../../../../platform/actions/common/actions.js'; import { ICommandService } from '../../../../../platform/commands/common/commands.js'; import { ContextKeyExpr } from '../../../../../platform/contextkey/common/contextkey.js'; import { IDialogService } from '../../../../../platform/dialogs/common/dialogs.js'; import { KeybindingWeight } from '../../../../../platform/keybinding/common/keybindingsRegistry.js'; import { IViewsService } from '../../../../services/views/common/viewsService.js'; -import { ChatAgentLocation, IChatAgentService } from '../../common/chatAgents.js'; -import { ChatContextKeys } from '../../common/chatContextKeys.js'; +import { IChatAgentService } from '../../common/chatAgents.js'; +import { ChatContextKeyExprs, ChatContextKeys } from '../../common/chatContextKeys.js'; import { IChatEditingService, IChatEditingSession, WorkingSetEntryState } from '../../common/chatEditingService.js'; import { chatAgentLeader, extractAgentAndCommand } from '../../common/chatParserTypes.js'; import { IChatService } from '../../common/chatService.js'; +import { ChatAgentLocation, ChatMode } from '../../common/constants.js'; import { EditsViewId, IChatWidget, IChatWidgetService } from '../chat.js'; -import { discardAllEditsWithConfirmation, EditingSessionAction } from '../chatEditing/chatEditingActions.js'; +import { discardAllEditsWithConfirmation, getEditingSessionContext } from '../chatEditing/chatEditingActions.js'; import { ChatViewPane } from '../chatViewPane.js'; import { CHAT_CATEGORY } from './chatActions.js'; -import { ChatDoneActionId } from './chatClearActions.js'; +import { ACTION_ID_NEW_CHAT, ChatDoneActionId } from './chatClearActions.js'; export interface IVoiceChatExecuteActionContext { readonly disableTimeout?: boolean; @@ -45,6 +45,8 @@ abstract class SubmitAction extends Action2 { } } +const whenNotInProgressOrPaused = ContextKeyExpr.or(ChatContextKeys.isRequestPaused, ChatContextKeys.requestInProgress.negate()); + export class ChatSubmitAction extends SubmitAction { static readonly ID = 'workbench.action.chat.submit'; @@ -53,8 +55,8 @@ export class ChatSubmitAction extends SubmitAction { // if the input has prompt instructions attached, allow submitting requests even // without text present - having instructions is enough context for a request ContextKeyExpr.or(ChatContextKeys.inputHasText, ChatContextKeys.instructionsAttached), - ChatContextKeys.requestInProgress.negate(), - ChatContextKeys.location.notEqualsTo(ChatAgentLocation.EditingSession), + whenNotInProgressOrPaused, + ChatContextKeys.chatMode.isEqualTo(ChatMode.Chat), ); super({ @@ -79,8 +81,8 @@ export class ChatSubmitAction extends SubmitAction { id: MenuId.ChatExecute, order: 4, when: ContextKeyExpr.and( - ContextKeyExpr.or(ChatContextKeys.isRequestPaused, ChatContextKeys.requestInProgress.negate()), - ChatContextKeys.location.notEqualsTo(ChatAgentLocation.EditingSession), + whenNotInProgressOrPaused, + ChatContextKeys.chatMode.isEqualTo(ChatMode.Chat), ), group: 'navigation', }, @@ -91,32 +93,31 @@ export class ChatSubmitAction extends SubmitAction { export const ToggleAgentModeActionId = 'workbench.action.chat.toggleAgentMode'; -export interface IToggleAgentModeArgs { - agentMode: boolean; +export interface IToggleChatModeArgs { + mode: ChatMode; } -export class ToggleAgentModeAction extends EditingSessionAction { +class ToggleChatModeAction extends Action2 { static readonly ID = ToggleAgentModeActionId; constructor() { super({ - id: ToggleAgentModeAction.ID, - title: localize2('interactive.toggleAgent.label', "Toggle Agent Mode (Experimental)"), + id: ToggleChatModeAction.ID, + title: localize2('interactive.toggleAgent.label', "Set Chat Mode (Experimental)"), f1: true, category: CHAT_CATEGORY, precondition: ContextKeyExpr.and( - ChatContextKeys.Editing.hasToolsAgent, + ChatContextKeys.enabled, + ContextKeyExpr.or( + ChatContextKeys.Editing.hasToolsAgent, + ChatContextKeyExprs.unifiedChatEnabled), ChatContextKeys.requestInProgress.negate()), - toggled: { - condition: ChatContextKeys.Editing.agentMode, - tooltip: localize('agentEnabled', "Agent Mode Enabled (Experimental)"), - }, - tooltip: localize('agentDisabled', "Agent Mode Disabled"), + tooltip: localize('setChatMode', "Set Mode (Experimental)"), keybinding: { when: ContextKeyExpr.and( ChatContextKeys.inChatInput, - ChatContextKeys.location.isEqualTo(ChatAgentLocation.EditingSession)), + ChatContextKeyExprs.inEditsOrUnified), primary: KeyMod.CtrlCmd | KeyCode.Period, weight: KeybindingWeight.EditorContrib }, @@ -124,34 +125,49 @@ export class ToggleAgentModeAction extends EditingSessionAction { { id: MenuId.ChatExecute, order: 1, + // Either in edits with agent mode available, or in unified chat view when: ContextKeyExpr.and( - ChatContextKeys.location.isEqualTo(ChatAgentLocation.EditingSession), - ChatContextKeys.Editing.hasToolsAgent), + ChatContextKeys.enabled, + ContextKeyExpr.or( + ContextKeyExpr.and( + ChatContextKeys.location.isEqualTo(ChatAgentLocation.EditingSession), + ChatContextKeys.Editing.hasToolsAgent, + ), + ChatContextKeys.inUnifiedChat)), group: 'navigation', }, ] }); } - override async runEditingSessionAction(accessor: ServicesAccessor, currentEditingSession: IChatEditingSession, chatWidget: IChatWidget, ...args: any[]) { - - const agentService = accessor.get(IChatAgentService); + async run(accessor: ServicesAccessor, ...args: any[]) { const chatService = accessor.get(IChatService); const commandService = accessor.get(ICommandService); const dialogService = accessor.get(IDialogService); - const entries = currentEditingSession.entries.get(); - if (entries.length > 0 && entries.some(entry => entry.state.get() === WorkingSetEntryState.Modified)) { - if (!await discardAllEditsWithConfirmation(accessor, currentEditingSession)) { + const context = getEditingSessionContext(accessor, args); + if (!context?.chatWidget) { + return; + } + + const arg = args.at(0) as IToggleChatModeArgs | undefined; + if (arg?.mode === context.chatWidget.input.currentMode) { + return; + } + + // TODO will not require discarding the session when we are able to switch modes mid-session + const entries = context.editingSession?.entries.get(); + if (context.editingSession && entries && entries.length > 0 && entries.some(entry => entry.state.get() === WorkingSetEntryState.Modified)) { + if (!await discardAllEditsWithConfirmation(accessor, context.editingSession)) { // User cancelled return; } } else { - const chatSession = chatService.getSession(currentEditingSession.chatSessionId); + const chatSession = context.chatWidget.viewModel?.model; if (chatSession?.getRequests().length) { const confirmation = await dialogService.confirm({ title: localize('agent.newSession', "Start new session?"), - message: localize('agent.newSessionMessage', "Toggling agent mode will start a new session. Would you like to continue?"), + message: localize('agent.newSessionMessage', "Changing the chat mode will start a new session. Would you like to continue?"), primaryButton: localize('agent.newSession.confirm', "Yes"), type: 'info' }); @@ -161,10 +177,23 @@ export class ToggleAgentModeAction extends EditingSessionAction { } } - const arg = args[0] as IToggleAgentModeArgs | undefined; - agentService.toggleToolsAgentMode(typeof arg?.agentMode === 'boolean' ? arg.agentMode : undefined); + if (arg?.mode) { + context.chatWidget.input.setChatMode(arg.mode); + } else { + const modes = [ChatMode.Agent, ChatMode.Edit]; + if (context.chatWidget.location === ChatAgentLocation.Panel) { + modes.push(ChatMode.Chat); + } + + const modeIndex = modes.indexOf(context.chatWidget.input.currentMode); + const newMode = modes[(modeIndex + 1) % modes.length]; + context.chatWidget.input.setChatMode(newMode); + } - await commandService.executeCommand(ChatDoneActionId); + if (context.chatWidget.viewModel?.model.getRequests().length) { + const clearAction = chatService.unifiedViewEnabled ? ACTION_ID_NEW_CHAT : ChatDoneActionId; + await commandService.executeCommand(clearAction); + } } } @@ -190,8 +219,9 @@ export class ToggleRequestPausedAction extends Action2 { order: 3.5, when: ContextKeyExpr.and( ChatContextKeys.canRequestBePaused, - ChatContextKeys.Editing.agentMode, - ChatContextKeys.location.isEqualTo(ChatAgentLocation.EditingSession), + ChatContextKeys.chatMode.isEqualTo(ChatMode.Agent), + ChatContextKeyExprs.inEditsOrUnified, + ContextKeyExpr.or(ChatContextKeys.isRequestPaused.negate(), ChatContextKeys.inputHasText.negate()), ), group: 'navigation', }, @@ -207,6 +237,47 @@ export class ToggleRequestPausedAction extends Action2 { } } +export const ChatSwitchToNextModelActionId = 'workbench.action.chat.switchToNextModel'; +export class SwitchToNextModelAction extends Action2 { + static readonly ID = ChatSwitchToNextModelActionId; + + constructor() { + super({ + id: SwitchToNextModelAction.ID, + title: localize2('interactive.switchToNextModel.label', "Switch to Next Model"), + category: CHAT_CATEGORY, + f1: true, + keybinding: { + primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.Period, + weight: KeybindingWeight.WorkbenchContrib, + when: ChatContextKeys.inChatInput + }, + precondition: ChatContextKeys.enabled, + menu: { + id: MenuId.ChatExecute, + order: 3, + group: 'navigation', + when: ContextKeyExpr.and( + ChatContextKeys.languageModelsAreUserSelectable, + ContextKeyExpr.or( + ContextKeyExpr.equals(ChatContextKeys.location.key, ChatAgentLocation.Panel), + ContextKeyExpr.equals(ChatContextKeys.location.key, ChatAgentLocation.EditingSession), + ContextKeyExpr.equals(ChatContextKeys.location.key, ChatAgentLocation.Editor), + ContextKeyExpr.equals(ChatContextKeys.location.key, ChatAgentLocation.Notebook), + ContextKeyExpr.equals(ChatContextKeys.location.key, ChatAgentLocation.Terminal) + ) + ), + } + }); + } + + override run(accessor: ServicesAccessor, ...args: any[]): void { + const widgetService = accessor.get(IChatWidgetService); + const widget = widgetService.lastFocusedWidget; + widget?.input.switchToNextModel(); + } +} + export class ChatEditingSessionSubmitAction extends SubmitAction { static readonly ID = 'workbench.action.edits.submit'; @@ -215,8 +286,8 @@ export class ChatEditingSessionSubmitAction extends SubmitAction { // if the input has prompt instructions attached, allow submitting requests even // without text present - having instructions is enough context for a request ContextKeyExpr.or(ChatContextKeys.inputHasText, ChatContextKeys.instructionsAttached), - ChatContextKeys.requestInProgress.negate(), - ChatContextKeys.location.isEqualTo(ChatAgentLocation.EditingSession), + whenNotInProgressOrPaused, + ChatContextKeys.chatMode.notEqualsTo(ChatMode.Chat), ); super({ @@ -235,13 +306,18 @@ export class ChatEditingSessionSubmitAction extends SubmitAction { { id: MenuId.ChatExecuteSecondary, group: 'group_1', - when: ContextKeyExpr.and(ContextKeyExpr.or(ChatContextKeys.isRequestPaused, ChatContextKeys.requestInProgress.negate()), ChatContextKeys.location.isEqualTo(ChatAgentLocation.EditingSession)), + when: ContextKeyExpr.and(whenNotInProgressOrPaused, ChatContextKeys.chatMode.notEqualsTo(ChatMode.Chat),), order: 1 }, { id: MenuId.ChatExecute, order: 4, - when: ContextKeyExpr.and(ContextKeyExpr.or(ChatContextKeys.isRequestPaused, ChatContextKeys.requestInProgress.negate()), ChatContextKeys.location.isEqualTo(ChatAgentLocation.EditingSession)), + when: ContextKeyExpr.and( + ContextKeyExpr.or( + ContextKeyExpr.and(ChatContextKeys.isRequestPaused, ChatContextKeys.inputHasText), + ChatContextKeys.requestInProgress.negate(), + ), + ChatContextKeys.chatMode.notEqualsTo(ChatMode.Chat),), group: 'navigation', }, ] @@ -257,11 +333,8 @@ class SubmitWithoutDispatchingAction extends Action2 { // if the input has prompt instructions attached, allow submitting requests even // without text present - having instructions is enough context for a request ContextKeyExpr.or(ChatContextKeys.inputHasText, ChatContextKeys.instructionsAttached), - ContextKeyExpr.or(ChatContextKeys.isRequestPaused, ChatContextKeys.requestInProgress.negate()), - ContextKeyExpr.and(ContextKeyExpr.or( - ChatContextKeys.location.isEqualTo(ChatAgentLocation.Panel), - ChatContextKeys.location.isEqualTo(ChatAgentLocation.Editor), - )), + whenNotInProgressOrPaused, + ChatContextKeys.chatMode.isEqualTo(ChatMode.Chat), ); super({ @@ -279,8 +352,9 @@ class SubmitWithoutDispatchingAction extends Action2 { { id: MenuId.ChatExecuteSecondary, group: 'group_1', - order: 2 - } // need 'when'? + order: 2, + when: ChatContextKeys.chatMode.isEqualTo(ChatMode.Chat), + } ] }); } @@ -294,26 +368,6 @@ class SubmitWithoutDispatchingAction extends Action2 { } } -export const ChatModelPickerActionId = 'workbench.action.chat.pickModel'; -MenuRegistry.appendMenuItem(MenuId.ChatExecute, { - command: { - id: ChatModelPickerActionId, - title: localize2('chat.pickModel.label', "Pick Model"), - }, - order: 3, - group: 'navigation', - when: ContextKeyExpr.and( - ChatContextKeys.languageModelsAreUserSelectable, - ContextKeyExpr.or( - ContextKeyExpr.equals(ChatContextKeys.location.key, ChatAgentLocation.Panel), - ContextKeyExpr.equals(ChatContextKeys.location.key, ChatAgentLocation.EditingSession), - ContextKeyExpr.equals(ChatContextKeys.location.key, ChatAgentLocation.Editor), - ContextKeyExpr.equals(ChatContextKeys.location.key, ChatAgentLocation.Notebook), - ContextKeyExpr.equals(ChatContextKeys.location.key, ChatAgentLocation.Terminal) - ) - ), -}); - export class ChatSubmitSecondaryAgentAction extends Action2 { static readonly ID = 'workbench.action.chat.submitSecondaryAgent'; @@ -323,7 +377,8 @@ export class ChatSubmitSecondaryAgentAction extends Action2 { // without text present - having instructions is enough context for a request ContextKeyExpr.or(ChatContextKeys.inputHasText, ChatContextKeys.instructionsAttached), ChatContextKeys.inputHasAgent.negate(), - ContextKeyExpr.or(ChatContextKeys.isRequestPaused, ChatContextKeys.requestInProgress.negate()), + whenNotInProgressOrPaused, + ChatContextKeys.chatMode.isEqualTo(ChatMode.Chat), ); super({ @@ -334,7 +389,10 @@ export class ChatSubmitSecondaryAgentAction extends Action2 { id: MenuId.ChatExecuteSecondary, group: 'group_1', order: 3, - when: ContextKeyExpr.equals(ChatContextKeys.location.key, ChatAgentLocation.Panel) + when: ContextKeyExpr.and( + ContextKeyExpr.equals(ChatContextKeys.location.key, ChatAgentLocation.Panel), + ChatContextKeys.chatMode.isEqualTo(ChatMode.Chat), + ), }, keybinding: { when: ChatContextKeys.inChatInput, @@ -367,14 +425,15 @@ export class ChatSubmitSecondaryAgentAction extends Action2 { } } -class SendToChatEditingAction extends EditingSessionAction { +class SendToChatEditingAction extends Action2 { constructor() { const precondition = ContextKeyExpr.and( // if the input has prompt instructions attached, allow submitting requests even // without text present - having instructions is enough context for a request ContextKeyExpr.or(ChatContextKeys.inputHasText, ChatContextKeys.instructionsAttached), ChatContextKeys.inputHasAgent.negate(), - ContextKeyExpr.or(ChatContextKeys.isRequestPaused, ChatContextKeys.requestInProgress.negate()), + whenNotInProgressOrPaused, + ChatContextKeyExprs.inNonUnifiedPanel ); super({ @@ -391,7 +450,8 @@ class SendToChatEditingAction extends EditingSessionAction { ChatContextKeys.enabled, ChatContextKeys.editingParticipantRegistered, ChatContextKeys.location.notEqualsTo(ChatAgentLocation.EditingSession), - ChatContextKeys.location.notEqualsTo(ChatAgentLocation.Editor) + ChatContextKeys.location.notEqualsTo(ChatAgentLocation.Editor), + ChatContextKeyExprs.inNonUnifiedPanel ) }, keybinding: { @@ -401,20 +461,23 @@ class SendToChatEditingAction extends EditingSessionAction { ChatContextKeys.enabled, ChatContextKeys.editingParticipantRegistered, ChatContextKeys.location.notEqualsTo(ChatAgentLocation.EditingSession), - ChatContextKeys.location.notEqualsTo(ChatAgentLocation.Editor), + ChatContextKeys.location.notEqualsTo(ChatAgentLocation.Editor) ) } }); } - async runEditingSessionAction(accessor: ServicesAccessor, currentEditingSession: IChatEditingSession, widget: IChatWidget, ...args: any[]) { + async run(accessor: ServicesAccessor, ...args: any[]) { if (!accessor.get(IChatAgentService).getDefaultAgent(ChatAgentLocation.EditingSession)) { return; } + const widget = args.length > 0 && args[0].widget ? args[0].widget : accessor.get(IChatWidgetService).lastFocusedWidget; + const viewsService = accessor.get(IViewsService); const dialogService = accessor.get(IDialogService); const chatEditingService = accessor.get(IChatEditingService); + const currentEditingSession: IChatEditingSession | undefined = chatEditingService.editingSessionsObs.get().at(0); const currentEditCount = currentEditingSession?.entries.get().length; if (currentEditCount) { @@ -438,16 +501,12 @@ class SendToChatEditingAction extends EditingSessionAction { if (!editingWidget.viewModel?.sessionId) { return; } - const chatEditingSession = chatEditingService.getEditingSession(editingWidget.viewModel.sessionId); + const chatEditingSession = await chatEditingService.startOrContinueGlobalEditingSession(editingWidget.viewModel.sessionId); if (!chatEditingSession) { return; } for (const attachment of widget.attachmentModel.attachments) { - if (attachment.isFile && URI.isUri(attachment.value)) { - chatEditingSession.addFileToWorkingSet(attachment.value); - } else { - editingWidget.attachmentModel.addContext(attachment); - } + editingWidget.attachmentModel.addContext(attachment); } editingWidget.setInput(widget.getInput()); @@ -464,7 +523,7 @@ class SendToNewChatAction extends Action2 { // if the input has prompt instructions attached, allow submitting requests even // without text present - having instructions is enough context for a request ContextKeyExpr.or(ChatContextKeys.inputHasText, ChatContextKeys.instructionsAttached), - ContextKeyExpr.or(ChatContextKeys.isRequestPaused, ChatContextKeys.requestInProgress.negate()), + whenNotInProgressOrPaused, ); super({ @@ -501,8 +560,9 @@ class SendToNewChatAction extends Action2 { } } +export const CancelChatActionId = 'workbench.action.chat.cancel'; export class CancelAction extends Action2 { - static readonly ID = 'workbench.action.chat.cancel'; + static readonly ID = CancelChatActionId; constructor() { super({ id: CancelAction.ID, @@ -548,6 +608,7 @@ export function registerChatExecuteActions() { registerAction2(SendToNewChatAction); registerAction2(ChatSubmitSecondaryAgentAction); registerAction2(SendToChatEditingAction); - registerAction2(ToggleAgentModeAction); + registerAction2(ToggleChatModeAction); registerAction2(ToggleRequestPausedAction); + registerAction2(SwitchToNextModelAction); } diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatGettingStarted.ts b/src/vs/workbench/contrib/chat/browser/actions/chatGettingStarted.ts index 7beb2b8c1fd4..45ee1c6fedf4 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatGettingStarted.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatGettingStarted.ts @@ -16,7 +16,7 @@ import { IWorkbenchLayoutService } from '../../../../services/layout/browser/lay import { ensureSideBarChatViewSize, showCopilotView } from '../chat.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; import { IViewsService } from '../../../../services/views/common/viewsService.js'; - +import { IStatusbarService } from '../../../../services/statusbar/browser/statusbar.js'; export class ChatGettingStartedContribution extends Disposable implements IWorkbenchContribution { static readonly ID = 'workbench.contrib.chatGettingStarted'; @@ -33,6 +33,7 @@ export class ChatGettingStartedContribution extends Disposable implements IWorkb @IViewDescriptorService private readonly viewDescriptorService: IViewDescriptorService, @IWorkbenchLayoutService private readonly layoutService: IWorkbenchLayoutService, @IConfigurationService private readonly configurationService: IConfigurationService, + @IStatusbarService private readonly statusbarService: IStatusbarService, ) { super(); @@ -71,9 +72,6 @@ export class ChatGettingStartedContribution extends Disposable implements IWorkb private async onDidInstallChat() { - // Enable chat command center if previously disabled - this.configurationService.updateValue('chat.commandCenter.enabled', true); - // Open Copilot view showCopilotView(this.viewsService, this.layoutService); ensureSideBarChatViewSize(this.viewDescriptorService, this.layoutService, this.viewsService); @@ -81,5 +79,9 @@ export class ChatGettingStartedContribution extends Disposable implements IWorkb // Only do this once this.storageService.store(ChatGettingStartedContribution.hideWelcomeView, true, StorageScope.APPLICATION, StorageTarget.MACHINE); this.recentlyInstalled = false; + + // Enable Copilot related UI if previously disabled + this.statusbarService.updateEntryVisibility('chat.statusBarEntry', true); + this.configurationService.updateValue('chat.commandCenter.enabled', true); } } diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatMoveActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatMoveActions.ts index e732c01efbbb..5f89fe7ab7a8 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatMoveActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatMoveActions.ts @@ -18,7 +18,7 @@ import { IEditorGroupsService } from '../../../../services/editor/common/editorG import { ACTIVE_GROUP, AUX_WINDOW_GROUP, IEditorService } from '../../../../services/editor/common/editorService.js'; import { IViewsService } from '../../../../services/views/common/viewsService.js'; import { isChatViewTitleActionContext } from '../../common/chatActions.js'; -import { ChatAgentLocation } from '../../common/chatAgents.js'; +import { ChatAgentLocation } from '../../common/constants.js'; enum MoveToNewLocation { Editor = 'Editor', @@ -100,17 +100,12 @@ async function executeMoveToAction(accessor: ServicesAccessor, moveTo: MoveToNew const widget = (_sessionId ? widgetService.getWidgetBySessionId(_sessionId) : undefined) ?? widgetService.lastFocusedWidget; - if (!widget || widget.location !== ChatAgentLocation.Panel) { + if (!widget || !widget.viewModel || widget.location !== ChatAgentLocation.Panel) { await editorService.openEditor({ resource: ChatEditorInput.getNewEditorUri(), options: { pinned: true } }, moveTo === MoveToNewLocation.Window ? AUX_WINDOW_GROUP : ACTIVE_GROUP); return; } - const viewModel = widget.viewModel; - if (!viewModel) { - return; - } - - const sessionId = viewModel.sessionId; + const sessionId = widget.viewModel.sessionId; const viewState = widget.getViewState(); widget.clear(); diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatQuickInputActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatQuickInputActions.ts index 46c4ab4ed314..f5f9343bed89 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatQuickInputActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatQuickInputActions.ts @@ -80,8 +80,8 @@ class QuickChatGlobalAction extends Action2 { }, menu: { id: MenuId.ChatTitleBarMenu, - group: 'e_quickChat', - order: 5 + group: 'a_open', + order: 4 }, metadata: { description: localize('toggle.desc', 'Toggle the quick chat'), @@ -134,6 +134,7 @@ class AskQuickChatAction extends Action2 { id: `workbench.action.openQuickChat`, category: CHAT_CATEGORY, title: localize2('interactiveSession.open', "Open Quick Chat"), + precondition: ChatContextKeys.enabled, f1: true }); } diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatTitleActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatTitleActions.ts index 53beeabd7990..af49c6b07364 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatTitleActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatTitleActions.ts @@ -29,12 +29,13 @@ import { MENU_INLINE_CHAT_WIDGET_SECONDARY } from '../../../inlineChat/common/in import { INotebookEditor } from '../../../notebook/browser/notebookBrowser.js'; import { CellEditType, CellKind, NOTEBOOK_EDITOR_ID } from '../../../notebook/common/notebookCommon.js'; import { NOTEBOOK_IS_ACTIVE_EDITOR } from '../../../notebook/common/notebookContextKeys.js'; -import { ChatAgentLocation, IChatAgentService } from '../../common/chatAgents.js'; -import { ChatContextKeys } from '../../common/chatContextKeys.js'; +import { IChatAgentService } from '../../common/chatAgents.js'; +import { ChatContextKeyExprs, ChatContextKeys } from '../../common/chatContextKeys.js'; import { applyingChatEditsFailedContextKey, ChatEditingSessionState, IChatEditingService, isChatEditingActionContext } from '../../common/chatEditingService.js'; import { IChatRequestModel } from '../../common/chatModel.js'; import { ChatAgentVoteDirection, ChatAgentVoteDownReason, IChatService } from '../../common/chatService.js'; import { isRequestVM, isResponseVM } from '../../common/chatViewModel.js'; +import { ChatAgentLocation, ChatMode } from '../../common/constants.js'; import { ChatTreeItem, EditsViewId, IChatWidgetService } from '../chat.js'; import { ChatViewPane } from '../chatViewPane.js'; import { CHAT_CATEGORY } from './chatActions.js'; @@ -229,7 +230,9 @@ export function registerChatTitleActions() { return; } const itemIndex = chatRequests?.findIndex(request => request.id === item.requestId); - if (chatModel?.initialLocation === ChatAgentLocation.EditingSession) { + const widget = chatWidgetService.getWidgetBySessionId(item.sessionId); + const mode = widget?.input.currentMode; + if (chatModel?.initialLocation === ChatAgentLocation.EditingSession || chatModel && (mode === ChatMode.Edit || mode === ChatMode.Agent)) { const configurationService = accessor.get(IConfigurationService); const dialogService = accessor.get(IDialogService); const chatEditingService = accessor.get(IChatEditingService); @@ -269,7 +272,7 @@ export function registerChatTitleActions() { } const request = chatModel?.getRequests().find(candidate => candidate.id === item.requestId); const languageModelId = chatWidgetService.getWidgetBySessionId(item.sessionId)?.input.currentLanguageModel; - chatService.resendRequest(request!, { userSelectedModelId: languageModelId }); + chatService.resendRequest(request!, { userSelectedModelId: languageModelId, attempt: (request?.attempt ?? -1) + 1 }); } }); @@ -354,20 +357,20 @@ export function registerChatTitleActions() { f1: false, category: CHAT_CATEGORY, icon: Codicon.x, - precondition: ChatContextKeys.location.notEqualsTo(ChatAgentLocation.EditingSession), + precondition: ChatContextKeys.chatMode.isEqualTo(ChatMode.Chat), keybinding: { primary: KeyCode.Delete, mac: { primary: KeyMod.CtrlCmd | KeyCode.Backspace, }, - when: ContextKeyExpr.and(ChatContextKeys.location.notEqualsTo(ChatAgentLocation.EditingSession), ChatContextKeys.inChatSession, ChatContextKeys.inChatInput.negate()), + when: ContextKeyExpr.and(ChatContextKeys.chatMode.isEqualTo(ChatMode.Chat), ChatContextKeys.inChatSession, ChatContextKeys.inChatInput.negate()), weight: KeybindingWeight.WorkbenchContrib, }, menu: { id: MenuId.ChatMessageTitle, group: 'navigation', order: 2, - when: ContextKeyExpr.and(ChatContextKeys.location.notEqualsTo(ChatAgentLocation.EditingSession), ChatContextKeys.isRequest) + when: ContextKeyExpr.and(ChatContextKeys.chatMode.isEqualTo(ChatMode.Chat), ChatContextKeys.isRequest) } }); } @@ -408,12 +411,17 @@ export function registerChatTitleActions() { f1: false, category: CHAT_CATEGORY, icon: Codicon.goToEditingSession, - precondition: ContextKeyExpr.and(ChatContextKeys.editingParticipantRegistered, ChatContextKeys.requestInProgress.toNegated(), ChatContextKeys.location.isEqualTo(ChatAgentLocation.Panel)), + precondition: ContextKeyExpr.and( + ChatContextKeys.editingParticipantRegistered, + ChatContextKeys.requestInProgress.toNegated(), + ChatContextKeys.location.isEqualTo(ChatAgentLocation.Panel), + ChatContextKeyExprs.unifiedChatEnabled.negate() + ), menu: { id: MenuId.ChatMessageFooter, group: 'navigation', order: 4, - when: ContextKeyExpr.and(ChatContextKeys.enabled, ChatContextKeys.isResponse, ChatContextKeys.editingParticipantRegistered, ChatContextKeys.location.isEqualTo(ChatAgentLocation.Panel)) + when: ContextKeyExpr.and(ChatContextKeys.enabled, ChatContextKeys.isResponse, ChatContextKeys.editingParticipantRegistered, ChatContextKeys.location.isEqualTo(ChatAgentLocation.Panel), ChatContextKeyExprs.unifiedChatEnabled.negate()) } }); } @@ -487,12 +495,12 @@ export function registerChatTitleActions() { await chatService.adoptRequest(editingSession.chatSessionId, request); this._collectWorkingSetAdditions(request, workingSetAdditions); } - workingSetAdditions.forEach(uri => editingSession.addFileToWorkingSet(uri)); + workingSetAdditions.forEach(uri => editsView.widget.attachmentModel.addFile(uri)); // make request await chatService.sendRequest(editingSession.chatSessionId, '', { agentId: editAgent.id, - acceptedConfirmationData: [{ _type: 'toEditTransfer', transferedTurnResults: sourceRequests.map(v => v.response?.result) }], // TODO@jrieken HACKY + acceptedConfirmationData: [{ _type: 'toEditTransfer', transferredTurnResults: sourceRequests.map(v => v.response?.result) }], // TODO@jrieken HACKY confirmation: typeof this.desc.title === 'string' ? this.desc.title : this.desc.title.value }); } diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatToolActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatToolActions.ts new file mode 100644 index 000000000000..5d71ac109d1e --- /dev/null +++ b/src/vs/workbench/contrib/chat/browser/actions/chatToolActions.ts @@ -0,0 +1,287 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Codicon } from '../../../../../base/common/codicons.js'; +import { diffSets } from '../../../../../base/common/collections.js'; +import { Event } from '../../../../../base/common/event.js'; +import { KeyCode, KeyMod } from '../../../../../base/common/keyCodes.js'; +import { DisposableStore } from '../../../../../base/common/lifecycle.js'; +import { ServicesAccessor } from '../../../../../editor/browser/editorExtensions.js'; +import { localize, localize2 } from '../../../../../nls.js'; +import { Action2, MenuId, registerAction2 } from '../../../../../platform/actions/common/actions.js'; +import { ContextKeyExpr } from '../../../../../platform/contextkey/common/contextkey.js'; +import { ExtensionIdentifier } from '../../../../../platform/extensions/common/extensions.js'; +import { KeybindingWeight } from '../../../../../platform/keybinding/common/keybindingsRegistry.js'; +import { IQuickInputService, IQuickPickItem, IQuickPickSeparator } from '../../../../../platform/quickinput/common/quickInput.js'; +import { IExtensionService } from '../../../../services/extensions/common/extensions.js'; +import { IMcpService, IMcpServer, McpConnectionState } from '../../../mcp/common/mcpTypes.js'; +import { ChatContextKeys } from '../../common/chatContextKeys.js'; +import { IChatToolInvocation } from '../../common/chatService.js'; +import { isResponseVM } from '../../common/chatViewModel.js'; +import { ChatMode } from '../../common/constants.js'; +import { ILanguageModelToolsService, IToolData } from '../../common/languageModelToolsService.js'; +import { IChatWidget, IChatWidgetService } from '../chat.js'; +import { CHAT_CATEGORY } from './chatActions.js'; + +export const AcceptToolConfirmationActionId = 'workbench.action.chat.acceptTool'; + +class AcceptToolConfirmation extends Action2 { + constructor() { + super({ + id: AcceptToolConfirmationActionId, + title: localize2('chat.accept', "Accept"), + f1: false, + category: CHAT_CATEGORY, + keybinding: { + when: ContextKeyExpr.and(ChatContextKeys.inChatSession, ChatContextKeys.Editing.hasToolConfirmation), + primary: KeyMod.CtrlCmd | KeyCode.Enter, + // Override chatEditor.action.accept + weight: KeybindingWeight.WorkbenchContrib + 1, + }, + }); + } + + run(accessor: ServicesAccessor, ...args: any[]) { + const chatWidgetService = accessor.get(IChatWidgetService); + const widget = chatWidgetService.lastFocusedWidget; + const lastItem = widget?.viewModel?.getItems().at(-1); + if (!isResponseVM(lastItem)) { + return; + } + + const unconfirmedToolInvocation = lastItem.model.response.value.find((item): item is IChatToolInvocation => item.kind === 'toolInvocation' && !item.isConfirmed); + if (unconfirmedToolInvocation) { + unconfirmedToolInvocation.confirmed.complete(true); + } + + // Return focus to the chat input, in case it was in the tool confirmation editor + widget?.focusInput(); + } +} + +export class AttachToolsAction extends Action2 { + + static readonly id = 'workbench.action.chat.attachTools'; + + constructor() { + super({ + id: AttachToolsAction.id, + title: localize('label', "Select Tools..."), + icon: Codicon.tools, + f1: false, + category: CHAT_CATEGORY, + precondition: ContextKeyExpr.and( + ContextKeyExpr.or(ChatContextKeys.Tools.toolsCount.greater(0)), + ChatContextKeys.chatMode.isEqualTo(ChatMode.Agent) + ), + menu: { + when: ContextKeyExpr.and( + ContextKeyExpr.or(ChatContextKeys.Tools.toolsCount.greater(0)), + ChatContextKeys.chatMode.isEqualTo(ChatMode.Agent) + ), + id: MenuId.ChatInputAttachmentToolbar, + group: 'navigation', + order: 1 + }, + keybinding: { + when: ContextKeyExpr.and(ChatContextKeys.inChatInput, ChatContextKeys.chatMode.isEqualTo(ChatMode.Agent)), + primary: KeyMod.CtrlCmd | KeyMod.Shift | KeyCode.Slash, + weight: KeybindingWeight.EditorContrib + } + }); + } + + override async run(accessor: ServicesAccessor, ...args: any[]): Promise { + + const quickPickService = accessor.get(IQuickInputService); + const mcpService = accessor.get(IMcpService); + const toolsService = accessor.get(ILanguageModelToolsService); + const extensionService = accessor.get(IExtensionService); + const chatWidgetService = accessor.get(IChatWidgetService); + + let widget = chatWidgetService.lastFocusedWidget; + if (!widget) { + type ChatActionContext = { widget: IChatWidget }; + function isChatActionContext(obj: any): obj is ChatActionContext { + return obj && typeof obj === 'object' && (obj as ChatActionContext).widget; + } + const context = args[0]; + if (isChatActionContext(context)) { + widget = context.widget; + } + } + + if (!widget) { + return; + } + + const mcpServerByTool = new Map(); + for (const server of mcpService.servers.get()) { + for (const tool of server.tools.get()) { + mcpServerByTool.set(tool.id, server); + } + } + + const enum BucketOrdinal { Extension, Mcp, Other } + type BucketPick = IQuickPickItem & { picked: boolean; ordinal: BucketOrdinal; status?: string; children: ToolPick[] }; + type ToolPick = IQuickPickItem & { picked: boolean; tool: IToolData; parent: BucketPick }; + type MyPick = ToolPick | BucketPick; + + const defaultBucket: BucketPick = { + type: 'item', + children: [], + label: localize('defaultBucketLabel', "Other Tools"), + ordinal: BucketOrdinal.Other, + picked: true, + }; + + const nowSelectedTools = new Set(widget.input.selectedToolsModel.tools.get()); + const toolBuckets = new Map(); + + for (const tool of toolsService.getTools()) { + + if (!tool.canBeReferencedInPrompt) { + continue; + } + + let bucket: BucketPick; + + const mcpServer = mcpServerByTool.get(tool.id); + const ext = extensionService.extensions.find(value => ExtensionIdentifier.equals(value.identifier, tool.extensionId)); + if (mcpServer) { + bucket = toolBuckets.get(mcpServer.definition.id) ?? { + type: 'item', + label: mcpServer.definition.label, + // description: mcpServer.definition., + status: localize('desc', "MCP - {0} ({1})", mcpServer.collection.label, McpConnectionState.toString(mcpServer.connectionState.get())), + ordinal: BucketOrdinal.Mcp, + picked: false, + children: [] + }; + toolBuckets.set(mcpServer.definition.id, bucket); + } else if (ext) { + bucket = toolBuckets.get(ExtensionIdentifier.toKey(ext.identifier)) ?? { + type: 'item', + label: ext.displayName ?? ext.name, + ordinal: BucketOrdinal.Extension, + picked: false, + children: [] + }; + toolBuckets.set(ExtensionIdentifier.toKey(ext.identifier), bucket); + } else { + bucket = defaultBucket; + } + + const picked = nowSelectedTools.has(tool); + + bucket.children.push({ + tool, + parent: bucket, + type: 'item', + label: `$(tools) ${tool.displayName}`, + description: tool.userDescription, + picked, + iconClasses: ['tool-pick'] + }); + + if (picked) { + bucket.picked = true; + } + } + + function isBucketPick(obj: any): obj is BucketPick { + return Boolean((obj as BucketPick).children); + } + function isToolPick(obj: any): obj is ToolPick { + return Boolean((obj as ToolPick).tool); + } + + const store = new DisposableStore(); + const picker = store.add(quickPickService.createQuickPick({ useSeparators: true })); + + const picks: (MyPick | IQuickPickSeparator)[] = []; + + for (const bucket of Array.from(toolBuckets.values()).sort((a, b) => a.ordinal - b.ordinal)) { + picks.push({ + type: 'separator', + label: bucket.status + }); + + picks.push(bucket); + picks.push(...bucket.children); + } + + + picker.placeholder = localize('placeholder', "Select tools that are available to chat"); + picker.canSelectMany = true; + + let lastSelectedItems = new Set(); + let ignoreEvent = false; + + const _update = () => { + ignoreEvent = true; + try { + const items = picks.filter((p): p is MyPick => p.type === 'item' && Boolean(p.picked)); + lastSelectedItems = new Set(items); + picker.items = picks; + picker.selectedItems = items; + + widget.input.selectedToolsModel.update(items.filter(isToolPick).map(tool => tool.tool)); + + } finally { + ignoreEvent = false; + } + }; + + _update(); + picker.show(); + + store.add(picker.onDidChangeSelection(selectedPicks => { + if (ignoreEvent) { + return; + } + + const { added, removed } = diffSets(lastSelectedItems, new Set(selectedPicks)); + + for (const item of added) { + item.picked = true; + + if (isBucketPick(item)) { + // add server -> add back tools + for (const toolPick of item.children) { + toolPick.picked = true; + } + } else if (isToolPick(item)) { + // add server when tool is picked + item.parent.picked = true; + } + } + + for (const item of removed) { + item.picked = false; + + if (isBucketPick(item)) { + // removed server -> remove tools + for (const toolPick of item.children) { + toolPick.picked = false; + } + } else if (isToolPick(item) && item.parent.children.every(child => !child.picked)) { + // remove LAST tool -> remove server + item.parent.picked = false; + } + } + + _update(); + })); + + await Promise.race([Event.toPromise(Event.any(picker.onDidAccept, picker.onDidHide))]); + store.dispose(); + } +} + +export function registerChatToolActions() { + registerAction2(AcceptToolConfirmation); + registerAction2(AttachToolsAction); +} diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatTransfer.ts b/src/vs/workbench/contrib/chat/browser/actions/chatTransfer.ts new file mode 100644 index 000000000000..52bc36801707 --- /dev/null +++ b/src/vs/workbench/contrib/chat/browser/actions/chatTransfer.ts @@ -0,0 +1,19 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IWorkbenchContribution } from '../../../../common/contributions.js'; +import { Disposable } from '../../../../../base/common/lifecycle.js'; +import { IChatTransferService } from '../../common/chatTransferService.js'; + +export class ChatTransferContribution extends Disposable implements IWorkbenchContribution { + static readonly ID = 'workbench.contrib.chatTransfer'; + + constructor( + @IChatTransferService chatTransferService: IChatTransferService, + ) { + super(); + chatTransferService.checkAndSetWorkspaceTrust(); + } +} diff --git a/src/vs/workbench/contrib/chat/browser/attachments/instructionsAttachment/instructionsAttachment.ts b/src/vs/workbench/contrib/chat/browser/attachments/promptAttachments/promptAttachmentWidget.ts similarity index 91% rename from src/vs/workbench/contrib/chat/browser/attachments/instructionsAttachment/instructionsAttachment.ts rename to src/vs/workbench/contrib/chat/browser/attachments/promptAttachments/promptAttachmentWidget.ts index dc2742e19345..ac4fcefcd206 100644 --- a/src/vs/workbench/contrib/chat/browser/attachments/instructionsAttachment/instructionsAttachment.ts +++ b/src/vs/workbench/contrib/chat/browser/attachments/promptAttachments/promptAttachmentWidget.ts @@ -23,15 +23,15 @@ import { FileKind, IFileService } from '../../../../../../platform/files/common/ import { IMenuService, MenuId } from '../../../../../../platform/actions/common/actions.js'; import { getCleanPromptName } from '../../../../../../platform/prompts/common/constants.js'; import { IContextKeyService } from '../../../../../../platform/contextkey/common/contextkey.js'; +import { ChatPromptAttachmentModel } from '../../chatAttachmentModel/chatPromptAttachmentModel.js'; import { IContextMenuService } from '../../../../../../platform/contextview/browser/contextView.js'; -import { ChatInstructionsAttachmentModel } from '../../chatAttachmentModel/chatInstructionsAttachment.js'; import { getDefaultHoverDelegate } from '../../../../../../base/browser/ui/hover/hoverDelegateFactory.js'; import { getFlatContextMenuActions } from '../../../../../../platform/actions/browser/menuEntryActionViewItem.js'; /** * Widget for a single prompt instructions attachment. */ -export class InstructionsAttachmentWidget extends Disposable { +export class PromptAttachmentWidget extends Disposable { /** * The root DOM node of the widget. */ @@ -66,7 +66,7 @@ export class InstructionsAttachmentWidget extends Disposable { private readonly renderDisposables = this._register(new DisposableStore()); constructor( - private readonly model: ChatInstructionsAttachmentModel, + private readonly model: ChatPromptAttachmentModel, private readonly resourceLabels: ResourceLabels, @IContextKeyService private readonly contextKeyService: IContextKeyService, @IContextMenuService private readonly contextMenuService: IContextMenuService, @@ -79,7 +79,7 @@ export class InstructionsAttachmentWidget extends Disposable { ) { super(); - this.domNode = dom.$('.chat-prompt-instructions-attachment.chat-attached-context-attachment.show-file-icons.implicit'); + this.domNode = dom.$('.chat-prompt-attachment.chat-attached-context-attachment.show-file-icons.implicit'); this.render = this.render.bind(this); this.dispose = this.dispose.bind(this); @@ -118,18 +118,18 @@ export class InstructionsAttachmentWidget extends Disposable { // add the issue details in the hover title for the attachment, one // error/warning at a time because there is a limited space available if (topError) { - const { isRootError, message: details } = topError; - const isWarning = !isRootError; + const { errorSubject: subject } = topError; + const isError = (subject === 'root'); this.domNode.classList.add( - (isWarning) ? 'warning' : 'error', + (isError) ? 'error' : 'warning', ); - const errorCaption = (isWarning) - ? localize('warning', "Warning") - : localize('error', "Error"); + const severity = (isError) + ? localize('error', "Error") + : localize('warning', "Warning"); - title += `\n-\n[${errorCaption}]: ${details}`; + title += `\n[${severity}]: ${topError.localizedMessage}`; } const fileWithoutExtension = getCleanPromptName(file); @@ -157,7 +157,8 @@ export class InstructionsAttachmentWidget extends Disposable { }, ), ); - removeButton.icon = Codicon.x; + + removeButton.icon = Codicon.close; this.renderDisposables.add(removeButton.onDidClick((e) => { e.stopPropagation(); this.model.dispose(); diff --git a/src/vs/workbench/contrib/chat/browser/attachments/instructionsAttachment/instructionAttachments.ts b/src/vs/workbench/contrib/chat/browser/attachments/promptAttachments/promptAttachmentsCollectionWidget.ts similarity index 76% rename from src/vs/workbench/contrib/chat/browser/attachments/instructionsAttachment/instructionAttachments.ts rename to src/vs/workbench/contrib/chat/browser/attachments/promptAttachments/promptAttachmentsCollectionWidget.ts index 58e60e5fbcd0..d05558a0489b 100644 --- a/src/vs/workbench/contrib/chat/browser/attachments/instructionsAttachment/instructionAttachments.ts +++ b/src/vs/workbench/contrib/chat/browser/attachments/promptAttachments/promptAttachmentsCollectionWidget.ts @@ -4,24 +4,23 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from '../../../../../../base/common/uri.js'; -import * as dom from '../../../../../../base/browser/dom.js'; import { Emitter } from '../../../../../../base/common/event.js'; import { ResourceLabels } from '../../../../../browser/labels.js'; +import { PromptAttachmentWidget } from './promptAttachmentWidget.js'; import { Disposable } from '../../../../../../base/common/lifecycle.js'; -import { InstructionsAttachmentWidget } from './instructionsAttachment.js'; import { ILogService } from '../../../../../../platform/log/common/log.js'; import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; -import { ChatInstructionAttachmentsModel } from '../../chatAttachmentModel/chatInstructionAttachmentsModel.js'; +import { ChatPromptAttachmentsCollection } from '../../chatAttachmentModel/chatPromptAttachmentsCollection.js'; /** - * Widget fot a collection of prompt instructions attachments. - * See {@linkcode InstructionsAttachmentWidget}. + * Widget for a collection of prompt instructions attachments. + * See {@linkcode PromptAttachmentWidget}. */ -export class InstructionAttachmentsWidget extends Disposable { +export class PromptAttachmentsCollectionWidget extends Disposable { /** * List of child instruction attachment widgets. */ - private children: InstructionsAttachmentWidget[] = []; + private children: PromptAttachmentWidget[] = []; /** * Event that fires when number of attachments change @@ -40,9 +39,9 @@ export class InstructionAttachmentsWidget extends Disposable { } /** - * The root DOM node of the widget. + * The parent DOM node this widget was rendered into. */ - public readonly domNode: HTMLElement; + private parentNode: HTMLElement | undefined; /** * Get all `URI`s of all valid references, including all @@ -68,7 +67,7 @@ export class InstructionAttachmentsWidget extends Disposable { } constructor( - private readonly model: ChatInstructionAttachmentsModel, + private readonly model: ChatPromptAttachmentsCollection, private readonly resourceLabels: ResourceLabels, @IInstantiationService private readonly initService: IInstantiationService, @ILogService private readonly logService: ILogService, @@ -76,14 +75,11 @@ export class InstructionAttachmentsWidget extends Disposable { super(); this.render = this.render.bind(this); - this.domNode = dom.$('.chat-prompt-instructions-attachments'); - - this._register(this.model.onUpdate(this.render)); // when a new attachment model is added, create a new child widget for it this.model.onAdd((attachment) => { const widget = this.initService.createInstance( - InstructionsAttachmentWidget, + PromptAttachmentWidget, attachment, this.resourceLabels, ); @@ -93,8 +89,12 @@ export class InstructionAttachmentsWidget extends Disposable { // register the new child widget this.children.push(widget); - this.domNode.appendChild(widget.domNode); - this.render(); + + // if parent node is present - append the wiget to it, otherwise wait + // until the `render` method will be called + if (this.parentNode) { + this.parentNode.appendChild(widget.domNode); + } // fire the event to notify about the change in the number of attachments this._onAttachmentsCountChange.fire(); @@ -105,7 +105,7 @@ export class InstructionAttachmentsWidget extends Disposable { * Handle child widget disposal. * @param widget The child widget that was disposed. */ - public handleAttachmentDispose(widget: InstructionsAttachmentWidget): this { + public handleAttachmentDispose(widget: PromptAttachmentWidget): this { // common prefix for all log messages const logPrefix = `[onChildDispose] Widget for instructions attachment '${widget.uri.path}'`; @@ -138,11 +138,14 @@ export class InstructionAttachmentsWidget extends Disposable { ); } - // remove the child widget root node from the DOM - this.domNode.removeChild(widget.domNode); + if (!this.parentNode) { + this.logService.warn( + `${logPrefix} no parent node reference found.`, + ); + } - // re-render the whole widget - this.render(); + // remove the child widget root node from the DOM + this.parentNode?.removeChild(widget.domNode); // fire the event to notify about the change in the number of attachments this._onAttachmentsCountChange.fire(); @@ -151,11 +154,18 @@ export class InstructionAttachmentsWidget extends Disposable { } /** - * Render this widget. + * Render attachments into the provided `parentNode`. + * + * Note! this method assumes that the provided `parentNode` is cleared by the caller. */ - private render(): this { - // set visibility of the root node based on the presence of attachments - dom.setVisibility(!this.empty, this.domNode); + public render( + parentNode: HTMLElement, + ): this { + this.parentNode = parentNode; + + for (const widget of this.children) { + this.parentNode.appendChild(widget.domNode); + } return this; } diff --git a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts index e1110b3e84f6..a091ff5f4691 100644 --- a/src/vs/workbench/contrib/chat/browser/chat.contribution.ts +++ b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { timeout } from '../../../../base/common/async.js'; +import { Event } from '../../../../base/common/event.js'; import { MarkdownString, isMarkdownString } from '../../../../base/common/htmlContent.js'; import { Disposable } from '../../../../base/common/lifecycle.js'; import { Schemas } from '../../../../base/common/network.js'; @@ -17,35 +18,43 @@ import { IContextKeyService } from '../../../../platform/contextkey/common/conte import { SyncDescriptor } from '../../../../platform/instantiation/common/descriptors.js'; import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; +import product from '../../../../platform/product/common/product.js'; import { IProductService } from '../../../../platform/product/common/productService.js'; +import { PromptsConfig } from '../../../../platform/prompts/common/config.js'; +import { PROMPT_FILE_EXTENSION } from '../../../../platform/prompts/common/constants.js'; import { Registry } from '../../../../platform/registry/common/platform.js'; import { EditorPaneDescriptor, IEditorPaneRegistry } from '../../../browser/editor.js'; import { Extensions, IConfigurationMigrationRegistry } from '../../../common/configuration.js'; import { IWorkbenchContribution, WorkbenchPhase, registerWorkbenchContribution2 } from '../../../common/contributions.js'; import { EditorExtensions, IEditorFactoryRegistry } from '../../../common/editor.js'; import { IWorkbenchAssignmentService } from '../../../services/assignment/common/assignmentService.js'; +import { mcpSchemaId } from '../../../services/configuration/common/configuration.js'; import { IEditorResolverService, RegisteredEditorPriority } from '../../../services/editor/common/editorResolverService.js'; -import { ChatAgentLocation, ChatAgentNameService, ChatAgentService, IChatAgentNameService, IChatAgentService } from '../common/chatAgents.js'; +import { mcpConfigurationSection, mcpDiscoverySection, mcpSchemaExampleServers } from '../../mcp/common/mcpConfiguration.js'; +import { ChatAgentNameService, ChatAgentService, IChatAgentNameService, IChatAgentService } from '../common/chatAgents.js'; import { CodeMapperService, ICodeMapperService } from '../common/chatCodeMapperService.js'; import '../common/chatColors.js'; import { ChatContextKeys } from '../common/chatContextKeys.js'; import { IChatEditingService } from '../common/chatEditingService.js'; +import { ChatEntitlement, ChatEntitlementService, IChatEntitlementService } from '../common/chatEntitlementService.js'; import { chatVariableLeader } from '../common/chatParserTypes.js'; import { IChatService } from '../common/chatService.js'; import { ChatService } from '../common/chatServiceImpl.js'; import { ChatSlashCommandService, IChatSlashCommandService } from '../common/chatSlashCommands.js'; +import { ChatTransferService, IChatTransferService } from '../common/chatTransferService.js'; import { IChatVariablesService } from '../common/chatVariables.js'; import { ChatWidgetHistoryService, IChatWidgetHistoryService } from '../common/chatWidgetHistoryService.js'; +import { ChatAgentLocation } from '../common/constants.js'; import { ILanguageModelIgnoredFilesService, LanguageModelIgnoredFilesService } from '../common/ignoredFiles.js'; import { ILanguageModelsService, LanguageModelsService } from '../common/languageModels.js'; import { ILanguageModelStatsService, LanguageModelStatsService } from '../common/languageModelStats.js'; import { ILanguageModelToolsService } from '../common/languageModelToolsService.js'; +import { DOCUMENTATION_URL } from '../common/promptSyntax/constants.js'; +import '../common/promptSyntax/languageFeatures/promptLinkDiagnosticsProvider.js'; import '../common/promptSyntax/languageFeatures/promptLinkProvider.js'; import '../common/promptSyntax/languageFeatures/promptPathAutocompletion.js'; import { PromptsService } from '../common/promptSyntax/service/promptsService.js'; import { IPromptsService } from '../common/promptSyntax/service/types.js'; -import './promptSyntax/contributions/usePromptCommand.js'; -import './promptSyntax/contributions/createPromptCommand/createPromptCommand.js'; import { LanguageModelToolsExtensionPointHandler } from '../common/tools/languageModelToolsContribution.js'; import { BuiltinToolsContribution } from '../common/tools/tools.js'; import { IVoiceChatService, VoiceChatService } from '../common/voiceChatService.js'; @@ -63,6 +72,8 @@ import { registerChatExportActions } from './actions/chatImportExport.js'; import { registerMoveActions } from './actions/chatMoveActions.js'; import { registerQuickChatActions } from './actions/chatQuickInputActions.js'; import { registerChatTitleActions } from './actions/chatTitleActions.js'; +import { registerChatToolActions } from './actions/chatToolActions.js'; +import { ChatTransferContribution } from './actions/chatTransfer.js'; import { IChatAccessibilityService, IChatCodeBlockContextProviderService, IChatWidgetService, IQuickChatService } from './chat.js'; import { ChatAccessibilityService } from './chatAccessibilityService.js'; import './chatAttachmentModel.js'; @@ -70,17 +81,19 @@ import { ChatMarkdownAnchorService, IChatMarkdownAnchorService } from './chatCon import { ChatInputBoxContentProvider } from './chatEdinputInputContentProvider.js'; import { ChatEditingEditorAccessibility } from './chatEditing/chatEditingEditorAccessibility.js'; import { registerChatEditorActions } from './chatEditing/chatEditingEditorActions.js'; +import { ChatEditingEditorContextKeys } from './chatEditing/chatEditingEditorContextKeys.js'; import { ChatEditingEditorOverlay } from './chatEditing/chatEditingEditorOverlay.js'; import { ChatEditingService } from './chatEditing/chatEditingServiceImpl.js'; +import { ChatEditingNotebookFileSystemProviderContrib } from './chatEditing/notebook/chatEditingNotebookFileSystemProvider.js'; import { ChatEditor, IChatEditorOptions } from './chatEditor.js'; import { ChatEditorInput, ChatEditorInputSerializer } from './chatEditorInput.js'; import { agentSlashCommandToMarkdown, agentToMarkdown } from './chatMarkdownDecorationsRenderer.js'; import { ChatCompatibilityNotifier, ChatExtensionPointHandler } from './chatParticipant.contribution.js'; import { ChatPasteProvidersFeature } from './chatPasteProviders.js'; import { QuickChatService } from './chatQuick.js'; -import { ChatQuotasService, ChatQuotasStatusBarEntry, IChatQuotasService } from './chatQuotasService.js'; import { ChatResponseAccessibleView } from './chatResponseAccessibleView.js'; import { ChatSetupContribution } from './chatSetup.js'; +import { ChatStatusBarEntry } from './chatStatus.js'; import { ChatVariablesService } from './chatVariables.js'; import { ChatWidgetService } from './chatWidget.js'; import { ChatCodeBlockContextProviderService } from './codeBlockContextProviderService.js'; @@ -90,11 +103,9 @@ import './contrib/chatInputEditorContrib.js'; import './contrib/chatInputEditorHover.js'; import { ChatRelatedFilesContribution } from './contrib/chatInputRelatedFilesContrib.js'; import { LanguageModelToolsService } from './languageModelToolsService.js'; +import './promptSyntax/contributions/createPromptCommand/createPromptCommand.js'; +import './promptSyntax/contributions/usePromptCommand.js'; import { ChatViewsWelcomeHandler } from './viewsWelcome/chatViewsWelcomeHandler.js'; -import { ChatEditingEditorContextKeys } from './chatEditing/chatEditingEditorContextKeys.js'; -import { PromptsConfig } from '../../../../platform/prompts/common/config.js'; -import { PROMPT_FILE_EXTENSION } from '../../../../platform/prompts/common/constants.js'; -import { DOCUMENTATION_URL } from '../common/promptSyntax/constants.js'; // Register configuration const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); @@ -134,6 +145,25 @@ configurationRegistry.registerConfiguration({ markdownDescription: nls.localize('chat.commandCenter.enabled', "Controls whether the command center shows a menu for actions to control Copilot (requires {0}).", '`#window.commandCenter#`'), default: true }, + 'chat.implicitContext.enabled': { + type: 'object', + tags: ['experimental'], + description: nls.localize('chat.implicitContext.enabled.1', "Enables automatically using the active editor as chat context for specified chat locations."), + additionalProperties: { + type: 'string', + enum: ['never', 'first', 'always'], + description: nls.localize('chat.implicitContext.value', "The value for the implicit context."), + enumDescriptions: [ + nls.localize('chat.implicitContext.value.never', "Implicit context is never enabled."), + nls.localize('chat.implicitContext.value.first', "Implicit context is enabled for the first interaction."), + nls.localize('chat.implicitContext.value.always', "Implicit context is always enabled.") + ] + }, + default: { + 'panel': 'always', + 'editing-session': 'first' + } + }, 'chat.editing.autoAcceptDelay': { type: 'number', markdownDescription: nls.localize('chat.editing.autoAcceptDelay', "Delay after which changes made by chat are automatically accepted. Values are in seconds, `0` means disabled and `100` seconds is the maximum."), @@ -164,27 +194,81 @@ configurationRegistry.registerConfiguration({ description: nls.localize('chat.detectParticipant.enabled', "Enables chat participant autodetection for panel chat."), default: true }, - [PromptsConfig.CONFIG_KEY]: { + 'chat.renderRelatedFiles': { + type: 'boolean', + description: nls.localize('chat.renderRelatedFiles', "Controls whether related files should be rendered in the chat input."), + default: false + }, + 'chat.experimental.statusIndicator.enabled': { // TODO@bpasero remove this eventually + type: 'boolean', + description: nls.localize('chat.statusIndicator', "Controls whether a Copilot related status indicator appears in the lower right corner."), + default: product.quality !== 'stable', + tags: ['experimental', 'onExp'] + }, + 'chat.experimental.setupFromDialog': { // TODO@bpasero remove this eventually + type: 'boolean', + description: nls.localize('chat.setupFromChat', "Controls whether Copilot setup starts from a dialog or from the welcome view."), + default: product.quality !== 'stable', + tags: ['experimental', 'onExp'] + }, + [mcpConfigurationSection]: { type: 'object', + default: { + inputs: [], + servers: mcpSchemaExampleServers, + }, + description: nls.localize('workspaceConfig.mcp.description', "Model Context Protocol server configurations"), + $ref: mcpSchemaId + }, + // [ChatConfiguration.UnifiedChatView]: { + // type: 'boolean', + // description: nls.localize('chat.experimental.unifiedChatView', "Enables the unified view with Chat, Edit, and Agent in one place."), + // default: false, + // tags: ['experimental'], + // }, + [mcpDiscoverySection]: { + type: 'boolean', + default: false, + description: nls.localize('mpc.discovery.enabled', "Enable discovery of Model Context Protocol servers on the machine."), + }, + [PromptsConfig.CONFIG_KEY]: { + type: 'boolean', title: nls.localize( - 'chat.promptFiles.config.title', + 'chat.reusablePrompts.config.enabled.title', "Prompt Files", ), markdownDescription: nls.localize( - 'chat.promptFiles.config.description', + 'chat.reusablePrompts.config.enabled.description', + "Enable reusable prompt files (`*{0}`) in Chat, Edits, and Inline Chat sessions. [Learn More]({1}).", + PROMPT_FILE_EXTENSION, + DOCUMENTATION_URL, + ), + default: true, + restricted: true, + disallowConfigurationDefault: true, + tags: ['experimental', 'prompts', 'reusable prompts', 'prompt snippets', 'instructions'], + }, + [PromptsConfig.LOCATIONS_CONFIG_KEY]: { + type: 'object', + title: nls.localize( + 'chat.reusablePrompts.config.locations.title', + "Prompt File Locations", + ), + markdownDescription: nls.localize( + 'chat.reusablePrompts.config.locations.description', "Specify location(s) of reusable prompt files (`*{0}`) that can be attached in Chat, Edits, and Inline Chat sessions. [Learn More]({1}).\n\nRelative paths are resolved from the root folder(s) of your workspace.", PROMPT_FILE_EXTENSION, DOCUMENTATION_URL, ), default: { - [PromptsConfig.DEFAULT_SOURCE_FOLDER]: false, + [PromptsConfig.DEFAULT_SOURCE_FOLDER]: true, }, required: [PromptsConfig.DEFAULT_SOURCE_FOLDER], additionalProperties: { type: 'boolean' }, unevaluatedProperties: { type: 'boolean' }, restricted: true, disallowConfigurationDefault: true, - tags: ['experimental'], + tags: ['experimental', 'prompts', 'reusable prompts', 'prompt snippets', 'instructions'], examples: [ { [PromptsConfig.DEFAULT_SOURCE_FOLDER]: true, @@ -247,34 +331,40 @@ class ChatResolverContribution extends Disposable { } } -class ChatAgentSettingContribution implements IWorkbenchContribution { +class ChatAgentSettingContribution extends Disposable implements IWorkbenchContribution { static readonly ID = 'workbench.contrib.chatAgentSetting'; private registeredNode: IConfigurationNode | undefined; constructor( - @IWorkbenchAssignmentService experimentService: IWorkbenchAssignmentService, + @IWorkbenchAssignmentService private readonly experimentService: IWorkbenchAssignmentService, @IProductService private readonly productService: IProductService, @IContextKeyService contextKeyService: IContextKeyService, + @IChatEntitlementService private readonly entitlementService: IChatEntitlementService, ) { + super(); + if (this.productService.quality !== 'stable') { - this.registerSetting(); + this.registerEnablementSetting(); } const expDisabledKey = ChatContextKeys.Editing.agentModeDisallowed.bindTo(contextKeyService); experimentService.getTreatment('chatAgentEnabled').then(enabled => { if (enabled) { - this.registerSetting(); + this.registerEnablementSetting(); expDisabledKey.set(false); - } else if (enabled === false) { + } else if (this.productService.quality === 'stable') { + // undefined treatment- on stable, fall back to disabled this.deregisterSetting(); expDisabledKey.set(true); } }); + + this.registerMaxRequestsSetting(); } - private registerSetting() { + private registerEnablementSetting() { if (this.registeredNode) { return; } @@ -301,6 +391,34 @@ class ChatAgentSettingContribution implements IWorkbenchContribution { this.registeredNode = undefined; } } + + private registerMaxRequestsSetting(): void { + let lastNode: IConfigurationNode | undefined; + const registerMaxRequestsSetting = () => { + const treatmentId = this.entitlementService.entitlement === ChatEntitlement.Limited ? + 'chatAgentMaxRequestsFree' : + 'chatAgentMaxRequestsPro'; + this.experimentService.getTreatment(treatmentId).then(value => { + const defaultValue = value ?? (this.entitlementService.entitlement === ChatEntitlement.Limited ? 5 : 15); + const node: IConfigurationNode = { + id: 'chatSidebar', + title: nls.localize('interactiveSessionConfigurationTitle', "Chat"), + type: 'object', + properties: { + 'chat.agent.maxRequests': { + type: 'number', + markdownDescription: nls.localize('chat.agent.maxRequests', "The maximum number of requests to allow Copilot Edits to use per-turn in agent mode. When the limit is reached, Copilot will ask the user to confirm that it should keep working. \n\n> **Note**: For users on the Copilot Free plan, note that each agent mode request currently uses one chat request."), + default: defaultValue, + tags: ['experimental'] + }, + } + }; + configurationRegistry.updateConfigurations({ remove: lastNode ? [lastNode] : [], add: [node] }); + lastNode = node; + }); + }; + this._register(Event.runAndSubscribe(Event.debounce(this.entitlementService.onDidChangeEntitlement, () => { }, 1000), () => registerMaxRequestsSetting())); + } } AccessibleViewRegistry.register(new ChatResponseAccessibleView()); @@ -417,12 +535,13 @@ registerWorkbenchContribution2(ChatRelatedFilesContribution.ID, ChatRelatedFiles registerWorkbenchContribution2(ChatViewsWelcomeHandler.ID, ChatViewsWelcomeHandler, WorkbenchPhase.BlockStartup); registerWorkbenchContribution2(ChatGettingStartedContribution.ID, ChatGettingStartedContribution, WorkbenchPhase.Eventually); registerWorkbenchContribution2(ChatSetupContribution.ID, ChatSetupContribution, WorkbenchPhase.BlockRestore); -registerWorkbenchContribution2(ChatQuotasStatusBarEntry.ID, ChatQuotasStatusBarEntry, WorkbenchPhase.Eventually); +registerWorkbenchContribution2(ChatStatusBarEntry.ID, ChatStatusBarEntry, WorkbenchPhase.BlockRestore); registerWorkbenchContribution2(BuiltinToolsContribution.ID, BuiltinToolsContribution, WorkbenchPhase.Eventually); registerWorkbenchContribution2(ChatAgentSettingContribution.ID, ChatAgentSettingContribution, WorkbenchPhase.BlockRestore); registerWorkbenchContribution2(ChatEditingEditorAccessibility.ID, ChatEditingEditorAccessibility, WorkbenchPhase.AfterRestored); registerWorkbenchContribution2(ChatEditingEditorOverlay.ID, ChatEditingEditorOverlay, WorkbenchPhase.AfterRestored); registerWorkbenchContribution2(ChatEditingEditorContextKeys.ID, ChatEditingEditorContextKeys, WorkbenchPhase.AfterRestored); +registerWorkbenchContribution2(ChatTransferContribution.ID, ChatTransferContribution, WorkbenchPhase.BlockRestore); registerChatActions(); registerChatCopyActions(); @@ -438,6 +557,7 @@ registerNewChatActions(); registerChatContextActions(); registerChatDeveloperActions(); registerChatEditorActions(); +registerChatToolActions(); registerEditorFeature(ChatPasteProvidersFeature); @@ -460,6 +580,8 @@ registerSingleton(ICodeMapperService, CodeMapperService, InstantiationType.Delay registerSingleton(IChatEditingService, ChatEditingService, InstantiationType.Delayed); registerSingleton(IChatMarkdownAnchorService, ChatMarkdownAnchorService, InstantiationType.Delayed); registerSingleton(ILanguageModelIgnoredFilesService, LanguageModelIgnoredFilesService, InstantiationType.Delayed); -registerSingleton(IChatQuotasService, ChatQuotasService, InstantiationType.Delayed); - +registerSingleton(IChatEntitlementService, ChatEntitlementService, InstantiationType.Delayed); registerSingleton(IPromptsService, PromptsService, InstantiationType.Delayed); +registerSingleton(IChatTransferService, ChatTransferService, InstantiationType.Delayed); + +registerWorkbenchContribution2(ChatEditingNotebookFileSystemProviderContrib.ID, ChatEditingNotebookFileSystemProviderContrib, WorkbenchPhase.BlockStartup); diff --git a/src/vs/workbench/contrib/chat/browser/chat.ts b/src/vs/workbench/contrib/chat/browser/chat.ts index 982143743ce4..03216596a8a9 100644 --- a/src/vs/workbench/contrib/chat/browser/chat.ts +++ b/src/vs/workbench/contrib/chat/browser/chat.ts @@ -14,11 +14,12 @@ import { createDecorator } from '../../../../platform/instantiation/common/insta import { IViewDescriptorService, ViewContainerLocation } from '../../../common/views.js'; import { IWorkbenchLayoutService, Parts } from '../../../services/layout/browser/layoutService.js'; import { IViewsService } from '../../../services/views/common/viewsService.js'; -import { ChatAgentLocation, IChatAgentCommand, IChatAgentData } from '../common/chatAgents.js'; +import { IChatAgentCommand, IChatAgentData } from '../common/chatAgents.js'; import { IChatResponseModel } from '../common/chatModel.js'; import { IParsedChatRequest } from '../common/chatParserTypes.js'; import { CHAT_PROVIDER_ID } from '../common/chatParticipantContribTypes.js'; import { IChatRequestViewModel, IChatResponseViewModel, IChatViewModel } from '../common/chatViewModel.js'; +import { ChatAgentLocation, ChatMode } from '../common/constants.js'; import { ChatAttachmentModel } from './chatAttachmentModel.js'; import { ChatInputPart } from './chatInputPart.js'; import { ChatViewPane } from './chatViewPane.js'; @@ -136,13 +137,12 @@ export interface IChatAccessibilityService { export interface IChatCodeBlockInfo { readonly ownerMarkdownPartId: string; readonly codeBlockIndex: number; - readonly element: ChatTreeItem; + readonly elementId: string; readonly uri: URI | undefined; readonly uriPromise: Promise; codemapperUri: URI | undefined; readonly isStreaming: boolean; focus(): void; - getContent(): string; } export interface IChatFileTreeInfo { @@ -156,17 +156,16 @@ export type ChatTreeItem = IChatRequestViewModel | IChatResponseViewModel; export interface IChatListItemRendererOptions { readonly renderStyle?: 'compact' | 'minimal'; readonly noHeader?: boolean; - readonly noPadding?: boolean; readonly editableCodeBlock?: boolean; - readonly renderCodeBlockPills?: boolean; + readonly renderCodeBlockPills?: boolean | ((mode: ChatMode) => boolean); readonly renderDetectedCommandsWithRequest?: boolean; readonly renderTextEditsAsSummary?: (uri: URI) => boolean; - readonly referencesExpandedWhenEmptyResponse?: boolean; - readonly progressMessageAtBottomOfResponse?: boolean; + readonly referencesExpandedWhenEmptyResponse?: boolean | ((mode: ChatMode) => boolean); + readonly progressMessageAtBottomOfResponse?: boolean | ((mode: ChatMode) => boolean); } export interface IChatWidgetViewOptions { - autoScroll?: boolean; + autoScroll?: boolean | ((mode: ChatMode) => boolean); renderInputOnTop?: boolean; renderFollowups?: boolean; renderStyle?: 'compact' | 'minimal'; @@ -190,7 +189,9 @@ export interface IChatWidgetViewOptions { }; defaultElementHeight?: number; editorOverflowWidgetsDomNode?: HTMLElement; + enableImplicitContext?: boolean; enableWorkingSet?: 'explicit' | 'implicit'; + supportsChangingModes?: boolean; } export interface IChatViewViewContext { @@ -226,6 +227,9 @@ export interface IChatWidget { readonly input: ChatInputPart; readonly attachmentModel: ChatAttachmentModel; + // TODO I don't like this + readonly isUnifiedPanelWidget: boolean; + getContrib(id: string): T | undefined; reveal(item: ChatTreeItem): void; focus(item: ChatTreeItem): void; diff --git a/src/vs/workbench/contrib/chat/browser/chatAttachmentModel.ts b/src/vs/workbench/contrib/chat/browser/chatAttachmentModel.ts index 9278fe4aed0a..24317550c93a 100644 --- a/src/vs/workbench/contrib/chat/browser/chatAttachmentModel.ts +++ b/src/vs/workbench/contrib/chat/browser/chatAttachmentModel.ts @@ -3,20 +3,20 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { URI } from '../../../../base/common/uri.js'; import { Emitter } from '../../../../base/common/event.js'; -import { Disposable } from '../../../../base/common/lifecycle.js'; import { basename } from '../../../../base/common/resources.js'; -import { URI } from '../../../../base/common/uri.js'; import { IRange } from '../../../../editor/common/core/range.js'; +import { Disposable } from '../../../../base/common/lifecycle.js'; import { IChatRequestVariableEntry } from '../common/chatModel.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; -import { ChatInstructionAttachmentsModel } from './chatAttachmentModel/chatInstructionAttachmentsModel.js'; +import { ChatPromptAttachmentsCollection } from './chatAttachmentModel/chatPromptAttachmentsCollection.js'; export class ChatAttachmentModel extends Disposable { /** * Collection on prompt instruction attachments. */ - public readonly promptInstructions: ChatInstructionAttachmentsModel; + public readonly promptInstructions: ChatPromptAttachmentsCollection; constructor( @IInstantiationService private readonly initService: IInstantiationService, @@ -24,7 +24,7 @@ export class ChatAttachmentModel extends Disposable { super(); this.promptInstructions = this._register( - this.initService.createInstance(ChatInstructionAttachmentsModel), + this.initService.createInstance(ChatPromptAttachmentsCollection), ).onUpdate(() => { this._onDidChangeContext.fire(); }); @@ -71,13 +71,22 @@ export class ChatAttachmentModel extends Disposable { this.addContext(this.asVariableEntry(uri, range)); } - asVariableEntry(uri: URI, range?: IRange, isMarkedReadonly?: boolean): IChatRequestVariableEntry { + addFolder(uri: URI) { + this.addContext({ + value: uri, + id: uri.toString(), + name: basename(uri), + isFile: false, + isDirectory: true, + }); + } + + asVariableEntry(uri: URI, range?: IRange): IChatRequestVariableEntry { return { value: range ? { uri, range } : uri, id: uri.toString() + (range?.toString() ?? ''), name: basename(uri), isFile: true, - isMarkedReadonly, }; } diff --git a/src/vs/workbench/contrib/chat/browser/chatAttachmentModel/chatInstructionsAttachment.ts b/src/vs/workbench/contrib/chat/browser/chatAttachmentModel/chatPromptAttachmentModel.ts similarity index 98% rename from src/vs/workbench/contrib/chat/browser/chatAttachmentModel/chatInstructionsAttachment.ts rename to src/vs/workbench/contrib/chat/browser/chatAttachmentModel/chatPromptAttachmentModel.ts index a9a6a6ffbe5a..bb41b884ec65 100644 --- a/src/vs/workbench/contrib/chat/browser/chatAttachmentModel/chatInstructionsAttachment.ts +++ b/src/vs/workbench/contrib/chat/browser/chatAttachmentModel/chatPromptAttachmentModel.ts @@ -12,7 +12,7 @@ import { IInstantiationService } from '../../../../../platform/instantiation/com /** * Model for a single chat prompt instructions attachment. */ -export class ChatInstructionsAttachmentModel extends Disposable { +export class ChatPromptAttachmentModel extends Disposable { /** * Private reference of the underlying prompt instructions * reference instance. diff --git a/src/vs/workbench/contrib/chat/browser/chatAttachmentModel/chatInstructionAttachmentsModel.ts b/src/vs/workbench/contrib/chat/browser/chatAttachmentModel/chatPromptAttachmentsCollection.ts similarity index 91% rename from src/vs/workbench/contrib/chat/browser/chatAttachmentModel/chatInstructionAttachmentsModel.ts rename to src/vs/workbench/contrib/chat/browser/chatAttachmentModel/chatPromptAttachmentsCollection.ts index 144e5bb96a17..a659cb8080b1 100644 --- a/src/vs/workbench/contrib/chat/browser/chatAttachmentModel/chatInstructionAttachmentsModel.ts +++ b/src/vs/workbench/contrib/chat/browser/chatAttachmentModel/chatPromptAttachmentsCollection.ts @@ -6,9 +6,9 @@ import { URI } from '../../../../../base/common/uri.js'; import { Emitter } from '../../../../../base/common/event.js'; import { IChatRequestVariableEntry } from '../../common/chatModel.js'; +import { ChatPromptAttachmentModel } from './chatPromptAttachmentModel.js'; import { PromptsConfig } from '../../../../../platform/prompts/common/config.js'; import { IPromptFileReference } from '../../common/promptSyntax/parsers/types.js'; -import { ChatInstructionsAttachmentModel } from './chatInstructionsAttachment.js'; import { Disposable, DisposableMap } from '../../../../../base/common/lifecycle.js'; import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; @@ -55,19 +55,18 @@ export const toChatVariable = ( isSelection: false, enabled: true, isFile: true, - isMarkedReadonly: isPromptSnippet, }; }; /** * Model for a collection of prompt instruction attachments. - * See {@linkcode ChatInstructionsAttachmentModel} for individual attachment. + * See {@linkcode ChatPromptAttachmentModel} for individual attachment. */ -export class ChatInstructionAttachmentsModel extends Disposable { +export class ChatPromptAttachmentsCollection extends Disposable { /** * List of all prompt instruction attachments. */ - private attachments: DisposableMap = + private attachments: DisposableMap = this._register(new DisposableMap()); /** @@ -146,13 +145,13 @@ export class ChatInstructionAttachmentsModel extends Disposable { * Event that fires when a new prompt instruction attachment is added. * See {@linkcode onAdd}. */ - protected _onAdd = this._register(new Emitter()); + protected _onAdd = this._register(new Emitter()); /** * The `onAdd` event fires when a new prompt instruction attachment is added. * * @param callback Function to invoke on add. */ - public onAdd(callback: (attachment: ChatInstructionsAttachmentModel) => unknown): this { + public onAdd(callback: (attachment: ChatPromptAttachmentModel) => unknown): this { this._register(this._onAdd.event(callback)); return this; @@ -177,7 +176,7 @@ export class ChatInstructionAttachmentsModel extends Disposable { return this; } - const instruction = this.initService.createInstance(ChatInstructionsAttachmentModel, uri) + const instruction = this.initService.createInstance(ChatPromptAttachmentModel, uri) .onUpdate(this._onUpdate.fire) .onDispose(() => { // note! we have to use `deleteAndLeak` here, because the `*AndDispose` diff --git a/src/vs/workbench/contrib/chat/browser/chatAttachmentWidgets.ts b/src/vs/workbench/contrib/chat/browser/chatAttachmentWidgets.ts new file mode 100644 index 000000000000..40691c175940 --- /dev/null +++ b/src/vs/workbench/contrib/chat/browser/chatAttachmentWidgets.ts @@ -0,0 +1,424 @@ +/*--------------------------------------------------------------------------------------------- + * 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 '../../../../base/browser/dom.js'; +import { Emitter, Event } from '../../../../base/common/event.js'; +import { $, addDisposableListener } from '../../../../base/browser/dom.js'; +import { StandardKeyboardEvent } from '../../../../base/browser/keyboardEvent.js'; +import { Button } from '../../../../base/browser/ui/button/button.js'; +import { IHoverDelegate } from '../../../../base/browser/ui/hover/hoverDelegate.js'; +import { IManagedHoverTooltipMarkdownString } from '../../../../base/browser/ui/hover/hover.js'; +import { Codicon } from '../../../../base/common/codicons.js'; +import { Disposable } from '../../../../base/common/lifecycle.js'; +import { IRange } from '../../../../editor/common/core/range.js'; +import { URI } from '../../../../base/common/uri.js'; +import { localize } from '../../../../nls.js'; +import { ICommandService } from '../../../../platform/commands/common/commands.js'; +import { ITextEditorOptions } from '../../../../platform/editor/common/editor.js'; +import { FileKind } from '../../../../platform/files/common/files.js'; +import { IHoverService } from '../../../../platform/hover/browser/hover.js'; +import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; +import { ILabelService } from '../../../../platform/label/common/label.js'; +import { IOpenerService, OpenInternalOptions } from '../../../../platform/opener/common/opener.js'; +import { IThemeService, FolderThemeIcon } from '../../../../platform/theme/common/themeService.js'; +import { IResourceLabel, ResourceLabels, IFileLabelOptions } from '../../../browser/labels.js'; +import { revealInSideBarCommand } from '../../files/browser/fileActions.contribution.js'; +import { IChatRequestPasteVariableEntry, IChatRequestVariableEntry, ILinkVariableEntry, isImageVariableEntry } from '../common/chatModel.js'; +import { ILanguageModelChatMetadataAndIdentifier, ILanguageModelsService } from '../common/languageModels.js'; +import { hookUpResourceAttachmentDragAndContextMenu, hookUpSymbolAttachmentDragAndContextMenu } from './chatContentParts/chatAttachmentsContentPart.js'; +import { convertUint8ArrayToString } from './imageUtils.js'; +import { KeyCode } from '../../../../base/common/keyCodes.js'; +import { basename, dirname } from '../../../../base/common/path.js'; +import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; +import { MenuId } from '../../../../platform/actions/common/actions.js'; +import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; + +abstract class AbstractChatAttachmentWidget extends Disposable { + public readonly element: HTMLElement; + public readonly label: IResourceLabel; + + private readonly _onDidDelete: Emitter = this._register(new Emitter()); + get onDidDelete(): Event { + return this._onDidDelete.event; + } + + constructor( + private readonly attachment: IChatRequestVariableEntry, + private readonly shouldFocusClearButton: boolean, + container: HTMLElement, + contextResourceLabels: ResourceLabels, + protected readonly hoverDelegate: IHoverDelegate, + protected readonly currentLanguageModel: ILanguageModelChatMetadataAndIdentifier | undefined, + @ICommandService protected readonly commandService: ICommandService, + @IOpenerService protected readonly openerService: IOpenerService, + ) { + super(); + this.element = dom.append(container, $('.chat-attached-context-attachment.show-file-icons')); + this.label = contextResourceLabels.create(this.element, { supportIcons: true, hoverDelegate, hoverTargetOverride: this.element }); + this._register(this.label); + this.element.tabIndex = 0; + } + + protected modelSupportsVision() { + return this.currentLanguageModel?.metadata.capabilities?.vision ?? false; + } + + protected attachClearButton() { + const clearButton = new Button(this.element, { + supportIcons: true, + hoverDelegate: this.hoverDelegate, + title: localize('chat.attachment.clearButton', "Remove from context") + }); + clearButton.icon = Codicon.close; + this._register(clearButton); + this._register(Event.once(clearButton.onDidClick)((e) => { + this._onDidDelete.fire(e); + })); + if (this.shouldFocusClearButton) { + clearButton.focus(); + } + } + + protected addResourceOpenHandlers(resource: URI, range: IRange | undefined): void { + this.element.style.cursor = 'pointer'; + this._register(dom.addDisposableListener(this.element, dom.EventType.CLICK, (e: MouseEvent) => { + dom.EventHelper.stop(e, true); + if (this.attachment.isDirectory) { + this.openResource(resource, true); + } else { + this.openResource(resource, false, range); + } + })); + + this._register(dom.addDisposableListener(this.element, dom.EventType.KEY_DOWN, (e: KeyboardEvent) => { + const event = new StandardKeyboardEvent(e); + if (event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) { + dom.EventHelper.stop(e, true); + if (this.attachment.isDirectory) { + this.openResource(resource, true); + } else { + this.openResource(resource, false, range); + } + } + })); + } + + protected openResource(resource: URI, isDirectory: true): void; + protected openResource(resource: URI, isDirectory: false, range: IRange | undefined): void; + protected openResource(resource: URI, isDirectory?: boolean, range?: IRange): void { + if (isDirectory) { + // Reveal Directory in explorer + this.commandService.executeCommand(revealInSideBarCommand.id, resource); + return; + } + + // Open file in editor + const openTextEditorOptions: ITextEditorOptions | undefined = range ? { selection: range } : undefined; + const options: OpenInternalOptions = { + fromUserGesture: true, + editorOptions: openTextEditorOptions, + }; + this.openerService.open(resource, options); + } +} + +export class FileAttachmentWidget extends AbstractChatAttachmentWidget { + + constructor( + resource: URI, + range: IRange | undefined, + attachment: IChatRequestVariableEntry, + currentLanguageModel: ILanguageModelChatMetadataAndIdentifier | undefined, + shouldFocusClearButton: boolean, + container: HTMLElement, + contextResourceLabels: ResourceLabels, + hoverDelegate: IHoverDelegate, + @ICommandService commandService: ICommandService, + @IOpenerService openerService: IOpenerService, + @IThemeService private readonly themeService: IThemeService, + @IHoverService private readonly hoverService: IHoverService, + @ILanguageModelsService private readonly languageModelsService: ILanguageModelsService, + @IInstantiationService private readonly instantiationService: IInstantiationService, + ) { + super(attachment, shouldFocusClearButton, container, contextResourceLabels, hoverDelegate, currentLanguageModel, commandService, openerService); + + const fileBasename = basename(resource.path); + const fileDirname = dirname(resource.path); + const friendlyName = `${fileBasename} ${fileDirname}`; + const ariaLabel = range ? localize('chat.fileAttachmentWithRange', "Attached file, {0}, line {1} to line {2}", friendlyName, range.startLineNumber, range.endLineNumber) : localize('chat.fileAttachment', "Attached file, {0}", friendlyName); + this.element.ariaLabel = ariaLabel; + + if (attachment.isOmitted) { + this.renderOmittedWarning(friendlyName, ariaLabel, hoverDelegate); + } else { + const fileOptions: IFileLabelOptions = { hidePath: true }; + this.label.setFile(resource, attachment.isFile ? { + ...fileOptions, + fileKind: FileKind.FILE, + range, + } : { + ...fileOptions, + fileKind: FileKind.FOLDER, + icon: !this.themeService.getFileIconTheme().hasFolderIcons ? FolderThemeIcon : undefined + }); + } + + this.instantiationService.invokeFunction(accessor => { + this._register(hookUpResourceAttachmentDragAndContextMenu(accessor, this.element, resource)); + }); + this.addResourceOpenHandlers(resource, range); + + this.attachClearButton(); + } + + private renderOmittedWarning(friendlyName: string, ariaLabel: string, hoverDelegate: IHoverDelegate) { + const pillIcon = dom.$('div.chat-attached-context-pill', {}, dom.$(this.modelSupportsVision() ? 'span.codicon.codicon-file-media' : 'span.codicon.codicon-warning')); + const textLabel = dom.$('span.chat-attached-context-custom-text', {}, friendlyName); + this.element.appendChild(pillIcon); + this.element.appendChild(textLabel); + + const hoverElement = dom.$('div.chat-attached-context-hover'); + hoverElement.setAttribute('aria-label', ariaLabel); + + if (!this.modelSupportsVision()) { + this.element.classList.add('warning'); + hoverElement.textContent = localize('chat.fileAttachmentHover', "{0} does not support this {1} type.", this.currentLanguageModel ? this.languageModelsService.lookupLanguageModel(this.currentLanguageModel.identifier)?.name : this.currentLanguageModel, 'file'); + this._register(this.hoverService.setupManagedHover(hoverDelegate, this.element, hoverElement, { trapFocus: true })); + } + } +} + +export class ImageAttachmentWidget extends AbstractChatAttachmentWidget { + + constructor( + resource: URI | undefined, + attachment: IChatRequestVariableEntry, + currentLanguageModel: ILanguageModelChatMetadataAndIdentifier | undefined, + shouldFocusClearButton: boolean, + container: HTMLElement, + contextResourceLabels: ResourceLabels, + hoverDelegate: IHoverDelegate, + @ICommandService commandService: ICommandService, + @IOpenerService openerService: IOpenerService, + @IHoverService private readonly hoverService: IHoverService, + @ILanguageModelsService private readonly languageModelsService: ILanguageModelsService, + @ITelemetryService private readonly telemetryService: ITelemetryService, + ) { + super(attachment, shouldFocusClearButton, container, contextResourceLabels, hoverDelegate, currentLanguageModel, commandService, openerService); + + const ariaLabel = localize('chat.imageAttachment', "Attached image, {0}", attachment.name); + this.element.ariaLabel = ariaLabel; + this.element.style.position = 'relative'; + + if (attachment.references) { + this.element.style.cursor = 'pointer'; + const clickHandler = () => { + if (attachment.references && URI.isUri(attachment.references[0].reference)) { + this.openResource(attachment.references[0].reference, false, undefined); + } + }; + this._register(addDisposableListener(this.element, 'click', clickHandler)); + } + + const pillIcon = dom.$('div.chat-attached-context-pill', {}, dom.$(this.modelSupportsVision() ? 'span.codicon.codicon-file-media' : 'span.codicon.codicon-warning')); + const textLabel = dom.$('span.chat-attached-context-custom-text', {}, attachment.name); + this.element.appendChild(pillIcon); + this.element.appendChild(textLabel); + + const hoverElement = dom.$('div.chat-attached-context-hover'); + hoverElement.setAttribute('aria-label', ariaLabel); + + type AttachImageEvent = { + currentModel: string; + supportsVision: boolean; + }; + type AttachImageEventClassification = { + currentModel: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The model at the point of attaching the image.' }; + supportsVision: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the current model supports vision or not.' }; + owner: 'justschen'; + comment: 'Event used to gain insights when images are attached, and if the model supported vision or not.'; + }; + + const currentLanguageModelName = this.currentLanguageModel ? this.languageModelsService.lookupLanguageModel(this.currentLanguageModel.identifier)?.name ?? this.currentLanguageModel.identifier : 'unknown'; + const supportsVision = this.modelSupportsVision(); + + this.telemetryService.publicLog2('copilot.attachImage', { + currentModel: currentLanguageModelName, + supportsVision: supportsVision + }); + + if (!supportsVision && this.currentLanguageModel) { + this.element.classList.add('warning'); + hoverElement.textContent = localize('chat.fileAttachmentHover', "{0} does not support this {1} type.", currentLanguageModelName, 'image'); + this._register(this.hoverService.setupManagedHover(hoverDelegate, this.element, hoverElement, { trapFocus: true })); + } else { + const buffer = attachment.value as Uint8Array; + const isURL = isImageVariableEntry(attachment) && attachment.isURL; + if (isURL) { + hoverElement.textContent = localize('chat.imageAttachmentHover', "{0}", convertUint8ArrayToString(buffer)); + this._register(this.hoverService.setupManagedHover(hoverDelegate, this.element, hoverElement, { trapFocus: true })); + } + this.createImageElements(buffer, this.element, hoverElement); + this._register(this.hoverService.setupManagedHover(hoverDelegate, this.element, hoverElement, { trapFocus: false })); + } + + if (resource) { + this.addResourceOpenHandlers(resource, undefined); + } + + this.attachClearButton(); + } + + private createImageElements(buffer: ArrayBuffer | Uint8Array, widget: HTMLElement, hoverElement: HTMLElement) { + const blob = new Blob([buffer], { type: 'image/png' }); + const url = URL.createObjectURL(blob); + const pillImg = dom.$('img.chat-attached-context-pill-image', { src: url, alt: '' }); + const pill = dom.$('div.chat-attached-context-pill', {}, pillImg); + + const existingPill = widget.querySelector('.chat-attached-context-pill'); + if (existingPill) { + existingPill.replaceWith(pill); + } + + const hoverImage = dom.$('img.chat-attached-context-image', { src: url, alt: '' }); + + // Update hover image + hoverElement.appendChild(hoverImage); + + hoverImage.onload = () => { + URL.revokeObjectURL(url); + }; + + hoverImage.onerror = () => { + // reset to original icon on error or invalid image + const pillIcon = dom.$('div.chat-attached-context-pill', {}, dom.$('span.codicon.codicon-file-media')); + const pill = dom.$('div.chat-attached-context-pill', {}, pillIcon); + const existingPill = widget.querySelector('.chat-attached-context-pill'); + if (existingPill) { + existingPill.replaceWith(pill); + } + }; + } +} + +export class PasteAttachmentWidget extends AbstractChatAttachmentWidget { + + constructor( + attachment: IChatRequestPasteVariableEntry, + currentLanguageModel: ILanguageModelChatMetadataAndIdentifier | undefined, + shouldFocusClearButton: boolean, + container: HTMLElement, + contextResourceLabels: ResourceLabels, + hoverDelegate: IHoverDelegate, + @ICommandService commandService: ICommandService, + @IOpenerService openerService: IOpenerService, + @IHoverService private readonly hoverService: IHoverService, + @IInstantiationService private readonly instantiationService: IInstantiationService, + ) { + super(attachment, shouldFocusClearButton, container, contextResourceLabels, hoverDelegate, currentLanguageModel, commandService, openerService); + + const ariaLabel = localize('chat.attachment', "Attached context, {0}", attachment.name); + this.element.ariaLabel = ariaLabel; + + const classNames = ['file-icon', `${attachment.language}-lang-file-icon`]; + let resource: URI | undefined; + let range: IRange | undefined; + + if (attachment.copiedFrom) { + resource = attachment.copiedFrom.uri; + range = attachment.copiedFrom.range; + const filename = basename(resource.path); + this.label.setLabel(filename, undefined, { extraClasses: classNames }); + } else { + this.label.setLabel(attachment.fileName, undefined, { extraClasses: classNames }); + } + this.element.appendChild(dom.$('span.attachment-additional-info', {}, `Pasted ${attachment.pastedLines}`)); + + this.element.style.position = 'relative'; + + const sourceUri = attachment.copiedFrom?.uri; + const hoverContent: IManagedHoverTooltipMarkdownString = { + markdown: { + value: `${sourceUri ? this.instantiationService.invokeFunction(accessor => accessor.get(ILabelService).getUriLabel(sourceUri, { relative: true })) : attachment.fileName}\n\n---\n\n\`\`\`${attachment.language}\n\n${attachment.code}\n\`\`\``, + }, + markdownNotSupportedFallback: attachment.code, + }; + this._register(this.hoverService.setupManagedHover(hoverDelegate, this.element, hoverContent, { trapFocus: true })); + + const copiedFromResource = attachment.copiedFrom?.uri; + if (copiedFromResource) { + this._register(this.instantiationService.invokeFunction(accessor => hookUpResourceAttachmentDragAndContextMenu(accessor, this.element, copiedFromResource))); + this.addResourceOpenHandlers(copiedFromResource, range); + } + + this.attachClearButton(); + } +} + +export class LinkAttachmentWidget extends AbstractChatAttachmentWidget { + constructor( + attachment: ILinkVariableEntry, + currentLanguageModel: ILanguageModelChatMetadataAndIdentifier | undefined, + shouldFocusClearButton: boolean, + container: HTMLElement, + contextResourceLabels: ResourceLabels, + hoverDelegate: IHoverDelegate, + @ICommandService commandService: ICommandService, + @IOpenerService openerService: IOpenerService, + ) { + super(attachment, shouldFocusClearButton, container, contextResourceLabels, hoverDelegate, currentLanguageModel, commandService, openerService); + + this.element.ariaLabel = localize('chat.attachment.link', "Attached link, {0}", attachment.name); + this.label.setResource({ resource: attachment.value, name: attachment.name }, { icon: Codicon.link, title: attachment.value.toString() }); + this.attachClearButton(); + } +} + +export class DefaultChatAttachmentWidget extends AbstractChatAttachmentWidget { + constructor( + resource: URI | undefined, + range: IRange | undefined, + attachment: IChatRequestVariableEntry, + currentLanguageModel: ILanguageModelChatMetadataAndIdentifier | undefined, + shouldFocusClearButton: boolean, + container: HTMLElement, + contextResourceLabels: ResourceLabels, + hoverDelegate: IHoverDelegate, + @ICommandService commandService: ICommandService, + @IOpenerService openerService: IOpenerService, + @IContextKeyService private readonly contextKeyService: IContextKeyService, + @IInstantiationService private readonly instantiationService: IInstantiationService, + ) { + super(attachment, shouldFocusClearButton, container, contextResourceLabels, hoverDelegate, currentLanguageModel, commandService, openerService); + + const attachmentLabel = attachment.fullName ?? attachment.name; + const withIcon = attachment.icon?.id ? `$(${attachment.icon.id}) ${attachmentLabel}` : attachmentLabel; + this.label.setLabel(withIcon, undefined); + this.element.ariaLabel = localize('chat.attachment', "Attached context, {0}", attachment.name); + + if (attachment.kind === 'diagnostic') { + if (attachment.filterUri) { + resource = attachment.filterUri ? URI.revive(attachment.filterUri) : undefined; + range = attachment.filterRange; + } else { + this.element.style.cursor = 'pointer'; + this._register(dom.addDisposableListener(this.element, dom.EventType.CLICK, () => { + this.commandService.executeCommand('workbench.panel.markers.view.focus'); + })); + } + } + + if (attachment.kind === 'symbol') { + const scopedContextKeyService = this._register(this.contextKeyService.createScoped(this.element)); + this._register(this.instantiationService.invokeFunction(accessor => hookUpSymbolAttachmentDragAndContextMenu(accessor, this.element, scopedContextKeyService, { ...attachment, kind: attachment.symbolKind }, MenuId.ChatInputSymbolAttachmentContext))); + } + + if (resource) { + this.addResourceOpenHandlers(resource, range); + } + + this.attachClearButton(); + } +} diff --git a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatAttachmentsContentPart.ts b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatAttachmentsContentPart.ts index b62621ea5bbc..bd7fd35870c7 100644 --- a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatAttachmentsContentPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatAttachmentsContentPart.ts @@ -8,7 +8,6 @@ import { StandardMouseEvent } from '../../../../../base/browser/mouseEvent.js'; import { IManagedHoverTooltipMarkdownString } from '../../../../../base/browser/ui/hover/hover.js'; import { IHoverDelegate } from '../../../../../base/browser/ui/hover/hoverDelegate.js'; import { createInstantHoverDelegate } from '../../../../../base/browser/ui/hover/hoverDelegateFactory.js'; -import { Promises } from '../../../../../base/common/async.js'; import { Codicon } from '../../../../../base/common/codicons.js'; import { Emitter } from '../../../../../base/common/event.js'; import { Disposable, DisposableStore, IDisposable } from '../../../../../base/common/lifecycle.js'; @@ -41,8 +40,9 @@ import { fillEditorsDragData } from '../../../../browser/dnd.js'; import { ResourceLabels } from '../../../../browser/labels.js'; import { ResourceContextKey } from '../../../../common/contextkeys.js'; import { revealInSideBarCommand } from '../../../files/browser/fileActions.contribution.js'; -import { IChatRequestVariableEntry, isLinkVariableEntry, isPasteVariableEntry } from '../../common/chatModel.js'; +import { IChatRequestVariableEntry, isImageVariableEntry, isLinkVariableEntry, isPasteVariableEntry } from '../../common/chatModel.js'; import { ChatResponseReferencePartStatusKind, IChatContentReference } from '../../common/chatService.js'; +import { convertUint8ArrayToString } from '../imageUtils.js'; export const chatAttachmentResourceContextKey = new RawContextKey('chatAttachmentResource', undefined, { type: 'URI', description: localize('resource', "The full value of the chat attachment resource, including scheme and path") }); @@ -61,7 +61,6 @@ export class ChatAttachmentsContentPart extends Disposable { @IInstantiationService private readonly instantiationService: IInstantiationService, @IOpenerService private readonly openerService: IOpenerService, @IHoverService private readonly hoverService: IHoverService, - @IFileService private readonly fileService: IFileService, @ICommandService private readonly commandService: ICommandService, @IThemeService private readonly themeService: IThemeService, @ILabelService private readonly labelService: ILabelService, @@ -74,12 +73,12 @@ export class ChatAttachmentsContentPart extends Disposable { } } + // TODO@joyceerhl adopt chat attachment widgets private initAttachedContext(container: HTMLElement) { dom.clearNode(container); this.attachedContextDisposables.clear(); const hoverDelegate = this.attachedContextDisposables.add(createInstantHoverDelegate()); - const attachmentInitPromises: Promise[] = []; this.variables.forEach(async (attachment) => { let resource = URI.isUri(attachment.value) ? attachment.value : attachment.value && typeof attachment.value === 'object' && 'uri' in attachment.value && URI.isUri(attachment.value.uri) ? attachment.value.uri : undefined; let range = attachment.value && typeof attachment.value === 'object' && 'range' in attachment.value && Range.isIRange(attachment.value.range) ? attachment.value.range : undefined; @@ -133,7 +132,8 @@ export class ChatAttachmentsContentPart extends Disposable { } else if (attachment.isImage) { ariaLabel = localize('chat.imageAttachment', "Attached image, {0}", attachment.name); - const hoverElement = this.customAttachment(widget, attachment.name, hoverDelegate, ariaLabel, isAttachmentOmitted, attachment.isImage); + const isURL = isImageVariableEntry(attachment) && attachment.isURL; + const hoverElement = this.customAttachment(widget, attachment.name, hoverDelegate, ariaLabel, isAttachmentOmitted, attachment.isImage, isURL, attachment.value as Uint8Array); if (attachment.references) { widget.style.cursor = 'pointer'; @@ -146,25 +146,9 @@ export class ChatAttachmentsContentPart extends Disposable { } if (!isAttachmentPartialOrOmitted) { - attachmentInitPromises.push(Promises.withAsyncBody(async (resolve) => { - let buffer: Uint8Array; - try { - if (attachment.value instanceof URI) { - const readFile = await this.fileService.readFile(attachment.value); - if (this.attachedContextDisposables.isDisposed) { - return; - } - buffer = readFile.value.buffer; - } else { - buffer = attachment.value as Uint8Array; - } - this.createImageElements(buffer, widget, hoverElement); - } catch (error) { - console.error('Error processing attachment:', error); - } - this.attachedContextDisposables.add(this.hoverService.setupManagedHover(hoverDelegate, widget, hoverElement, { trapFocus: false })); - resolve(); - })); + const buffer = attachment.value as Uint8Array; + this.createImageElements(buffer, widget, hoverElement); + this.attachedContextDisposables.add(this.hoverService.setupManagedHover(hoverDelegate, widget, hoverElement, { trapFocus: false })); } widget.style.position = 'relative'; } else if (isPasteVariableEntry(attachment)) { @@ -229,7 +213,6 @@ export class ChatAttachmentsContentPart extends Disposable { } } - await Promise.all(attachmentInitPromises); if (this.attachedContextDisposables.isDisposed) { return; } @@ -253,7 +236,7 @@ export class ChatAttachmentsContentPart extends Disposable { }); } - private customAttachment(widget: HTMLElement, friendlyName: string, hoverDelegate: IHoverDelegate, ariaLabel: string, isAttachmentOmitted: boolean, isImage?: boolean): HTMLElement { + private customAttachment(widget: HTMLElement, friendlyName: string, hoverDelegate: IHoverDelegate, ariaLabel: string, isAttachmentOmitted: boolean, isImage?: boolean, isURL?: boolean, value?: Uint8Array): HTMLElement { const pillIcon = dom.$('div.chat-attached-context-pill', {}, dom.$(isAttachmentOmitted ? 'span.codicon.codicon-warning' : 'span.codicon.codicon-file-media')); const textLabel = dom.$('span.chat-attached-context-custom-text', {}, friendlyName); widget.appendChild(pillIcon); @@ -262,6 +245,12 @@ export class ChatAttachmentsContentPart extends Disposable { const hoverElement = dom.$('div.chat-attached-context-hover'); hoverElement.setAttribute('aria-label', ariaLabel); + if (isURL && !isAttachmentOmitted && value) { + hoverElement.textContent = localize('chat.imageAttachmentHover', "{0}", convertUint8ArrayToString(value)); + this.attachedContextDisposables.add(this.hoverService.setupManagedHover(hoverDelegate, widget, hoverElement, { trapFocus: true })); + } + + if (isAttachmentOmitted) { widget.classList.add('warning'); hoverElement.textContent = localize('chat.fileAttachmentHover', "Selected model does not support this {0} type.", isImage ? 'image' : 'file'); @@ -304,6 +293,20 @@ export class ChatAttachmentsContentPart extends Disposable { // Update hover image hoverElement.appendChild(img); + + img.onload = () => { + URL.revokeObjectURL(url); + }; + + img.onerror = () => { + // reset to original icon on error or invalid image + const pillIcon = dom.$('div.chat-attached-context-pill', {}, dom.$('span.codicon.codicon-file-media')); + const pill = dom.$('div.chat-attached-context-pill', {}, pillIcon); + const existingPill = widget.querySelector('.chat-attached-context-pill'); + if (existingPill) { + existingPill.replaceWith(pill); + } + }; } } diff --git a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatConfirmationContentPart.ts b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatConfirmationContentPart.ts index 68a8d913c373..c3361ccfd3ba 100644 --- a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatConfirmationContentPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatConfirmationContentPart.ts @@ -53,7 +53,9 @@ export class ChatConfirmationContentPart extends Disposable implements IChatCont options.agentId = element.agent?.id; options.slashCommand = element.slashCommand?.name; options.confirmation = e.label; - options.userSelectedModelId = chatWidgetService.getWidgetBySessionId(element.sessionId)?.input.currentLanguageModel; + const widget = chatWidgetService.getWidgetBySessionId(element.sessionId); + options.userSelectedModelId = widget?.input.currentLanguageModel; + options.mode = widget?.input.currentMode; if (await this.chatService.sendRequest(element.sessionId, prompt, options)) { confirmation.isUsed = true; confirmationWidget.setShowButtons(false); diff --git a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatConfirmationWidget.ts b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatConfirmationWidget.ts index b6f42ef60cca..f95fd944049a 100644 --- a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatConfirmationWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatConfirmationWidget.ts @@ -12,18 +12,21 @@ import { Disposable } from '../../../../../base/common/lifecycle.js'; import { MarkdownRenderer } from '../../../../../editor/browser/widget/markdownRenderer/browser/markdownRenderer.js'; import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; import { defaultButtonStyles } from '../../../../../platform/theme/browser/defaultStyles.js'; +import { renderIcon } from '../../../../../base/browser/ui/iconLabel/iconLabels.js'; +import { Codicon } from '../../../../../base/common/codicons.js'; export interface IChatConfirmationButton { label: string; isSecondary?: boolean; + tooltip?: string; data: any; } -export class ChatConfirmationWidget extends Disposable { +abstract class BaseChatConfirmationWidget extends Disposable { private _onDidClick = this._register(new Emitter()); get onDidClick(): Event { return this._onDidClick.event; } - private _onDidChangeHeight = this._register(new Emitter()); + protected _onDidChangeHeight = this._register(new Emitter()); get onDidChangeHeight(): Event { return this._onDidChangeHeight.event; } private _domNode: HTMLElement; @@ -35,37 +38,94 @@ export class ChatConfirmationWidget extends Disposable { this.domNode.classList.toggle('hideButtons', !showButton); } + private readonly messageElement: HTMLElement; + protected readonly markdownRenderer: MarkdownRenderer; + constructor( title: string, - message: string | IMarkdownString, + rawInput: object | undefined, buttons: IChatConfirmationButton[], - @IInstantiationService private readonly instantiationService: IInstantiationService, + @IInstantiationService protected readonly instantiationService: IInstantiationService, ) { super(); const elements = dom.h('.chat-confirmation-widget@root', [ - dom.h('.chat-confirmation-widget-title@title'), + dom.h('.chat-confirmation-widget-title-container@titleContainer', [ + dom.h('.chat-confirmation-widget-expando@expando'), + dom.h('.chat-confirmation-widget-title@title'), + dom.h('.chat-confirmation-widget-input-value@inputValue'), + ]), dom.h('.chat-confirmation-widget-message@message'), dom.h('.chat-confirmation-buttons-container@buttonsContainer'), ]); this._domNode = elements.root; - const renderer = this.instantiationService.createInstance(MarkdownRenderer, {}); + this.markdownRenderer = this.instantiationService.createInstance(MarkdownRenderer, {}); - const renderedTitle = this._register(renderer.render(new MarkdownString(title), { + const renderedTitle = this._register(this.markdownRenderer.render(new MarkdownString(title, { supportThemeIcons: true }), { asyncRenderCallback: () => this._onDidChangeHeight.fire(), })); - elements.title.appendChild(renderedTitle.element); + elements.title.append(renderedTitle.element); - const renderedMessage = this._register(renderer.render( - typeof message === 'string' ? new MarkdownString(message) : message, - { asyncRenderCallback: () => this._onDidChangeHeight.fire() } - )); - elements.message.appendChild(renderedMessage.element); + elements.titleContainer.classList.toggle('input', !!rawInput); + + if (rawInput) { + + dom.reset(elements.expando, renderIcon(Codicon.chevronRight)); + + const inputMDStr = new MarkdownString().appendCodeblock('json', JSON.stringify(rawInput, undefined, 2)); + const renderedInput = this._register(this.markdownRenderer.render(inputMDStr, { asyncRenderCallback: () => this._onDidChangeHeight.fire() })); + elements.inputValue.append(renderedInput.element); + + let expanded = false; + this._register(dom.addStandardDisposableListener(elements.titleContainer, 'click', () => { + expanded = !expanded; + elements.titleContainer.classList.toggle('expanded', expanded); + this._onDidChangeHeight.fire(); + dom.reset(elements.expando, expanded ? renderIcon(Codicon.chevronDown) : renderIcon(Codicon.chevronRight)); + })); + } + + this.messageElement = elements.message; buttons.forEach(buttonData => { - const button = new Button(elements.buttonsContainer, { ...defaultButtonStyles, secondary: buttonData.isSecondary }); + const button = this._register(new Button(elements.buttonsContainer, { ...defaultButtonStyles, secondary: buttonData.isSecondary, title: buttonData.tooltip })); button.label = buttonData.label; this._register(button.onDidClick(() => this._onDidClick.fire(buttonData))); }); } + + protected renderMessage(element: HTMLElement): void { + this.messageElement.append(element); + } +} + +export class ChatConfirmationWidget extends BaseChatConfirmationWidget { + constructor( + title: string, + private readonly message: string | IMarkdownString, + rawInput: object | undefined, + buttons: IChatConfirmationButton[], + @IInstantiationService instantiationService: IInstantiationService, + ) { + super(title, rawInput, buttons, instantiationService); + + const renderedMessage = this._register(this.markdownRenderer.render( + typeof this.message === 'string' ? new MarkdownString(this.message) : this.message, + { asyncRenderCallback: () => this._onDidChangeHeight.fire() } + )); + this.renderMessage(renderedMessage.element); + } +} + +export class ChatCustomConfirmationWidget extends BaseChatConfirmationWidget { + constructor( + title: string, + messageElement: HTMLElement, + rawInput: object | undefined, + buttons: IChatConfirmationButton[], + @IInstantiationService instantiationService: IInstantiationService, + ) { + super(title, rawInput, buttons, instantiationService); + this.renderMessage(messageElement); + } } diff --git a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatContentParts.ts b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatContentParts.ts index a9375bb86881..c4d1f90c5761 100644 --- a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatContentParts.ts +++ b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatContentParts.ts @@ -4,11 +4,21 @@ *--------------------------------------------------------------------------------------------*/ import { IDisposable } from '../../../../../base/common/lifecycle.js'; -import { ChatTreeItem } from '../chat.js'; +import { ChatTreeItem, IChatCodeBlockInfo } from '../chat.js'; import { IChatRendererContent } from '../../common/chatViewModel.js'; export interface IChatContentPart extends IDisposable { - domNode: HTMLElement; + domNode: HTMLElement | undefined; + + /** + * Used to indicate a part's ownership of a code block. + */ + codeblocksPartId?: string; + + /** + * Codeblocks that were rendered by this part into CodeBlockModelCollection. + */ + codeblocks?: IChatCodeBlockInfo[]; /** * Returns true if the other content is equivalent to what is already rendered in this content part. @@ -16,6 +26,8 @@ export interface IChatContentPart extends IDisposable { * followingContent is all the content that will be rendered after this content part (to support progress messages' behavior). */ hasSameContent(other: IChatRendererContent, followingContent: IChatRendererContent[], element: ChatTreeItem): boolean; + + addDisposable?(disposable: IDisposable): void; } export interface IChatContentPartRenderContext { diff --git a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatMarkdownContentPart.ts b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatMarkdownContentPart.ts index 215acff06b2a..d9238e060d82 100644 --- a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatMarkdownContentPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatMarkdownContentPart.ts @@ -5,10 +5,11 @@ import * as dom from '../../../../../base/browser/dom.js'; import { StandardMouseEvent } from '../../../../../base/browser/mouseEvent.js'; +import { HoverPosition } from '../../../../../base/browser/ui/hover/hoverWidget.js'; import { findLast } from '../../../../../base/common/arraysFind.js'; import { Codicon } from '../../../../../base/common/codicons.js'; import { Emitter } from '../../../../../base/common/event.js'; -import { Disposable, DisposableStore, IDisposable } from '../../../../../base/common/lifecycle.js'; +import { Disposable, DisposableStore, IDisposable, MutableDisposable } from '../../../../../base/common/lifecycle.js'; import { autorun } from '../../../../../base/common/observable.js'; import { equalsIgnoreCase } from '../../../../../base/common/strings.js'; import { ThemeIcon } from '../../../../../base/common/themables.js'; @@ -26,6 +27,7 @@ import { IMenuService, MenuId } from '../../../../../platform/actions/common/act import { IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js'; import { IContextMenuService } from '../../../../../platform/contextview/browser/contextView.js'; import { FileKind } from '../../../../../platform/files/common/files.js'; +import { IHoverService } from '../../../../../platform/hover/browser/hover.js'; import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; import { ILabelService } from '../../../../../platform/label/common/label.js'; import { IEditorService } from '../../../../services/editor/common/editorService.js'; @@ -35,20 +37,25 @@ import { IChatProgressRenderableResponseContent } from '../../common/chatModel.j import { IChatMarkdownContent, IChatUndoStop } from '../../common/chatService.js'; import { isRequestVM, isResponseVM } from '../../common/chatViewModel.js'; import { CodeBlockModelCollection } from '../../common/codeBlockModelCollection.js'; -import { IChatCodeBlockInfo, IChatListItemRendererOptions } from '../chat.js'; +import { IChatCodeBlockInfo } from '../chat.js'; import { IChatRendererDelegate } from '../chatListRenderer.js'; import { ChatMarkdownDecorationsRenderer } from '../chatMarkdownDecorationsRenderer.js'; import { ChatEditorOptions } from '../chatOptions.js'; -import { CodeBlockPart, ICodeBlockData, localFileLanguageId, parseLocalFileData } from '../codeBlockPart.js'; +import { CodeBlockPart, ICodeBlockData, ICodeBlockRenderOptions, localFileLanguageId, parseLocalFileData } from '../codeBlockPart.js'; import '../media/chatCodeBlockPill.css'; import { IDisposableReference, ResourcePool } from './chatCollections.js'; import { IChatContentPart, IChatContentPartRenderContext } from './chatContentParts.js'; const $ = dom.$; +export interface IChatMarkdownContentPartOptions { + readonly codeBlockRenderOptions?: ICodeBlockRenderOptions; + readonly renderCodeBlockPills?: boolean; +} + export class ChatMarkdownContentPart extends Disposable implements IChatContentPart { private static idPool = 0; - public readonly id = String(++ChatMarkdownContentPart.idPool); + public readonly codeblocksPartId = String(++ChatMarkdownContentPart.idPool); public readonly domNode: HTMLElement; private readonly allRefs: IDisposableReference[] = []; @@ -66,7 +73,7 @@ export class ChatMarkdownContentPart extends Disposable implements IChatContentP renderer: MarkdownRenderer, currentWidth: number, private readonly codeBlockModelCollection: CodeBlockModelCollection, - private readonly rendererOptions: IChatListItemRendererOptions, + private readonly rendererOptions: IChatMarkdownContentPartOptions, @IContextKeyService contextKeyService: IContextKeyService, @ITextModelService private readonly textModelService: ITextModelService, @IInstantiationService private readonly instantiationService: IInstantiationService, @@ -116,7 +123,13 @@ export class ChatMarkdownContentPart extends Disposable implements IChatContentP } const hideToolbar = isResponseVM(element) && element.errorDetails?.responseIsFiltered; - const codeBlockInfo: ICodeBlockData = { languageId, textModel, codeBlockIndex: globalIndex, codeBlockPartIndex: thisPartIndex, element, range, hideToolbar, parentContextKeyService: contextKeyService, vulns, codemapperUri }; + const renderOptions = { + ...this.rendererOptions.codeBlockRenderOptions, + }; + if (hideToolbar !== undefined) { + renderOptions.hideToolbar = hideToolbar; + } + const codeBlockInfo: ICodeBlockData = { languageId, textModel, codeBlockIndex: globalIndex, codeBlockPartIndex: thisPartIndex, element, range, parentContextKeyService: contextKeyService, vulns, codemapperUri, renderOptions }; if (!rendererOptions.renderCodeBlockPills || element.isCompleteAddedRequest || !codemapperUri) { const ref = this.renderCodeBlock(codeBlockInfo, text, isCodeBlockComplete, currentWidth); @@ -126,11 +139,11 @@ export class ChatMarkdownContentPart extends Disposable implements IChatContentP // not during a renderElement OR a progressive render (when we will be firing this event anyway at the end of the render) this._register(ref.object.onDidChangeContentHeight(() => this._onDidChangeHeight.fire())); - const ownerMarkdownPartId = this.id; - const info: IChatCodeBlockInfo = new class { + const ownerMarkdownPartId = this.codeblocksPartId; + const info: IChatCodeBlockInfo = new class implements IChatCodeBlockInfo { readonly ownerMarkdownPartId = ownerMarkdownPartId; readonly codeBlockIndex = globalIndex; - readonly element = element; + readonly elementId = element.id; readonly isStreaming = !rendererOptions.renderCodeBlockPills; codemapperUri = undefined; // will be set async public get uri() { @@ -142,9 +155,6 @@ export class ChatMarkdownContentPart extends Disposable implements IChatContentP public focus() { ref.object.focus(); } - public getContent(): string { - return ref.object.editor.getValue(); - } }(); this.codeblocks.push(info); orderedDisposablesList.push(ref); @@ -161,11 +171,11 @@ export class ChatMarkdownContentPart extends Disposable implements IChatContentP }); } this.allRefs.push(ref); - const ownerMarkdownPartId = this.id; - const info: IChatCodeBlockInfo = new class { + const ownerMarkdownPartId = this.codeblocksPartId; + const info: IChatCodeBlockInfo = new class implements IChatCodeBlockInfo { readonly ownerMarkdownPartId = ownerMarkdownPartId; readonly codeBlockIndex = globalIndex; - readonly element = element; + readonly elementId = element.id; readonly isStreaming = !isCodeBlockComplete; readonly codemapperUri = codemapperUri; public get uri() { @@ -175,9 +185,6 @@ export class ChatMarkdownContentPart extends Disposable implements IChatContentP public focus() { return ref.object.element.focus(); } - public getContent(): string { - return ''; // Not needed for collapsed code blocks - } }(); this.codeblocks.push(info); orderedDisposablesList.push(ref); @@ -289,6 +296,9 @@ class CollapsedCodeBlock extends Disposable { public readonly element: HTMLElement; + private readonly hover = this._register(new MutableDisposable()); + private tooltip: string | undefined; + private _uri: URI | undefined; public get uri(): URI | undefined { return this._uri; @@ -310,6 +320,7 @@ class CollapsedCodeBlock extends Disposable { @IContextKeyService private readonly contextKeyService: IContextKeyService, @IMenuService private readonly menuService: IMenuService, @IChatEditingService private readonly chatEditingService: IChatEditingService, + @IHoverService private readonly hoverService: IHoverService, ) { super(); this.element = $('.chat-codeblock-pill-widget'); @@ -370,8 +381,7 @@ class CollapsedCodeBlock extends Disposable { } this.element.replaceChildren(iconEl, ...children); - this.element.title = this.labelService.getUriLabel(uri, { relative: false }); - + this.updateTooltip(this.labelService.getUriLabel(uri, { relative: false })); const renderDiff = (changes: IEditSessionEntryDiff | undefined) => { const labelAdded = this.element.querySelector('.label-added') ?? this.element.appendChild(dom.$('span.label-added')); @@ -382,7 +392,9 @@ class CollapsedCodeBlock extends Disposable { labelRemoved.textContent = `-${changes.removed}`; const insertionsFragment = changes.added === 1 ? localize('chat.codeblock.insertions.one', "1 insertion") : localize('chat.codeblock.insertions', "{0} insertions", changes.added); const deletionsFragment = changes.removed === 1 ? localize('chat.codeblock.deletions.one', "1 deletion") : localize('chat.codeblock.deletions', "{0} deletions", changes.removed); - this.element.ariaLabel = this.element.title = localize('summary', 'Edited {0}, {1}, {2}', iconText, insertionsFragment, deletionsFragment); + const summary = localize('summary', 'Edited {0}, {1}, {2}', iconText, insertionsFragment, deletionsFragment); + this.element.ariaLabel = summary; + this.updateTooltip(summary); } }; @@ -411,4 +423,18 @@ class CollapsedCodeBlock extends Disposable { } })); } + + private updateTooltip(tooltip: string): void { + this.tooltip = tooltip; + if (!this.hover.value) { + this.hover.value = this.hoverService.setupDelayedHover(this.element, () => ( + { + content: this.tooltip!, + appearance: { compact: true, showPointer: true }, + position: { hoverPosition: HoverPosition.BELOW }, + persistence: { hideOnKeyDown: true }, + } + )); + } + } } diff --git a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatProgressContentPart.ts b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatProgressContentPart.ts index 8a6c2c3d6ec4..e3f08938b2d8 100644 --- a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatProgressContentPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatProgressContentPart.ts @@ -70,11 +70,14 @@ export class ChatProgressContentPart extends Disposable implements IChatContentP private renderFileWidgets(element: HTMLElement): void { const links = element.querySelectorAll('a'); links.forEach(a => { - const href = a.getAttribute('data-href'); - const uri = href ? URI.parse(href) : undefined; - if (uri?.scheme) { - const widget = this._register(this.instantiationService.createInstance(InlineAnchorWidget, a, { kind: 'inlineReference', inlineReference: uri })); - this._register(this.chatMarkdownAnchorService.register(widget)); + // Empty link text -> render file widget + if (!a.textContent?.trim()) { + const href = a.getAttribute('data-href'); + const uri = href ? URI.parse(href) : undefined; + if (uri?.scheme) { + const widget = this._register(this.instantiationService.createInstance(InlineAnchorWidget, a, { kind: 'inlineReference', inlineReference: uri })); + this._register(this.chatMarkdownAnchorService.register(widget)); + } } }); } @@ -117,3 +120,20 @@ export class ChatWorkingProgressContentPart extends ChatProgressContentPart impl return other.kind === 'working' && this.workingProgress.isPaused === other.isPaused; } } + +export class ChatCustomProgressPart { + public readonly domNode: HTMLElement; + + constructor( + messageElement: HTMLElement, + icon: ThemeIcon, + ) { + this.domNode = $('.progress-container'); + const iconElement = $('div'); + iconElement.classList.add(...ThemeIcon.asClassNameArray(icon)); + append(this.domNode, iconElement); + + messageElement.classList.add('progress-step'); + append(this.domNode, messageElement); + } +} diff --git a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatQuotaExceededPart.ts b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatQuotaExceededPart.ts index 97985ca49694..0e8dcd58fa0f 100644 --- a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatQuotaExceededPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatQuotaExceededPart.ts @@ -99,7 +99,7 @@ export class ChatQuotaExceededPart extends Disposable implements IChatContentPar }; this._register(button1.onDidClick(async () => { - await commandService.executeCommand('workbench.action.chat.upgradePlan'); + await commandService.executeCommand('workbench.action.chat.upgradePlan', 'chat-response'); shouldShowRetryButton = true; addRetryButtonIfNeeded(); diff --git a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatReferencesContentPart.ts b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatReferencesContentPart.ts index 427e7b2fca6b..2d53ebef93aa 100644 --- a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatReferencesContentPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatReferencesContentPart.ts @@ -38,7 +38,7 @@ import { ResourceContextKey } from '../../../../common/contextkeys.js'; import { SETTINGS_AUTHORITY } from '../../../../services/preferences/common/preferences.js'; import { createFileIconThemableTreeContainerScope } from '../../../files/browser/views/explorerView.js'; import { ExplorerFolderContext } from '../../../files/common/files.js'; -import { chatEditingWidgetFileReadonlyContextKey, chatEditingWidgetFileStateContextKey, WorkingSetEntryState } from '../../common/chatEditingService.js'; +import { chatEditingWidgetFileStateContextKey, WorkingSetEntryState } from '../../common/chatEditingService.js'; import { ChatResponseReferencePartStatusKind, IChatContentReference, IChatWarningMessage } from '../../common/chatService.js'; import { IChatVariablesService } from '../../common/chatVariables.js'; import { IChatRendererContent, IChatResponseViewModel } from '../../common/chatViewModel.js'; @@ -53,7 +53,6 @@ export interface IChatReferenceListItem extends IChatContentReference { description?: string; state?: WorkingSetEntryState; excluded?: boolean; - isMarkedReadonly?: boolean; } export type IChatCollapsibleListItem = IChatReferenceListItem | IChatWarningMessage; @@ -475,7 +474,6 @@ class CollapsibleListRenderer implements IListRenderer()); public readonly onDidChangeHeight = this._onDidChangeHeight.event; + public get codeblocks(): IChatCodeBlockInfo[] { + return this.subPart?.codeblocks ?? []; + } + + public get codeblocksPartId(): string | undefined { + return this.subPart?.codeblocksPartId; + } + + private subPart!: ChatToolInvocationSubPart; + constructor( toolInvocation: IChatToolInvocation | IChatToolInvocationSerialized, context: IChatContentPartRenderContext, renderer: MarkdownRenderer, listPool: CollapsibleListPool, + editorPool: EditorPool, + currentWidthDelegate: () => number, + codeBlockModelCollection: CodeBlockModelCollection, + codeBlockStartIndex: number, @IInstantiationService instantiationService: IInstantiationService, ) { super(); @@ -47,11 +72,12 @@ export class ChatToolInvocationPart extends Disposable implements IChatContentPa const partStore = this._register(new DisposableStore()); const render = () => { dom.clearNode(this.domNode); + partStore.clear(); - const subPart = partStore.add(instantiationService.createInstance(ChatToolInvocationSubPart, toolInvocation, context, renderer, listPool)); - this.domNode.appendChild(subPart.domNode); - partStore.add(subPart.onDidChangeHeight(() => this._onDidChangeHeight.fire())); - partStore.add(subPart.onNeedsRerender(() => { + this.subPart = partStore.add(instantiationService.createInstance(ChatToolInvocationSubPart, toolInvocation, context, renderer, listPool, editorPool, currentWidthDelegate, codeBlockModelCollection, codeBlockStartIndex)); + this.domNode.appendChild(this.subPart.domNode); + partStore.add(this.subPart.onDidChangeHeight(() => this._onDidChangeHeight.fire())); + partStore.add(this.subPart.onNeedsRerender(() => { render(); this._onDidChangeHeight.fire(); })); @@ -69,30 +95,57 @@ export class ChatToolInvocationPart extends Disposable implements IChatContentPa } class ChatToolInvocationSubPart extends Disposable { + private static idPool = 0; + private readonly _codeblocksPartId = 'tool-' + (ChatToolInvocationSubPart.idPool++); + public readonly domNode: HTMLElement; private _onNeedsRerender = this._register(new Emitter()); public readonly onNeedsRerender = this._onNeedsRerender.event; - private _onDidChangeHeight = this._register(new Relay()); + private _onDidChangeHeight = this._register(new Emitter()); public readonly onDidChangeHeight = this._onDidChangeHeight.event; + private markdownPart: ChatMarkdownContentPart | undefined; + private _codeblocks: IChatCodeBlockInfo[] = []; + public get codeblocks(): IChatCodeBlockInfo[] { + // TODO this is weird, the separate cases should maybe be their own "subparts" + return this.markdownPart?.codeblocks ?? this._codeblocks; + } + + public get codeblocksPartId(): string { + return this.markdownPart?.codeblocksPartId ?? this._codeblocksPartId; + } + constructor( - toolInvocation: IChatToolInvocation | IChatToolInvocationSerialized, - context: IChatContentPartRenderContext, - renderer: MarkdownRenderer, - listPool: CollapsibleListPool, - @IInstantiationService instantiationService: IInstantiationService, - @IHoverService hoverService: IHoverService, + private readonly toolInvocation: IChatToolInvocation | IChatToolInvocationSerialized, + private readonly context: IChatContentPartRenderContext, + private readonly renderer: MarkdownRenderer, + private readonly listPool: CollapsibleListPool, + private readonly editorPool: EditorPool, + private readonly currentWidthDelegate: () => number, + private readonly codeBlockModelCollection: CodeBlockModelCollection, + private readonly codeBlockStartIndex: number, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @IKeybindingService private readonly keybindingService: IKeybindingService, + @IModelService private readonly modelService: IModelService, + @ILanguageService private readonly languageService: ILanguageService, + @IContextKeyService private readonly contextKeyService: IContextKeyService, ) { super(); if (toolInvocation.kind === 'toolInvocation' && toolInvocation.confirmationMessages) { - this.domNode = this.createConfirmationWidget(toolInvocation, instantiationService); + if (toolInvocation.toolSpecificData?.kind === 'terminal') { + this.domNode = this.createTerminalConfirmationWidget(toolInvocation, toolInvocation.toolSpecificData); + } else { + this.domNode = this.createConfirmationWidget(toolInvocation); + } + } else if (toolInvocation.toolSpecificData?.kind === 'terminal') { + this.domNode = this.createTerminalMarkdownProgressPart(toolInvocation, toolInvocation.toolSpecificData); } else if (toolInvocation.resultDetails?.length) { - this.domNode = this.createResultList(toolInvocation.pastTenseMessage ?? toolInvocation.invocationMessage, toolInvocation.resultDetails, context, instantiationService, listPool); + this.domNode = this.createResultList(toolInvocation.pastTenseMessage ?? toolInvocation.invocationMessage, toolInvocation.resultDetails); } else { - this.domNode = this.createProgressPart(toolInvocation, context, renderer, instantiationService, hoverService); + this.domNode = this.createProgressPart(); } if (toolInvocation.kind === 'toolInvocation' && !toolInvocation.isComplete) { @@ -100,80 +153,227 @@ class ChatToolInvocationSubPart extends Disposable { } } - private createConfirmationWidget(toolInvocation: IChatToolInvocation, instantiationService: IInstantiationService): HTMLElement { - if (!toolInvocation.confirmationMessages) { - throw new Error('Confirmation messages are missing'); + private createConfirmationWidget(toolInvocation: IChatToolInvocation): HTMLElement { + assertType(toolInvocation.confirmationMessages, 'Confirmation messages are missing'); + + const title = toolInvocation.confirmationMessages.title; + const message = toolInvocation.confirmationMessages.message; + const toolInput = toolInvocation.confirmationMessages.toolInput; + + const continueLabel = localize('continue', "Continue"); + const continueKeybinding = this.keybindingService.lookupKeybinding(AcceptToolConfirmationActionId)?.getLabel(); + const continueTooltip = continueKeybinding ? `${continueLabel} (${continueKeybinding})` : continueLabel; + const cancelLabel = localize('cancel', "Cancel"); + const cancelKeybinding = this.keybindingService.lookupKeybinding(CancelChatActionId)?.getLabel(); + const cancelTooltip = cancelKeybinding ? `${cancelLabel} (${cancelKeybinding})` : cancelLabel; + + const buttons: IChatConfirmationButton[] = [ + { + label: continueLabel, + data: true, + tooltip: continueTooltip + }, + { + label: cancelLabel, + data: false, + isSecondary: true, + tooltip: cancelTooltip + }]; + let confirmWidget: ChatConfirmationWidget | ChatCustomConfirmationWidget; + if (typeof message === 'string') { + confirmWidget = this._register(this.instantiationService.createInstance( + ChatConfirmationWidget, + title, + message, + toolInput, + buttons + )); + } else { + const chatMarkdownContent: IChatMarkdownContent = { + kind: 'markdownContent', + content: message, + }; + const codeBlockRenderOptions: ICodeBlockRenderOptions = { + hideToolbar: true, + reserveWidth: 19, + verticalPadding: 5, + editorOptions: { + wordWrap: 'on' + } + }; + this.markdownPart = this._register(this.instantiationService.createInstance(ChatMarkdownContentPart, chatMarkdownContent, this.context, this.editorPool, false, this.codeBlockStartIndex, this.renderer, this.currentWidthDelegate(), this.codeBlockModelCollection, { codeBlockRenderOptions })); + this._register(this.markdownPart.onDidChangeHeight(() => this._onDidChangeHeight.fire())); + confirmWidget = this._register(this.instantiationService.createInstance( + ChatCustomConfirmationWidget, + title, + this.markdownPart.domNode, + toolInput, + buttons + )); } + + this._register(confirmWidget.onDidClick(button => { + toolInvocation.confirmed.complete(button.data); + })); + this._register(confirmWidget.onDidChangeHeight(() => this._onDidChangeHeight.fire())); + toolInvocation.confirmed.p.then(() => { + this._onNeedsRerender.fire(); + }); + return confirmWidget.domNode; + } + + private createTerminalConfirmationWidget(toolInvocation: IChatToolInvocation, terminalData: IChatTerminalToolInvocationData): HTMLElement { + assertType(toolInvocation.confirmationMessages, 'Confirmation messages are missing'); + const title = toolInvocation.confirmationMessages.title; const message = toolInvocation.confirmationMessages.message; - const confirmWidget = this._register(instantiationService.createInstance( - ChatConfirmationWidget, + const continueLabel = localize('continue', "Continue"); + const continueKeybinding = this.keybindingService.lookupKeybinding(AcceptToolConfirmationActionId)?.getLabel(); + const continueTooltip = continueKeybinding ? `${continueLabel} (${continueKeybinding})` : continueLabel; + const cancelLabel = localize('cancel', "Cancel"); + const cancelKeybinding = this.keybindingService.lookupKeybinding(CancelChatActionId)?.getLabel(); + const cancelTooltip = cancelKeybinding ? `${cancelLabel} (${cancelKeybinding})` : cancelLabel; + + const buttons: IChatConfirmationButton[] = [ + { + label: continueLabel, + data: true, + tooltip: continueTooltip + }, + { + label: cancelLabel, + data: false, + isSecondary: true, + tooltip: cancelTooltip + }]; + const renderedMessage = this._register(this.renderer.render( + typeof message === 'string' ? new MarkdownString(message) : message, + { asyncRenderCallback: () => this._onDidChangeHeight.fire() } + )); + const codeBlockRenderOptions: ICodeBlockRenderOptions = { + hideToolbar: true, + reserveWidth: 19, + verticalPadding: 5, + editorOptions: { + wordWrap: 'on', + readOnly: false + } + }; + const langId = this.languageService.getLanguageIdByLanguageName(terminalData.language ?? 'sh') ?? 'shellscript'; + const model = this.modelService.createModel(terminalData.command, this.languageService.createById(langId)); + const editor = this._register(this.editorPool.get()); + editor.object.render({ + codeBlockIndex: this.codeBlockStartIndex, + codeBlockPartIndex: 0, + element: this.context.element, + languageId: langId, + renderOptions: codeBlockRenderOptions, + textModel: Promise.resolve(model) + }, this.currentWidthDelegate()); + this._codeblocks.push({ + codeBlockIndex: this.codeBlockStartIndex, + codemapperUri: undefined, + elementId: this.context.element.id, + focus: () => editor.object.focus(), + isStreaming: false, + ownerMarkdownPartId: this.codeblocksPartId, + uri: model.uri, + uriPromise: Promise.resolve(model.uri) + }); + this._register(editor.object.onDidChangeContentHeight(() => { + editor.object.layout(this.currentWidthDelegate()); + this._onDidChangeHeight.fire(); + })); + this._register(model.onDidChangeContent(e => { + terminalData.command = model.getValue(); + })); + const element = dom.$(''); + dom.append(element, editor.object.element); + dom.append(element, renderedMessage.element); + const confirmWidget = this._register(this.instantiationService.createInstance( + ChatCustomConfirmationWidget, title, - message, - [{ label: localize('continue', "Continue"), data: true }, { label: localize('cancel', "Cancel"), data: false, isSecondary: true }] + element, + undefined, + buttons )); + + ChatContextKeys.Editing.hasToolConfirmation.bindTo(this.contextKeyService).set(true); this._register(confirmWidget.onDidClick(button => { toolInvocation.confirmed.complete(button.data); })); - this._onDidChangeHeight.input = confirmWidget.onDidChangeHeight; + this._register(confirmWidget.onDidChangeHeight(() => this._onDidChangeHeight.fire())); toolInvocation.confirmed.p.then(() => { + ChatContextKeys.Editing.hasToolConfirmation.bindTo(this.contextKeyService).set(false); this._onNeedsRerender.fire(); }); return confirmWidget.domNode; } - private createProgressPart( - toolInvocation: IChatToolInvocation | IChatToolInvocationSerialized, - context: IChatContentPartRenderContext, - renderer: MarkdownRenderer, - instantiationService: IInstantiationService, - hoverService: IHoverService - ): HTMLElement { + private createProgressPart(): HTMLElement { let content: IMarkdownString; - if (toolInvocation.isComplete && toolInvocation.isConfirmed !== false && toolInvocation.pastTenseMessage) { - content = typeof toolInvocation.pastTenseMessage === 'string' ? - new MarkdownString().appendText(toolInvocation.pastTenseMessage) : - toolInvocation.pastTenseMessage; + if (this.toolInvocation.isComplete && this.toolInvocation.isConfirmed !== false && this.toolInvocation.pastTenseMessage) { + content = typeof this.toolInvocation.pastTenseMessage === 'string' ? + new MarkdownString().appendText(this.toolInvocation.pastTenseMessage) : + this.toolInvocation.pastTenseMessage; } else { - content = typeof toolInvocation.invocationMessage === 'string' ? - new MarkdownString().appendText(toolInvocation.invocationMessage + '…') : - new MarkdownString(toolInvocation.invocationMessage.value + '…'); + content = typeof this.toolInvocation.invocationMessage === 'string' ? + new MarkdownString().appendText(this.toolInvocation.invocationMessage + '…') : + MarkdownString.lift(this.toolInvocation.invocationMessage).appendText('…'); } const progressMessage: IChatProgressMessage = { kind: 'progressMessage', content }; - const iconOverride = toolInvocation.isConfirmed === false ? + const iconOverride = !this.toolInvocation.isConfirmed ? Codicon.error : - toolInvocation.isComplete ? + this.toolInvocation.isComplete ? Codicon.check : undefined; - const progressPart = this._register(instantiationService.createInstance(ChatProgressContentPart, progressMessage, renderer, context, undefined, true, iconOverride)); - if (toolInvocation.tooltip) { - this._register(hoverService.setupDelayedHover(progressPart.domNode, { content: toolInvocation.tooltip, additionalClasses: ['chat-tool-hover'] })); - } + const progressPart = this._register(this.instantiationService.createInstance(ChatProgressContentPart, progressMessage, this.renderer, this.context, undefined, true, iconOverride)); + return progressPart.domNode; + } + + private createTerminalMarkdownProgressPart(toolInvocation: IChatToolInvocation | IChatToolInvocationSerialized, terminalData: IChatTerminalToolInvocationData): HTMLElement { + const content = new MarkdownString(`\`\`\`${terminalData.language}\n${terminalData.command}\n\`\`\``); + const chatMarkdownContent: IChatMarkdownContent = { + kind: 'markdownContent', + content: content as IMarkdownString, + }; + const codeBlockRenderOptions: ICodeBlockRenderOptions = { + hideToolbar: true, + reserveWidth: 19, + verticalPadding: 5, + editorOptions: { + wordWrap: 'on' + } + }; + this.markdownPart = this._register(this.instantiationService.createInstance(ChatMarkdownContentPart, chatMarkdownContent, this.context, this.editorPool, false, this.codeBlockStartIndex, this.renderer, this.currentWidthDelegate(), this.codeBlockModelCollection, { codeBlockRenderOptions })); + this._register(this.markdownPart.onDidChangeHeight(() => this._onDidChangeHeight.fire())); + const icon = !this.toolInvocation.isConfirmed ? + Codicon.error : + this.toolInvocation.isComplete ? + Codicon.check : ThemeIcon.modify(Codicon.loading, 'spin'); + const progressPart = this.instantiationService.createInstance(ChatCustomProgressPart, this.markdownPart.domNode, icon); return progressPart.domNode; } private createResultList( message: string | IMarkdownString, toolDetails: NonNullable, - context: IChatContentPartRenderContext, - instantiationService: IInstantiationService, - listPool: CollapsibleListPool ): HTMLElement { - const collapsibleListPart = this._register(instantiationService.createInstance( + const collapsibleListPart = this._register(this.instantiationService.createInstance( ChatCollapsibleListContentPart, toolDetails.map(detail => ({ kind: 'reference', reference: detail, })), message, - context, - listPool, + this.context, + this.listPool, )); - this._onDidChangeHeight.input = collapsibleListPart.onDidChangeHeight; + this._register(collapsibleListPart.onDidChangeHeight(() => this._onDidChangeHeight.fire())); return collapsibleListPart.domNode; } } diff --git a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatWarningContentPart.ts b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatWarningContentPart.ts index d3cc224782a1..236d3552e501 100644 --- a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatWarningContentPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatWarningContentPart.ts @@ -11,6 +11,7 @@ import { Disposable } from '../../../../../base/common/lifecycle.js'; import { MarkdownRenderer } from '../../../../../editor/browser/widget/markdownRenderer/browser/markdownRenderer.js'; import { IChatContentPart } from './chatContentParts.js'; import { IChatProgressRenderableResponseContent } from '../../common/chatModel.js'; +import { ChatErrorLevel } from '../../common/chatService.js'; const $ = dom.$; @@ -18,7 +19,7 @@ export class ChatWarningContentPart extends Disposable implements IChatContentPa public readonly domNode: HTMLElement; constructor( - kind: 'info' | 'warning' | 'error', + kind: ChatErrorLevel, content: IMarkdownString, renderer: MarkdownRenderer, ) { @@ -28,21 +29,21 @@ export class ChatWarningContentPart extends Disposable implements IChatContentPa let icon; let iconClass; switch (kind) { - case 'warning': + case ChatErrorLevel.Warning: icon = Codicon.warning; iconClass = '.chat-warning-codicon'; break; - case 'error': + case ChatErrorLevel.Error: icon = Codicon.error; iconClass = '.chat-error-codicon'; break; - case 'info': + case ChatErrorLevel.Info: icon = Codicon.info; iconClass = '.chat-info-codicon'; break; } this.domNode.appendChild($(iconClass, undefined, renderIcon(icon))); - const markdownContent = renderer.render(content); + const markdownContent = this._register(renderer.render(content)); this.domNode.appendChild(markdownContent.element); } diff --git a/src/vs/workbench/contrib/chat/browser/chatContentParts/media/chatConfirmationWidget.css b/src/vs/workbench/contrib/chat/browser/chatContentParts/media/chatConfirmationWidget.css index 4d711ebf7838..b8b7e1bab5bd 100644 --- a/src/vs/workbench/contrib/chat/browser/chatContentParts/media/chatConfirmationWidget.css +++ b/src/vs/workbench/contrib/chat/browser/chatContentParts/media/chatConfirmationWidget.css @@ -39,3 +39,37 @@ .chat-confirmation-widget.hideButtons .chat-confirmation-buttons-container { display: none; } + +.chat-confirmation-widget .chat-confirmation-widget-title-container.input { + cursor: pointer; + display: flex; + flex-wrap: wrap; +} + +.chat-confirmation-widget .chat-confirmation-widget-title-container.input .chat-confirmation-widget-title { + display: inline-flex; +} + +.chat-confirmation-widget .chat-confirmation-widget-title-container .chat-confirmation-widget-expando { + display: none; +} + +.chat-confirmation-widget .chat-confirmation-widget-title-container.input .chat-confirmation-widget-expando { + display: inline-flex; +} + +.chat-confirmation-widget .chat-confirmation-widget-title-container .chat-confirmation-widget-input-value { + display: none; + flex-basis: 100%; + width: 100%; +} + +.chat-confirmation-widget .chat-confirmation-widget-title-container .chat-confirmation-widget-input-value .rendered-markdown{ + width: 100%; +} + +.chat-confirmation-widget .chat-confirmation-widget-title-container.expanded .chat-confirmation-widget-input-value { + display: inherit; + overflow: scroll; + max-height: 400px; +} diff --git a/src/vs/workbench/contrib/chat/browser/chatDragAndDrop.ts b/src/vs/workbench/contrib/chat/browser/chatDragAndDrop.ts index 522a72577568..0de0ecc7d072 100644 --- a/src/vs/workbench/contrib/chat/browser/chatDragAndDrop.ts +++ b/src/vs/workbench/contrib/chat/browser/chatDragAndDrop.ts @@ -10,22 +10,23 @@ import { coalesce } from '../../../../base/common/arrays.js'; import { Codicon } from '../../../../base/common/codicons.js'; import { IDisposable } from '../../../../base/common/lifecycle.js'; import { Mimes } from '../../../../base/common/mime.js'; -import { basename, joinPath } from '../../../../base/common/resources.js'; +import { basename } from '../../../../base/common/resources.js'; import { URI } from '../../../../base/common/uri.js'; import { IRange } from '../../../../editor/common/core/range.js'; import { SymbolKinds } from '../../../../editor/common/languages.js'; import { ITextModelService } from '../../../../editor/common/services/resolverService.js'; import { localize } from '../../../../nls.js'; import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js'; -import { CodeDataTransfers, containsDragType, DocumentSymbolTransferData, extractEditorsDropData, extractSymbolDropData, IDraggedResourceEditorInput } from '../../../../platform/dnd/browser/dnd.js'; -import { FileType, IFileService, IFileSystemProvider } from '../../../../platform/files/common/files.js'; +import { CodeDataTransfers, containsDragType, DocumentSymbolTransferData, extractEditorsDropData, extractMarkerDropData, extractSymbolDropData, IDraggedResourceEditorInput, MarkerTransferData } from '../../../../platform/dnd/browser/dnd.js'; +import { IFileService } from '../../../../platform/files/common/files.js'; +import { MarkerSeverity } from '../../../../platform/markers/common/markers.js'; import { IThemeService, Themable } from '../../../../platform/theme/common/themeService.js'; import { isUntitledResourceEditorInput } from '../../../common/editor.js'; import { EditorInput } from '../../../common/editor/editorInput.js'; import { IEditorService } from '../../../services/editor/common/editorService.js'; import { IExtensionService, isProposedApiEnabled } from '../../../services/extensions/common/extensions.js'; import { UntitledTextEditorInput } from '../../../services/untitled/common/untitledTextEditorInput.js'; -import { IChatRequestVariableEntry, ISymbolVariableEntry } from '../common/chatModel.js'; +import { IChatRequestVariableEntry, IDiagnosticVariableEntry, IDiagnosticVariableEntryFilterData, ISymbolVariableEntry } from '../common/chatModel.js'; import { ChatAttachmentModel } from './chatAttachmentModel.js'; import { IChatInputStyles } from './chatInputPart.js'; import { resizeImage } from './imageUtils.js'; @@ -35,7 +36,9 @@ enum ChatDragAndDropType { FILE_EXTERNAL, FOLDER, IMAGE, - SYMBOL + SYMBOL, + HTML, + MARKER, } export class ChatDragAndDrop extends Themable { @@ -45,14 +48,14 @@ export class ChatDragAndDrop extends Themable { private overlayTextBackground: string = ''; constructor( - protected readonly attachmentModel: ChatAttachmentModel, + private readonly attachmentModel: ChatAttachmentModel, private readonly styles: IChatInputStyles, @IThemeService themeService: IThemeService, @IExtensionService private readonly extensionService: IExtensionService, - @IFileService protected readonly fileService: IFileService, - @IEditorService protected readonly editorService: IEditorService, - @IDialogService protected readonly dialogService: IDialogService, - @ITextModelService protected readonly textModelService: ITextModelService + @IFileService private readonly fileService: IFileService, + @IEditorService private readonly editorService: IEditorService, + @IDialogService private readonly dialogService: IDialogService, + @ITextModelService private readonly textModelService: ITextModelService ) { super(themeService); @@ -147,10 +150,6 @@ export class ChatDragAndDrop extends Themable { return; } - this.handleDrop(contexts); - } - - protected handleDrop(contexts: IChatRequestVariableEntry[]): void { this.attachmentModel.addContext(...contexts); } @@ -167,13 +166,17 @@ export class ChatDragAndDrop extends Themable { // This is an esstimation based on the datatransfer types/items if (this.isImageDnd(e)) { return this.extensionService.extensions.some(ext => isProposedApiEnabled(ext, 'chatReferenceBinaryData')) ? ChatDragAndDropType.IMAGE : undefined; + // } else if (containsDragType(e, 'text/html')) { + // return ChatDragAndDropType.HTML; } else if (containsDragType(e, CodeDataTransfers.SYMBOLS)) { return ChatDragAndDropType.SYMBOL; + } else if (containsDragType(e, CodeDataTransfers.MARKERS)) { + return ChatDragAndDropType.MARKER; } else if (containsDragType(e, DataTransfers.FILES)) { return ChatDragAndDropType.FILE_EXTERNAL; } else if (containsDragType(e, DataTransfers.INTERNAL_URI_LIST)) { return ChatDragAndDropType.FILE_INTERNAL; - } else if (containsDragType(e, Mimes.uriList, CodeDataTransfers.FILES)) { + } else if (containsDragType(e, Mimes.uriList, CodeDataTransfers.FILES, DataTransfers.RESOURCES)) { return ChatDragAndDropType.FOLDER; } @@ -186,13 +189,15 @@ export class ChatDragAndDrop extends Themable { return dropType !== undefined; } - protected getDropTypeName(type: ChatDragAndDropType): string { + private getDropTypeName(type: ChatDragAndDropType): string { switch (type) { case ChatDragAndDropType.FILE_INTERNAL: return localize('file', 'File'); case ChatDragAndDropType.FILE_EXTERNAL: return localize('file', 'File'); case ChatDragAndDropType.FOLDER: return localize('folder', 'Folder'); case ChatDragAndDropType.IMAGE: return localize('image', 'Image'); case ChatDragAndDropType.SYMBOL: return localize('symbol', 'Symbol'); + case ChatDragAndDropType.MARKER: return localize('problem', 'Problem'); + case ChatDragAndDropType.HTML: return localize('url', 'URL'); } } @@ -224,11 +229,22 @@ export class ChatDragAndDrop extends Themable { return []; } + const markerData = extractMarkerDropData(e); + if (markerData) { + return this.resolveMarkerAttachContext(markerData); + } + if (containsDragType(e, CodeDataTransfers.SYMBOLS)) { const data = extractSymbolDropData(e); return this.resolveSymbolsAttachContext(data); } + // Removing HTML support for now + // if (containsDragType(e, 'text/html')) { + // const data = e.dataTransfer?.getData('text/html'); + // return data ? this.resolveHTMLAttachContext(data) : []; + // } + const data = extractEditorsDropData(e); return coalesce(await Promise.all(data.map(editorInput => { return this.resolveAttachContext(editorInput); @@ -304,6 +320,69 @@ export class ChatDragAndDrop extends Themable { }); } + // private async resolveHTMLAttachContext(data: string): Promise { + // const displayName = localize('dragAndDroppedImageName', 'Image from URL'); + // let finalDisplayName = displayName; + + // for (let appendValue = 2; this.attachmentModel.attachments.some(attachment => attachment.name === finalDisplayName); appendValue++) { + // finalDisplayName = `${displayName} ${appendValue}`; + // } + + // const { src, alt } = extractImageAttributes(data); + // finalDisplayName = alt ?? finalDisplayName; + + // if (/^data:image\/[a-z]+;base64,/.test(src)) { + // const resizedImage = await resizeImage(src); + // return [{ + // id: await imageToHash(resizedImage), + // name: finalDisplayName, + // value: resizedImage, + // isImage: true, + // isFile: false, + // isDirectory: false + // }]; + // } else if (/^https?:\/\/.+/.test(src)) { + // const url = new URL(src); + // const isImage = /\.(jpg|jpeg|png|gif|webp)$/i.test(url.pathname); + // if (isImage) { + // const buffer = convertStringToUInt8Array(src); + // return [{ + // kind: 'image', + // id: url.toString(), + // name: finalDisplayName, + // value: buffer, + // isImage, + // isFile: false, + // isDirectory: false, + // isURL: true, + // }]; + // } else { + // return [{ + // kind: 'link', + // id: url.toString(), + // name: finalDisplayName, + // value: URI.parse(url.toString()), + // isFile: false, + // isDirectory: false, + // }]; + // } + // } + // return []; + // } + + private resolveMarkerAttachContext(markers: MarkerTransferData[]): IDiagnosticVariableEntry[] { + return markers.map((marker): IDiagnosticVariableEntry => { + let filter: IDiagnosticVariableEntryFilterData; + if (!('severity' in marker)) { + filter = { filterUri: URI.revive(marker.uri), filterSeverity: MarkerSeverity.Warning }; + } else { + filter = IDiagnosticVariableEntryFilterData.fromMarker(marker); + } + + return IDiagnosticVariableEntryFilterData.toEntry(filter); + }); + } + private setOverlay(target: HTMLElement, type: ChatDragAndDropType | undefined): void { // Remove any previous overlay text this.overlayText?.remove(); @@ -329,7 +408,7 @@ export class ChatDragAndDrop extends Themable { overlay.classList.toggle('visible', type !== undefined); } - protected getOverlayText(type: ChatDragAndDropType): string { + private getOverlayText(type: ChatDragAndDropType): string { const typeName = this.getDropTypeName(type); return localize('attacAsContext', 'Attach {0} as Context', typeName); } @@ -345,90 +424,22 @@ export class ChatDragAndDrop extends Themable { } } -export class EditsDragAndDrop extends ChatDragAndDrop { - - constructor( - attachmentModel: ChatAttachmentModel, - styles: IChatInputStyles, - @IThemeService themeService: IThemeService, - @IExtensionService extensionService: IExtensionService, - @IFileService fileService: IFileService, - @IEditorService editorService: IEditorService, - @IDialogService dialogService: IDialogService, - @ITextModelService textModelService: ITextModelService - ) { - super(attachmentModel, styles, themeService, extensionService, fileService, editorService, dialogService, textModelService); - } - - protected override handleDrop(context: IChatRequestVariableEntry[]): void { - this.handleDropAsync(context); - } - - protected async handleDropAsync(context: IChatRequestVariableEntry[]): Promise { - const nonDirectoryContext = context.filter(context => !context.isDirectory); - const directories = context - .filter(context => context.isDirectory) - .map(context => context.value) - .filter(value => !!value && URI.isUri(value)); - - // If there are directories, we need to resolve the files and add them to the working set - for (const directory of directories) { - const fileSystemProvider = this.fileService.getProvider(directory.scheme); - if (!fileSystemProvider) { - continue; - } - - const resolvedFiles = await resolveFilesInDirectory(directory, fileSystemProvider, true); - const resolvedFileContext = await Promise.all(resolvedFiles.map(file => getResourceAttachContext(file, false, this.textModelService))); - nonDirectoryContext.push(...resolvedFileContext.filter(context => !!context)); - } - - super.handleDrop(nonDirectoryContext); - } +async function getResourceAttachContext(resource: URI, isDirectory: boolean, textModelService: ITextModelService): Promise { + let isOmitted = false; - protected override getOverlayText(type: ChatDragAndDropType): string { - const typeName = this.getDropTypeName(type); - switch (type) { - case ChatDragAndDropType.FILE_INTERNAL: - case ChatDragAndDropType.FILE_EXTERNAL: - return localize('addToWorkingSet', 'Add {0} to Working Set', typeName); - case ChatDragAndDropType.FOLDER: - return localize('addToWorkingSet', 'Add {0} to Working Set', localize('files', 'Files')); - default: - return super.getOverlayText(type); + if (!isDirectory) { + try { + const createdModel = await textModelService.createModelReference(resource); + createdModel.dispose(); + } catch { + isOmitted = true; } - } -} - -async function resolveFilesInDirectory(resource: URI, fileSystemProvider: IFileSystemProvider, shouldRecurse: boolean): Promise { - const entries = await fileSystemProvider.readdir(resource); - - const files: URI[] = []; - const folders: URI[] = []; - for (const [name, type] of entries) { - const entryResource = joinPath(resource, name); - if (type === FileType.File) { - files.push(entryResource); - } else if (type === FileType.Directory && shouldRecurse) { - folders.push(entryResource); + if (/\.(svg)$/i.test(resource.path)) { + isOmitted = true; } } - const subFiles = await Promise.all(folders.map(folder => resolveFilesInDirectory(folder, fileSystemProvider, shouldRecurse))); - - return [...files, ...subFiles.flat()]; -} - -async function getResourceAttachContext(resource: URI, isDirectory: boolean, textModelService: ITextModelService): Promise { - let isOmitted = false; - try { - const createdModel = await textModelService.createModelReference(resource); - createdModel.dispose(); - } catch { - isOmitted = true; - } - return { value: resource, id: resource.toString(), @@ -477,3 +488,17 @@ function symbolId(resource: URI, range?: IRange): string { } return resource.fsPath + rangePart; } + +// function extractImageAttributes(html: string): { src: string; alt?: string } { +// const imgTagRegex = /]+src=["']([^"']+)["'][^>]*>/; +// const altRegex = /alt=["']([^"']+)["']/; + +// const match = imgTagRegex.exec(html); +// if (match) { +// const src = match[1]; +// const altMatch = match[0].match(altRegex); +// return { src, alt: altMatch ? altMatch[1] : undefined }; +// } + +// return { src: '', alt: undefined }; +// } diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingActions.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingActions.ts index 12f2ec5f187a..e38245f82b86 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingActions.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingActions.ts @@ -26,18 +26,15 @@ import { KeybindingWeight } from '../../../../../platform/keybinding/common/keyb import { IListService } from '../../../../../platform/list/browser/listService.js'; import { GroupsOrder, IEditorGroupsService } from '../../../../services/editor/common/editorGroupsService.js'; import { IEditorService } from '../../../../services/editor/common/editorService.js'; -import { ChatAgentLocation } from '../../common/chatAgents.js'; +import { isChatViewTitleActionContext } from '../../common/chatActions.js'; import { ChatContextKeys } from '../../common/chatContextKeys.js'; -import { applyingChatEditsFailedContextKey, CHAT_EDITING_MULTI_DIFF_SOURCE_RESOLVER_SCHEME, chatEditingAgentSupportsReadonlyReferencesContextKey, chatEditingResourceContextKey, chatEditingWidgetFileReadonlyContextKey, chatEditingWidgetFileStateContextKey, decidedChatEditingResourceContextKey, hasAppliedChatEditsContextKey, hasUndecidedChatEditingResourceContextKey, IChatEditingService, IChatEditingSession, WorkingSetEntryRemovalReason, WorkingSetEntryState } from '../../common/chatEditingService.js'; +import { applyingChatEditsFailedContextKey, CHAT_EDITING_MULTI_DIFF_SOURCE_RESOLVER_SCHEME, chatEditingResourceContextKey, chatEditingWidgetFileStateContextKey, decidedChatEditingResourceContextKey, hasAppliedChatEditsContextKey, hasUndecidedChatEditingResourceContextKey, IChatEditingService, IChatEditingSession, WorkingSetEntryRemovalReason, WorkingSetEntryState } from '../../common/chatEditingService.js'; import { IChatService } from '../../common/chatService.js'; import { isRequestVM, isResponseVM } from '../../common/chatViewModel.js'; +import { ChatAgentLocation, ChatMode } from '../../common/constants.js'; import { CHAT_CATEGORY } from '../actions/chatActions.js'; import { ChatTreeItem, IChatWidget, IChatWidgetService } from '../chat.js'; -export interface IEditingSessionActionContext { - widget?: IChatWidget; -} - export abstract class EditingSessionAction extends Action2 { constructor(opts: Readonly) { @@ -48,26 +45,46 @@ export abstract class EditingSessionAction extends Action2 { } run(accessor: ServicesAccessor, ...args: any[]) { - const context: IEditingSessionActionContext | undefined = args[0]; - - const chatEditingService = accessor.get(IChatEditingService); - const chatWidget = context?.widget ?? accessor.get(IChatWidgetService).getWidgetsByLocations(ChatAgentLocation.EditingSession).at(0); - - if (!chatWidget?.viewModel) { + const context = getEditingSessionContext(accessor, args); + if (!context || !context.editingSession) { return; } - const chatSessionId = chatWidget.viewModel.model.sessionId; - const editingSession = chatEditingService.getEditingSession(chatSessionId); + return this.runEditingSessionAction(accessor, context.editingSession, context.chatWidget, ...args); + } + + abstract runEditingSessionAction(accessor: ServicesAccessor, editingSession: IChatEditingSession, chatWidget: IChatWidget, ...args: any[]): any; +} - if (!editingSession) { - return; +export function getEditingSessionContext(accessor: ServicesAccessor, args: any[]): { editingSession?: IChatEditingSession; chatWidget: IChatWidget } | undefined { + const arg0 = args.at(0); + const context = isChatViewTitleActionContext(arg0) ? arg0 : undefined; + + const chatService = accessor.get(IChatService); + const chatWidgetService = accessor.get(IChatWidgetService); + const chatEditingService = accessor.get(IChatEditingService); + let chatWidget = context ? chatWidgetService.getWidgetBySessionId(context.sessionId) : undefined; + if (!chatWidget) { + if (chatService.unifiedViewEnabled) { + // TODO ugly + chatWidget = chatWidgetService.getWidgetsByLocations(ChatAgentLocation.Panel).find(w => w.isUnifiedPanelWidget); + } else { + chatWidget = chatWidgetService.getWidgetsByLocations(ChatAgentLocation.EditingSession).at(0); } + } - return this.runEditingSessionAction(accessor, editingSession, chatWidget, ...args); + if (!chatWidget?.viewModel) { + return; } - abstract runEditingSessionAction(accessor: ServicesAccessor, editingSession: IChatEditingSession, chatWidget: IChatWidget, ...args: any[]): any; + const chatSessionId = chatWidget.viewModel.model.sessionId; + const editingSession = chatEditingService.getEditingSession(chatSessionId); + + if (!editingSession) { + return; + } + + return { editingSession, chatWidget }; } @@ -91,63 +108,13 @@ abstract class WorkingSetAction extends EditingSessionAction { abstract runWorkingSetAction(accessor: ServicesAccessor, editingSession: IChatEditingSession, chatWidget: IChatWidget | undefined, ...uris: URI[]): any; } -registerAction2(class MarkFileAsReadonly extends WorkingSetAction { - constructor() { - super({ - id: 'chatEditing.markFileAsReadonly', - title: localize2('markFileAsReadonly', 'Mark as read-only'), - icon: Codicon.lock, - toggled: chatEditingWidgetFileReadonlyContextKey, - menu: [{ - id: MenuId.ChatEditingWidgetModifiedFilesToolbar, - when: ContextKeyExpr.and( - chatEditingAgentSupportsReadonlyReferencesContextKey, - ContextKeyExpr.or( - ContextKeyExpr.equals(chatEditingWidgetFileReadonlyContextKey.key, true), - ContextKeyExpr.equals(chatEditingWidgetFileReadonlyContextKey.key, false), - ) - ), - order: 10, - group: 'navigation' - }], - }); - } - - async runWorkingSetAction(_accessor: ServicesAccessor, currentEditingSession: IChatEditingSession, _chatWidget: IChatWidget, ...uris: URI[]): Promise { - for (const uri of uris) { - currentEditingSession.markIsReadonly(uri); - } - } -}); - -registerAction2(class AddFileToWorkingSet extends WorkingSetAction { - constructor() { - super({ - id: 'chatEditing.addFileToWorkingSet', - title: localize2('addFileToWorkingSet', 'Add File'), - icon: Codicon.plus, - menu: [{ - id: MenuId.ChatEditingWidgetModifiedFilesToolbar, - when: ContextKeyExpr.equals(chatEditingWidgetFileStateContextKey.key, WorkingSetEntryState.Suggested), - order: 0, - group: 'navigation' - }], - }); - } - - async runWorkingSetAction(_accessor: ServicesAccessor, currentEditingSession: IChatEditingSession, _chatWidget: IChatWidget, ...uris: URI[]): Promise { - for (const uri of uris) { - currentEditingSession.addFileToWorkingSet(uri); - } - } -}); - registerAction2(class RemoveFileFromWorkingSet extends WorkingSetAction { constructor() { super({ id: 'chatEditing.removeFileFromWorkingSet', title: localize2('removeFileFromWorkingSet', 'Remove File'), icon: Codicon.close, + precondition: ChatContextKeys.requestInProgress.negate(), menu: [{ id: MenuId.ChatEditingWidgetModifiedFilesToolbar, // when: ContextKeyExpr.or(ContextKeyExpr.equals(chatEditingWidgetFileStateContextKey.key, WorkingSetEntryState.Attached), ContextKeyExpr.equals(chatEditingWidgetFileStateContextKey.key, WorkingSetEntryState.Suggested), ContextKeyExpr.equals(chatEditingWidgetFileStateContextKey.key, WorkingSetEntryState.Transient)), @@ -186,11 +153,9 @@ registerAction2(class RemoveFileFromWorkingSet extends WorkingSetAction { chatWidget.attachmentModel.delete(uri.toString()); } - // If there are now only suggested files in the working set, also clear those - const entries = [...currentEditingSession.workingSet.entries()]; - const suggestedFiles = entries.filter(([_, state]) => state.state === WorkingSetEntryState.Suggested); - if (suggestedFiles.length === entries.length && !chatWidget.attachmentModel.attachments.find((v) => v.isFile && URI.isUri(v.value))) { - currentEditingSession.remove(WorkingSetEntryRemovalReason.Programmatic, ...entries.map(([uri,]) => uri)); + // Clear all related file suggestions + if (chatWidget.attachmentModel.fileAttachments.length === 0) { + chatWidget.input.relatedFiles?.clear(); } } }); @@ -232,6 +197,7 @@ registerAction2(class AcceptAction extends WorkingSetAction { id: 'chatEditing.acceptFile', title: localize2('accept.file', 'Keep'), icon: Codicon.check, + precondition: ChatContextKeys.requestInProgress.negate(), menu: [{ when: ContextKeyExpr.and(ContextKeyExpr.equals('resourceScheme', CHAT_EDITING_MULTI_DIFF_SOURCE_RESOLVER_SCHEME), ContextKeyExpr.notIn(chatEditingResourceContextKey.key, decidedChatEditingResourceContextKey.key)), id: MenuId.MultiDiffEditorFileToolbar, @@ -257,6 +223,7 @@ registerAction2(class DiscardAction extends WorkingSetAction { id: 'chatEditing.discardFile', title: localize2('discard.file', 'Undo'), icon: Codicon.discard, + precondition: ChatContextKeys.requestInProgress.negate(), menu: [{ when: ContextKeyExpr.and(ContextKeyExpr.equals('resourceScheme', CHAT_EDITING_MULTI_DIFF_SOURCE_RESOLVER_SCHEME), ContextKeyExpr.notIn(chatEditingResourceContextKey.key, decidedChatEditingResourceContextKey.key)), id: MenuId.MultiDiffEditorFileToolbar, @@ -287,7 +254,7 @@ export class ChatEditingAcceptAllAction extends EditingSessionAction { precondition: ContextKeyExpr.and(ChatContextKeys.requestInProgress.negate(), hasUndecidedChatEditingResourceContextKey), keybinding: { primary: KeyMod.CtrlCmd | KeyCode.Enter, - when: ContextKeyExpr.and(ChatContextKeys.requestInProgress.negate(), hasUndecidedChatEditingResourceContextKey, ChatContextKeys.location.isEqualTo(ChatAgentLocation.EditingSession), ChatContextKeys.inChatInput), + when: ContextKeyExpr.and(ChatContextKeys.requestInProgress.negate(), hasUndecidedChatEditingResourceContextKey, ChatContextKeys.inChatInput), weight: KeybindingWeight.WorkbenchContrib, }, menu: [ @@ -296,7 +263,7 @@ export class ChatEditingAcceptAllAction extends EditingSessionAction { id: MenuId.ChatEditingWidgetToolbar, group: 'navigation', order: 0, - when: ContextKeyExpr.and(applyingChatEditsFailedContextKey.negate(), ContextKeyExpr.and(hasUndecidedChatEditingResourceContextKey, ContextKeyExpr.and(ChatContextKeys.location.isEqualTo(ChatAgentLocation.EditingSession)))) + when: ContextKeyExpr.and(applyingChatEditsFailedContextKey.negate(), ContextKeyExpr.and(hasUndecidedChatEditingResourceContextKey)) } ] }); @@ -322,11 +289,11 @@ export class ChatEditingDiscardAllAction extends EditingSessionAction { id: MenuId.ChatEditingWidgetToolbar, group: 'navigation', order: 1, - when: ContextKeyExpr.and(applyingChatEditsFailedContextKey.negate(), ContextKeyExpr.and(ChatContextKeys.location.isEqualTo(ChatAgentLocation.EditingSession), hasUndecidedChatEditingResourceContextKey)) + when: ContextKeyExpr.and(applyingChatEditsFailedContextKey.negate(), hasUndecidedChatEditingResourceContextKey) } ], keybinding: { - when: ContextKeyExpr.and(ChatContextKeys.requestInProgress.negate(), hasUndecidedChatEditingResourceContextKey, ChatContextKeys.location.isEqualTo(ChatAgentLocation.EditingSession), ChatContextKeys.inChatInput, ChatContextKeys.inputHasText.negate()), + when: ContextKeyExpr.and(ChatContextKeys.requestInProgress.negate(), hasUndecidedChatEditingResourceContextKey, ChatContextKeys.inChatInput, ChatContextKeys.inputHasText.negate()), weight: KeybindingWeight.WorkbenchContrib, primary: KeyMod.CtrlCmd | KeyCode.Backspace, }, @@ -378,7 +345,7 @@ export class ChatEditingRemoveAllFilesAction extends EditingSessionAction { id: MenuId.ChatEditingWidgetToolbar, group: 'navigation', order: 5, - when: ContextKeyExpr.and(hasAppliedChatEditsContextKey.negate(), ChatContextKeys.location.isEqualTo(ChatAgentLocation.EditingSession)) + when: hasAppliedChatEditsContextKey.negate() } ] }); @@ -386,7 +353,7 @@ export class ChatEditingRemoveAllFilesAction extends EditingSessionAction { override async runEditingSessionAction(accessor: ServicesAccessor, editingSession: IChatEditingSession, chatWidget: IChatWidget, ...args: any[]): Promise { // Remove all files from working set - const uris = [...editingSession.workingSet.keys()]; + const uris = [...editingSession.entries.get()].map((e) => e.modifiedURI); editingSession.remove(WorkingSetEntryRemovalReason.User, ...uris); // Remove all file attachments @@ -414,7 +381,7 @@ export class ChatEditingShowChangesAction extends EditingSessionAction { id: MenuId.ChatEditingWidgetToolbar, group: 'navigation', order: 4, - when: ContextKeyExpr.and(applyingChatEditsFailedContextKey.negate(), ContextKeyExpr.and(hasAppliedChatEditsContextKey, hasUndecidedChatEditingResourceContextKey, ChatContextKeys.location.isEqualTo(ChatAgentLocation.EditingSession))) + when: ContextKeyExpr.and(applyingChatEditsFailedContextKey.negate(), ContextKeyExpr.and(hasAppliedChatEditsContextKey, hasUndecidedChatEditingResourceContextKey)) } ], }); @@ -463,7 +430,7 @@ registerAction2(class AddFilesToWorkingSetAction extends EditingSessionAction { } for (const file of uris) { - editingSession.addFileToWorkingSet(file); + chatWidget.attachmentModel.addFile(file); } } }); @@ -481,7 +448,7 @@ registerAction2(class RemoveAction extends Action2 { mac: { primary: KeyMod.CtrlCmd | KeyCode.Backspace, }, - when: ContextKeyExpr.and(ChatContextKeys.location.isEqualTo(ChatAgentLocation.EditingSession), ChatContextKeys.inChatSession, ChatContextKeys.inChatInput.negate()), + when: ContextKeyExpr.and(ChatContextKeys.chatMode.notEqualsTo(ChatMode.Chat), ChatContextKeys.inChatSession, ChatContextKeys.inChatInput.negate()), weight: KeybindingWeight.WorkbenchContrib, }, menu: [ @@ -489,7 +456,7 @@ registerAction2(class RemoveAction extends Action2 { id: MenuId.ChatMessageTitle, group: 'navigation', order: 2, - when: ContextKeyExpr.and(ChatContextKeys.location.isEqualTo(ChatAgentLocation.EditingSession), ChatContextKeys.isRequest) + when: ContextKeyExpr.and(ChatContextKeys.chatMode.notEqualsTo(ChatMode.Chat), ChatContextKeys.isRequest) } ] }); @@ -572,11 +539,6 @@ registerAction2(class RemoveAction extends Action2 { // Restore the snapshot to what it was before the request(s) that we deleted const snapshotRequestId = chatRequests[itemIndex].id; await session.restoreSnapshot(snapshotRequestId, undefined); - - // Remove the request and all that come after it - for (const request of requestsToRemove) { - await chatService.removeRequest(item.sessionId, request.id); - } } } }); @@ -658,7 +620,7 @@ registerAction2(class ResolveSymbolsContextAction extends EditingSessionAction { id: MenuId.ChatInputSymbolAttachmentContext, group: 'navigation', order: 1, - when: ContextKeyExpr.and(ChatContextKeys.location.isEqualTo(ChatAgentLocation.EditingSession), EditorContextKeys.hasReferenceProvider) + when: ContextKeyExpr.and(ChatContextKeys.chatMode.isEqualTo(ChatMode.Chat), EditorContextKeys.hasReferenceProvider) } }); } diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingCodeEditorIntegration.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingCodeEditorIntegration.ts index 19f72730a4ac..ba68a8321b5c 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingCodeEditorIntegration.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingCodeEditorIntegration.ts @@ -35,9 +35,11 @@ import { IInstantiationService } from '../../../../../platform/instantiation/com import { EditorsOrder, IEditorIdentifier, isDiffEditorInput } from '../../../../common/editor.js'; import { IEditorService } from '../../../../services/editor/common/editorService.js'; import { overviewRulerModifiedForeground, minimapGutterModifiedBackground, overviewRulerAddedForeground, minimapGutterAddedBackground, overviewRulerDeletedForeground, minimapGutterDeletedBackground } from '../../../scm/common/quickDiff.js'; -import { ChatAgentLocation, IChatAgentService } from '../../common/chatAgents.js'; -import { ChatEditingSessionState, IChatEditingService, IModifiedFileEntry, IModifiedFileEntryChangeHunk, IModifiedFileEntryEditorIntegration, WorkingSetEntryState } from '../../common/chatEditingService.js'; +import { IChatAgentService } from '../../common/chatAgents.js'; +import { IChatEditingService, IModifiedFileEntry, IModifiedFileEntryChangeHunk, IModifiedFileEntryEditorIntegration, WorkingSetEntryState } from '../../common/chatEditingService.js'; import { isTextDiffEditorForEntry } from './chatEditing.js'; +import { IEditorDecorationsCollection } from '../../../../../editor/common/editorCommon.js'; +import { ChatAgentLocation } from '../../common/constants.js'; export interface IDocumentDiff2 extends IDocumentDiff { @@ -56,9 +58,8 @@ export class ChatEditingCodeEditorIntegration implements IModifiedFileEntryEdito readonly currentIndex: IObservable = this._currentIndex; private readonly _store = new DisposableStore(); - private readonly _diffLineDecorations = this._editor.createDecorationsCollection(); // tracks the line range w/o visuals (used for navigate) - - private readonly _diffVisualDecorations = this._editor.createDecorationsCollection(); // tracks the real diff with character level inserts + private readonly _diffLineDecorations: IEditorDecorationsCollection; + private readonly _diffVisualDecorations: IEditorDecorationsCollection; private readonly _diffHunksRenderStore = this._store.add(new DisposableStore()); private readonly _diffHunkWidgets: DiffHunkWidget[] = []; private _viewZones: string[] = []; @@ -78,6 +79,9 @@ export class ChatEditingCodeEditorIntegration implements IModifiedFileEntryEdito this._diffLineDecorations = _editor.createDecorationsCollection(); const codeEditorObs = observableCodeEditor(_editor); + this._diffLineDecorations = this._editor.createDecorationsCollection(); // tracks the line range w/o visuals (used for navigate) + this._diffVisualDecorations = this._editor.createDecorationsCollection(); // tracks the real diff with character level inserts + const enabledObs = derived(r => { if (!isEqual(codeEditorObs.model.read(r)?.uri, documentDiffInfo.read(r).modifiedModel.uri)) { return false; @@ -210,17 +214,18 @@ export class ChatEditingCodeEditorIntegration implements IModifiedFileEntryEdito // ---- readonly while streaming let actualOptions: IEditorOptions | undefined; - const shouldBeReadOnly = derived(this, r => { - const model = codeEditorObs.model.read(r); - if (!model) { - return false; - } - for (const session of chatEditingService.editingSessionsObs.read(r)) { - if (session.readEntry(model.uri, r) && session.state.read(r) === ChatEditingSessionState.StreamingEdits) { - return true; - } + + const restoreActualOptions = () => { + if (actualOptions !== undefined) { + this._editor.updateOptions(actualOptions); + actualOptions = undefined; } - return false; + }; + + this._store.add(toDisposable(restoreActualOptions)); + + const shouldBeReadOnly = derived(this, r => { + return enabledObs.read(r) && Boolean(_entry.isCurrentlyBeingModifiedBy.read(r)); }); this._store.add(autorun(r => { @@ -229,20 +234,15 @@ export class ChatEditingCodeEditorIntegration implements IModifiedFileEntryEdito actualOptions ??= { readOnly: this._editor.getOption(EditorOption.readOnly), - renderValidationDecorations: this._editor.getOption(EditorOption.renderValidationDecorations), stickyScroll: this._editor.getOption(EditorOption.stickyScroll) }; this._editor.updateOptions({ readOnly: true, - renderValidationDecorations: 'off', stickyScroll: { enabled: false } }); } else { - if (actualOptions !== undefined) { - this._editor.updateOptions(actualOptions); - actualOptions = undefined; - } + restoreActualOptions(); } })); } @@ -254,7 +254,7 @@ export class ChatEditingCodeEditorIntegration implements IModifiedFileEntryEdito private _clear() { this._diffLineDecorations.clear(); - // this._currentChangeIndex.set(undefined, undefined); + this._clearDiffRendering(); this._currentIndex.set(-1, undefined); } @@ -415,8 +415,6 @@ export class ChatEditingCodeEditorIntegration implements IModifiedFileEntryEdito diffHunkDecoCollection.clear(); })); - - const positionObs = observableFromEvent(this._editor.onDidChangeCursorPosition, _ => this._editor.getPosition()); const activeWidgetIdx = derived(r => { diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingEditorActions.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingEditorActions.ts index a394490b3e86..29299765e873 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingEditorActions.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingEditorActions.ts @@ -14,8 +14,6 @@ import { ContextKeyExpr } from '../../../../../platform/contextkey/common/contex import { EditorContextKeys } from '../../../../../editor/common/editorContextKeys.js'; import { ACTIVE_GROUP, IEditorService } from '../../../../services/editor/common/editorService.js'; import { CHAT_EDITING_MULTI_DIFF_SOURCE_RESOLVER_SCHEME, IChatEditingService, IChatEditingSession, IModifiedFileEntry, IModifiedFileEntryEditorIntegration, WorkingSetEntryState } from '../../common/chatEditingService.js'; -import { ChatContextKeys } from '../../common/chatContextKeys.js'; -import { ctxNotebookHasEditorModification } from '../../../notebook/browser/contrib/chatEdit/notebookChatEditContext.js'; import { resolveCommandsContext } from '../../../../browser/parts/editor/editorCommandsContext.js'; import { IListService } from '../../../../../platform/list/browser/listService.js'; import { IEditorGroupsService } from '../../../../services/editor/common/editorGroupsService.js'; @@ -23,6 +21,7 @@ import { MultiDiffEditorInput } from '../../../multiDiffEditor/browser/multiDiff import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; import { ActiveEditorContext } from '../../../../common/contextkeys.js'; import { EditorResourceAccessor, SideBySideEditor, TEXT_DIFF_EDITOR_ID } from '../../../../common/editor.js'; +import { ChatContextKeys } from '../../common/chatContextKeys.js'; abstract class ChatEditingEditorAction extends Action2 { @@ -73,13 +72,14 @@ abstract class NavigateAction extends ChatEditingEditorAction { ? localize2('next', 'Go to Next Chat Edit') : localize2('prev', 'Go to Previous Chat Edit'), icon: next ? Codicon.arrowDown : Codicon.arrowUp, + precondition: ContextKeyExpr.and(ChatContextKeys.enabled, ctxHasRequestInProgress.negate()), keybinding: { primary: next ? KeyMod.Alt | KeyCode.F5 : KeyMod.Alt | KeyMod.Shift | KeyCode.F5, weight: KeybindingWeight.WorkbenchContrib, when: ContextKeyExpr.and( - ContextKeyExpr.or(ctxHasEditorModification, ctxNotebookHasEditorModification), + ctxHasEditorModification, EditorContextKeys.focus ), }, @@ -88,7 +88,7 @@ abstract class NavigateAction extends ChatEditingEditorAction { id: MenuId.ChatEditingEditorContent, group: 'navigate', order: !next ? 2 : 3, - when: ctxReviewModeEnabled + when: ContextKeyExpr.and(ctxReviewModeEnabled, ctxHasRequestInProgress.negate()) } }); } @@ -147,7 +147,11 @@ async function openNextOrPreviousChange(accessor: ServicesAccessor, session: ICh return false; } - newEntry.getEditorIntegration(pane).reveal(next); + if (session.entries.get().includes(newEntry)) { + // make sure newEntry is still valid! + newEntry.getEditorIntegration(pane).reveal(next); + } + return true; } @@ -165,7 +169,7 @@ abstract class AcceptDiscardAction extends ChatEditingEditorAction { tooltip: accept ? localize2('accept3', 'Keep Chat Edits in this File') : localize2('discard3', 'Undo Chat Edits in this File'), - precondition: ContextKeyExpr.and(ctxHasEditorModification), + precondition: ContextKeyExpr.and(ctxHasEditorModification, ctxHasRequestInProgress.negate()), icon: accept ? Codicon.check : Codicon.discard, @@ -181,7 +185,7 @@ abstract class AcceptDiscardAction extends ChatEditingEditorAction { id: MenuId.ChatEditingEditorContent, group: 'a_resolve', order: accept ? 0 : 1, - when: !accept ? ctxReviewModeEnabled : undefined + when: ContextKeyExpr.and(!accept ? ctxReviewModeEnabled : undefined, ctxHasRequestInProgress.negate()) } }); } @@ -262,7 +266,7 @@ class ToggleDiffAction extends ChatEditingEditorAction { condition: ContextKeyExpr.or(EditorContextKeys.inDiffEditor, ActiveEditorContext.isEqualTo(TEXT_DIFF_EDITOR_ID))!, icon: Codicon.goToFile, }, - precondition: ContextKeyExpr.and(ctxHasEditorModification, ChatContextKeys.requestInProgress.negate()), + precondition: ContextKeyExpr.and(ctxHasEditorModification, ctxHasRequestInProgress.negate()), icon: Codicon.diffSingle, keybinding: { when: EditorContextKeys.focus, @@ -276,7 +280,7 @@ class ToggleDiffAction extends ChatEditingEditorAction { id: MenuId.ChatEditingEditorContent, group: 'a_resolve', order: 2, - when: ctxReviewModeEnabled + when: ContextKeyExpr.and(ctxReviewModeEnabled, ctxHasRequestInProgress.negate()) }] }); } @@ -292,7 +296,7 @@ class ToggleAccessibleDiffViewAction extends ChatEditingEditorAction { id: 'chatEditor.action.showAccessibleDiffView', title: localize2('accessibleDiff', 'Show Accessible Diff View'), f1: true, - precondition: ContextKeyExpr.and(ctxHasEditorModification, ChatContextKeys.requestInProgress.negate()), + precondition: ContextKeyExpr.and(ctxHasEditorModification, ctxHasRequestInProgress.negate()), keybinding: { when: EditorContextKeys.focus, weight: KeybindingWeight.WorkbenchContrib, @@ -312,11 +316,12 @@ export class ReviewChangesAction extends ChatEditingEditorAction { super({ id: 'chatEditor.action.reviewChanges', title: localize2('review', "Review"), + precondition: ContextKeyExpr.and(ctxHasEditorModification, ctxHasRequestInProgress.negate()), menu: [{ id: MenuId.ChatEditingEditorContent, group: 'a_resolve', order: 3, - when: ctxReviewModeEnabled.negate(), + when: ContextKeyExpr.and(ctxReviewModeEnabled.negate(), ctxHasRequestInProgress.negate()), }] }); } @@ -395,7 +400,7 @@ export function registerChatEditorActions() { }, group: 'navigate', order: -1, - when: ctxReviewModeEnabled, + when: ContextKeyExpr.and(ctxReviewModeEnabled, ctxHasRequestInProgress.negate()), }); } diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingEditorContextKeys.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingEditorContextKeys.ts index ee536a777996..d86957b3ca83 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingEditorContextKeys.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingEditorContextKeys.ts @@ -4,13 +4,16 @@ *--------------------------------------------------------------------------------------------*/ import { Event } from '../../../../../base/common/event.js'; import { DisposableMap, DisposableStore } from '../../../../../base/common/lifecycle.js'; -import { autorun, constObservable, observableFromEvent } from '../../../../../base/common/observable.js'; +import { autorun, constObservable, derived, IObservable, observableFromEvent } from '../../../../../base/common/observable.js'; +import { URI } from '../../../../../base/common/uri.js'; import { localize } from '../../../../../nls.js'; import { IContextKey, RawContextKey } from '../../../../../platform/contextkey/common/contextkey.js'; +import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; import { IWorkbenchContribution } from '../../../../common/contributions.js'; import { EditorResourceAccessor, SideBySideEditor } from '../../../../common/editor.js'; import { IEditorGroup, IEditorGroupsService } from '../../../../services/editor/common/editorGroupsService.js'; -import { ChatEditingSessionState, IChatEditingService, IChatEditingSession, IModifiedFileEntry } from '../../common/chatEditingService.js'; +import { IInlineChatSessionService } from '../../../inlineChat/browser/inlineChatSessionService.js'; +import { IChatEditingService, IChatEditingSession, IModifiedFileEntry, WorkingSetEntryState } from '../../common/chatEditingService.js'; import { IChatService } from '../../common/chatService.js'; export const ctxIsGlobalEditingSession = new RawContextKey('chatEdits.isGlobalEditingSession', undefined, localize('chat.ctxEditSessionIsGlobal', "The current editor is part of the global edit session")); @@ -26,8 +29,7 @@ export class ChatEditingEditorContextKeys implements IWorkbenchContribution { private readonly _store = new DisposableStore(); constructor( - @IChatEditingService chatEditingService: IChatEditingService, - @IChatService chatService: IChatService, + @IInstantiationService instaService: IInstantiationService, @IEditorGroupsService editorGroupsService: IEditorGroupsService, ) { @@ -51,7 +53,7 @@ export class ChatEditingEditorContextKeys implements IWorkbenchContribution { continue; } - editorGroupCtx.set(group, new ContextKeyGroup(group, chatEditingService, chatService)); + editorGroupCtx.set(group, instaService.createInstance(ContextKeyGroup, group)); } for (const item of toDispose) { @@ -78,8 +80,9 @@ class ContextKeyGroup { constructor( group: IEditorGroup, - chatEditingService: IChatEditingService, - chatService: IChatService, + @IInlineChatSessionService inlineChatSessionService: IInlineChatSessionService, + @IChatEditingService chatEditingService: IChatEditingService, + @IChatService chatService: IChatService, ) { this._ctxIsGlobalEditingSession = ctxIsGlobalEditingSession.bindTo(group.scopedContextKeyService); this._ctxHasEditorModification = ctxHasEditorModification.bindTo(group.scopedContextKeyService); @@ -99,29 +102,30 @@ class ContextKeyGroup { return; } - let tuple: [IChatEditingSession, IModifiedFileEntry] | undefined; - for (const session of chatEditingService.editingSessionsObs.read(r)) { - const entry = session.readEntry(uri, r); - if (entry) { - tuple = [session, entry]; - break; - } - } + const tuple = new ObservableEditorSession(uri, chatEditingService, inlineChatSessionService).value.read(r); if (!tuple) { this._reset(); return; } - const [session, entry] = tuple; + const { session, entry, isInlineChat } = tuple; - this._ctxHasEditorModification.set(true); + const chatModel = chatService.getSession(session.chatSessionId); + + const isRequestInProgress = chatModel + ? observableFromEvent(this, chatModel.onDidChange, () => chatModel.requestInProgress) + : constObservable(false); + + this._ctxHasEditorModification.set(isInlineChat || entry?.state.read(r) === WorkingSetEntryState.Modified); this._ctxIsGlobalEditingSession.set(session.isGlobalEditingSession); - this._ctxReviewModeEnabled.set(entry.reviewMode.read(r)); - this._ctxHasRequestInProgress.set(session.state.read(r) === ChatEditingSessionState.StreamingEdits); + this._ctxReviewModeEnabled.set(entry ? entry.reviewMode.read(r) : false); + this._ctxHasRequestInProgress.set( + Boolean(entry?.isCurrentlyBeingModifiedBy.read(r)) // any entry changing + || (isInlineChat && isRequestInProgress.read(r)) // inline chat request + ); // number of requests - const chatModel = chatService.getSession(session.chatSessionId); const requestCount = chatModel ? observableFromEvent(this, chatModel.onDidChange, () => chatModel.getRequests().length) : constObservable(0); @@ -143,3 +147,38 @@ class ContextKeyGroup { this._reset(); } } + +export class ObservableEditorSession { + + readonly value: IObservable; + + constructor( + uri: URI, + @IChatEditingService chatEditingService: IChatEditingService, + @IInlineChatSessionService inlineChatService: IInlineChatSessionService + ) { + + const inlineSessionObs = observableFromEvent(this, inlineChatService.onDidChangeSessions, () => inlineChatService.getSession2(uri)); + + const sessionObs = chatEditingService.editingSessionsObs.map((value, r) => { + for (const session of value) { + const entry = session.readEntry(uri, r); + if (entry) { + return { session, entry, isInlineChat: false }; + } + } + return undefined; + }); + + this.value = derived(r => { + + const inlineSession = inlineSessionObs.read(r); + + if (inlineSession) { + return { session: inlineSession.editingSession, entry: inlineSession.editingSession.readEntry(uri, r), isInlineChat: true }; + } + + return sessionObs.read(r); + }); + } +} diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingEditorOverlay.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingEditorOverlay.ts index 2779b1fa483e..13643c63bf9b 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingEditorOverlay.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingEditorOverlay.ts @@ -3,6 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import '../media/chatEditingEditorOverlay.css'; import { combinedDisposable, DisposableMap, DisposableStore, MutableDisposable, toDisposable } from '../../../../../base/common/lifecycle.js'; import { autorun, derived, derivedOpts, IObservable, observableFromEvent, observableSignalFromEvent, observableValue, transaction } from '../../../../../base/common/observable.js'; import { HiddenItemStrategy, MenuWorkbenchToolBar, WorkbenchToolBar } from '../../../../../platform/actions/browser/toolbar.js'; @@ -11,17 +12,14 @@ import { IChatEditingService, IChatEditingSession, IModifiedFileEntry, WorkingSe import { MenuId } from '../../../../../platform/actions/common/actions.js'; import { ActionViewItem } from '../../../../../base/browser/ui/actionbar/actionViewItems.js'; import { IActionRunner } from '../../../../../base/common/actions.js'; -import { $, addDisposableGenericMouseMoveListener, append, reset } from '../../../../../base/browser/dom.js'; +import { addDisposableGenericMouseMoveListener, append, reset } from '../../../../../base/browser/dom.js'; import { renderIcon } from '../../../../../base/browser/ui/iconLabel/iconLabels.js'; import { ThemeIcon } from '../../../../../base/common/themables.js'; import { Codicon } from '../../../../../base/common/codicons.js'; import { assertType } from '../../../../../base/common/types.js'; import { localize } from '../../../../../nls.js'; import { AcceptAction, navigationBearingFakeActionId, RejectAction } from './chatEditingEditorActions.js'; -import '../media/chatEditorOverlay.css'; import { IChatService } from '../../common/chatService.js'; -import { rcut } from '../../../../../base/common/strings.js'; -import { IHoverService } from '../../../../../platform/hover/browser/hover.js'; import { IWorkbenchContribution } from '../../../../common/contributions.js'; import { IEditorGroup, IEditorGroupsService } from '../../../../services/editor/common/editorGroupsService.js'; import { EditorGroupView } from '../../../../browser/parts/editor/editorGroupView.js'; @@ -31,22 +29,24 @@ import { IContextKeyService } from '../../../../../platform/contextkey/common/co import { EditorResourceAccessor, SideBySideEditor } from '../../../../common/editor.js'; import { IInlineChatSessionService } from '../../../inlineChat/browser/inlineChatSessionService.js'; import { isEqual } from '../../../../../base/common/resources.js'; +import { ObservableEditorSession } from './chatEditingEditorContextKeys.js'; +import { rcut } from '../../../../../base/common/strings.js'; class ChatEditorOverlayWidget { private readonly _domNode: HTMLElement; - private readonly _progressNode: HTMLElement; + // private readonly _progressNode: HTMLElement; private readonly _toolbar: WorkbenchToolBar; private readonly _showStore = new DisposableStore(); + private readonly _session = observableValue(this, undefined); private readonly _entry = observableValue(this, undefined); private readonly _navigationBearings = observableValue<{ changeCount: number; activeIdx: number; entriesCount: number }>(this, { changeCount: -1, activeIdx: -1, entriesCount: -1 }); constructor( private readonly _editor: { focus(): void }, - @IHoverService private readonly _hoverService: IHoverService, @IChatService private readonly _chatService: IChatService, @IInstantiationService instaService: IInstantiationService, ) { @@ -56,7 +56,6 @@ class ChatEditorOverlayWidget { const progressNode = document.createElement('div'); progressNode.classList.add('chat-editor-overlay-progress'); append(progressNode, renderIcon(ThemeIcon.modify(Codicon.loading, 'spin'))); - this._progressNode = append(progressNode, $('SPAN.busy-label')); this._domNode.appendChild(progressNode); const toolbarNode = document.createElement('div'); @@ -90,9 +89,13 @@ class ChatEditorOverlayWidget { assertType(this.label); const { changeCount, activeIdx } = that._navigationBearings.read(r); - const n = activeIdx === -1 ? '?' : `${activeIdx + 1}`; - const m = changeCount === -1 ? '?' : `${changeCount}`; - this.label.innerText = localize('nOfM', "{0} of {1}", n, m); + + if (changeCount > 0) { + const n = activeIdx === -1 ? '1' : `${activeIdx + 1}`; + this.label.innerText = localize('nOfM', "{0} of {1}", n, changeCount); + } else { + this.label.innerText = localize('0Of0', "0 of 0"); + } this.updateTooltip(); })); @@ -164,6 +167,93 @@ class ChatEditorOverlayWidget { } }; } + + if (action.id === 'inlineChat2.reveal' || action.id === 'workbench.action.chat.openEditSession') { + return new class extends ActionViewItem { + + private _requestMessage: IObservable<{ message: string; paused?: boolean } | undefined>; + + constructor() { + super(undefined, action, options); + + this._requestMessage = derived(r => { + const session = that._session.read(r); + const chatModel = that._chatService.getSession(session?.chatSessionId ?? ''); + if (!session || !chatModel) { + return undefined; + } + + const response = that._entry.read(r)?.isCurrentlyBeingModifiedBy.read(r); + + if (response) { + + if (response?.isPaused.read(r)) { + return { message: localize('paused', "Edits Paused"), paused: true }; + } + + const entry = that._entry.read(r); + if (entry) { + const progress = entry?.rewriteRatio.read(r); + const message = progress === 0 + ? localize('generating', "Generating Edits") + : localize('applyingPercentage', "{0}% Applying Edits", Math.round(progress * 100)); + + return { message }; + } + } + + if (session.isGlobalEditingSession) { + return undefined; + } + + const request = observableFromEvent(this, chatModel.onDidChange, () => chatModel.getRequests().at(-1)).read(r); + if (!request || request.response?.isComplete) { + return undefined; + } + return { message: request.message.text }; + }); + } + + override render(container: HTMLElement) { + super.render(container); + + container.classList.add('label-item'); + + this._store.add(autorun(r => { + assertType(this.label); + + const value = this._requestMessage.read(r); + if (!value) { + // normal rendering + this.options.icon = true; + this.options.label = false; + reset(this.label); + this.updateClass(); + this.updateLabel(); + this.updateTooltip(); + + } else { + this.options.icon = false; + this.options.label = true; + this.updateClass(); + this.updateTooltip(); + + const message = rcut(value.message, 47); + reset(this.label, message); + } + + const busy = Boolean(value && !value.paused); + that._domNode.classList.toggle('busy', busy); + this.label.classList.toggle('busy', busy); + + })); + } + + protected override getTooltip(): string | undefined { + return this._requestMessage.get()?.message || super.getTooltip(); + } + }; + } return undefined; } }); @@ -179,54 +269,14 @@ class ChatEditorOverlayWidget { return this._domNode; } - showRequest(session: IChatEditingSession) { + show(session: IChatEditingSession, entry: IModifiedFileEntry | undefined, indicies: { entryIndex: IObservable; changeIndex: IObservable }) { this._showStore.clear(); - const chatModel = this._chatService.getSession(session.chatSessionId); - const chatRequest = chatModel?.getRequests().at(-1); - - if (!chatRequest || !chatRequest.response) { - this.hide(); - return; - } - - this._domNode.classList.toggle('busy', true); - - const message = rcut(chatRequest.message.text, 47); - reset(this._progressNode, message); - - this._showStore.add(this._hoverService.setupDelayedHover(this._progressNode, { - content: chatRequest.message.text, - appearance: { showPointer: true } - })); - - } - - showEntry(session: IChatEditingSession, activeEntry: IModifiedFileEntry, indicies: { entryIndex: IObservable; changeIndex: IObservable }) { - - this._showStore.clear(); - - this._entry.set(activeEntry, undefined); - - this._showStore.add(autorun(r => { - const busy = activeEntry.isCurrentlyBeingModifiedBy.read(r); - this._domNode.classList.toggle('busy', !!busy); - })); - - this._showStore.add(autorun(r => { - const paused = activeEntry.isCurrentlyBeingModifiedBy.read(r)?.isPaused.read(r); - const progress = activeEntry.rewriteRatio.read(r); - - const text = paused - ? localize('paused', "Edits Paused") - : progress === 0 - ? localize('generating', "Generating Edits") - : localize('applyingPercentage', "{0}% Applying Edits", Math.round(progress * 100)); - - this._domNode.classList.toggle('paused', !!paused); - reset(this._progressNode, text); - })); + transaction(tx => { + this._session.set(session, tx); + this._entry.set(entry, tx); + }); this._showStore.add(autorun(r => { @@ -256,6 +306,7 @@ class ChatEditorOverlayWidget { hide() { transaction(tx => { + this._session.set(undefined, tx); this._entry.set(undefined, tx); this._navigationBearings.set({ changeCount: -1, activeIdx: -1, entriesCount: -1 }, tx); }); @@ -323,27 +374,7 @@ class ChatEditingOverlayController { return undefined; } - const sessionObs = chatEditingService.editingSessionsObs.map((value, r) => { - for (const session of value) { - const entry = session.readEntry(uri, r); - if (entry) { - return { session, entry }; - } - } - return undefined; - }); - - const result = sessionObs.read(r); - if (result) { - return result; - } - - const inlineSessionObs = observableFromEvent(this, inlineChatService.onDidChangeSessions, () => inlineChatService.getSession2(uri)); - const inlineSession = inlineSessionObs.read(r); - return inlineSession - ? { session: inlineSession.editingSession, entry: inlineSession.editingSession.readEntry(uri, r) } - : undefined; - + return new ObservableEditorSession(uri, chatEditingService, inlineChatService).value.read(r); }); const isInProgress = derived(r => { @@ -374,25 +405,24 @@ class ChatEditingOverlayController { const { session, entry } = data; - if (!session.isGlobalEditingSession && isInProgress.read(r) && !entry?.isCurrentlyBeingModifiedBy.read(r)) { - // inline chat request - widget.showRequest(session); - show(); - - } else if (entry?.state.read(r) === WorkingSetEntryState.Modified) { + if ( + entry?.state.read(r) === WorkingSetEntryState.Modified // any entry changing + || (!session.isGlobalEditingSession && isInProgress.read(r)) // inline chat request + ) { // any session with changes const editorPane = group.activeEditorPane; assertType(editorPane); - const changeIndex = derived(r => { - const idx = entry.getEditorIntegration(editorPane).currentIndex.read(r); - return idx < 0 ? undefined : idx; - }); + const changeIndex = derived(r => entry + ? entry.getEditorIntegration(editorPane).currentIndex.read(r) + : 0); + + const entryIndex = derived(r => entry + ? session.entries.read(r).indexOf(entry) + : 0 + ); - widget.showEntry(session, entry, { - entryIndex: derived(r => session.entries.read(r).indexOf(entry)), - changeIndex - }); + widget.show(session, entry, { entryIndex, changeIndex }); show(); } else { diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedDocumentEntry.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedDocumentEntry.ts index 6d8e6f6b27bf..cd89a9c4a70e 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedDocumentEntry.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedDocumentEntry.ts @@ -5,7 +5,7 @@ import { assert } from '../../../../../base/common/assert.js'; import { RunOnceScheduler } from '../../../../../base/common/async.js'; -import { IReference, toDisposable } from '../../../../../base/common/lifecycle.js'; +import { IReference, MutableDisposable, toDisposable } from '../../../../../base/common/lifecycle.js'; import { observableValue, IObservable, ITransaction, autorun, transaction } from '../../../../../base/common/observable.js'; import { isEqual } from '../../../../../base/common/resources.js'; import { themeColorFromId } from '../../../../../base/common/themables.js'; @@ -31,9 +31,10 @@ import { localize } from '../../../../../nls.js'; import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; import { IFileService } from '../../../../../platform/files/common/files.js'; import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; +import { IMarkerService } from '../../../../../platform/markers/common/markers.js'; import { observableConfigValue } from '../../../../../platform/observable/common/platformObservableUtils.js'; import { editorSelectionBackground } from '../../../../../platform/theme/common/colorRegistry.js'; -import { IUndoRedoService } from '../../../../../platform/undoRedo/common/undoRedo.js'; +import { IUndoRedoElement, IUndoRedoService } from '../../../../../platform/undoRedo/common/undoRedo.js'; import { SaveReason, IEditorPane } from '../../../../common/editor.js'; import { IFilesConfigurationService } from '../../../../services/filesConfiguration/common/filesConfigurationService.js'; import { IResolvedTextFileEditorModel, stringToSnapshot } from '../../../../services/textfile/common/textfiles.js'; @@ -71,30 +72,22 @@ export class ChatEditingModifiedDocumentEntry extends AbstractChatEditingModifie readonly initialContent: string; - private readonly docSnapshot: ITextModel; - private readonly doc: ITextModel; - readonly docFileEditorModel: IResolvedTextFileEditorModel; - - get originalModel(): ITextModel { - return this.docSnapshot; - } + private readonly originalModel: ITextModel; + private readonly modifiedModel: ITextModel; - get modifiedModel(): ITextModel { - return this.doc; - } + readonly docFileEditorModel: IResolvedTextFileEditorModel; - private _isFirstEditAfterStartOrSnapshot: boolean = true; private _edit: OffsetEdit = OffsetEdit.empty; private _isEditFromUs: boolean = false; private _allEditsAreFromUs: boolean = true; - private _diffOperation: Promise | undefined; + private _diffOperation: Promise | undefined; private _diffOperationIds: number = 0; private readonly _diffInfo = observableValue(this, nullDocumentDiff); readonly changesCount = this._diffInfo.map(diff => diff.changes.length); - private readonly _editDecorationClear = this._register(new RunOnceScheduler(() => { this._editDecorations = this.doc.deltaDecorations(this._editDecorations, []); }, 500)); + private readonly _editDecorationClear = this._register(new RunOnceScheduler(() => { this._editDecorations = this.modifiedModel.deltaDecorations(this._editDecorations, []); }, 500)); private _editDecorations: string[] = []; @@ -108,6 +101,7 @@ export class ChatEditingModifiedDocumentEntry extends AbstractChatEditingModifie telemetryInfo: IModifiedEntryTelemetryInfo, kind: ChatEditKind, initialContent: string | undefined, + @IMarkerService markerService: IMarkerService, @IModelService modelService: IModelService, @ITextModelService textModelService: ITextModelService, @ILanguageService languageService: ILanguageService, @@ -115,8 +109,8 @@ export class ChatEditingModifiedDocumentEntry extends AbstractChatEditingModifie @IFilesConfigurationService fileConfigService: IFilesConfigurationService, @IChatService chatService: IChatService, @IEditorWorkerService private readonly _editorWorkerService: IEditorWorkerService, - @IUndoRedoService private readonly _undoRedoService: IUndoRedoService, @IFileService fileService: IFileService, + @IUndoRedoService undoRedoService: IUndoRedoService, @IInstantiationService instantiationService: IInstantiationService ) { super( @@ -127,18 +121,19 @@ export class ChatEditingModifiedDocumentEntry extends AbstractChatEditingModifie fileConfigService, chatService, fileService, + undoRedoService, instantiationService ); this.docFileEditorModel = this._register(resourceRef).object as IResolvedTextFileEditorModel; - this.doc = resourceRef.object.textEditorModel; + this.modifiedModel = resourceRef.object.textEditorModel; this.originalURI = ChatEditingTextModelContentProvider.getFileURI(telemetryInfo.sessionId, this.entryId, this.modifiedURI.path); - this.initialContent = initialContent ?? this.doc.getValue(); - const docSnapshot = this.docSnapshot = this._register( + this.initialContent = initialContent ?? this.modifiedModel.getValue(); + const docSnapshot = this.originalModel = this._register( modelService.createModel( - createTextBufferFactoryFromSnapshot(initialContent ? stringToSnapshot(initialContent) : this.doc.createSnapshot()), - languageService.createById(this.doc.getLanguageId()), + createTextBufferFactoryFromSnapshot(initialContent ? stringToSnapshot(initialContent) : this.modifiedModel.createSnapshot()), + languageService.createById(this.modifiedModel.getLanguageId()), this.originalURI, false ) @@ -155,7 +150,7 @@ export class ChatEditingModifiedDocumentEntry extends AbstractChatEditingModifie })(); - this._register(this.doc.onDidChangeContent(e => this._mirrorEdits(e))); + this._register(this.modifiedModel.onDidChangeContent(e => this._mirrorEdits(e))); this._register(toDisposable(() => { this._clearCurrentEditLineDecoration(); @@ -166,10 +161,21 @@ export class ChatEditingModifiedDocumentEntry extends AbstractChatEditingModifie this._diffTrimWhitespace.read(r); this._updateDiffInfoSeq(); })); + + const resourceFilter = this._register(new MutableDisposable()); + this._register(autorun(r => { + const res = this.isCurrentlyBeingModifiedBy.read(r); + if (res) { + const req = res.session.getRequests().find(value => value.id === res.requestId); + resourceFilter.value = markerService.installResourceFilter(this.modifiedURI, req?.message.text || localize('default', "Chat Edits")); + } else { + resourceFilter.clear(); + } + })); } private _clearCurrentEditLineDecoration() { - this._editDecorations = this.doc.deltaDecorations(this._editDecorations, []); + this._editDecorations = this.modifiedModel.deltaDecorations(this._editDecorations, []); } equalsSnapshot(snapshot: ISnapshotEntry | undefined): boolean { @@ -183,7 +189,6 @@ export class ChatEditingModifiedDocumentEntry extends AbstractChatEditingModifie } createSnapshot(requestId: string | undefined, undoStop: string | undefined): ISnapshotEntry { - this._isFirstEditAfterStartOrSnapshot = true; return { resource: this.modifiedURI, languageId: this.modifiedModel.getLanguageId(), @@ -198,7 +203,7 @@ export class ChatEditingModifiedDocumentEntry extends AbstractChatEditingModifie restoreFromSnapshot(snapshot: ISnapshotEntry) { this._stateObs.set(snapshot.state, undefined); - this.docSnapshot.setValue(snapshot.original); + this.originalModel.setValue(snapshot.original); this._setDocValue(snapshot.current); this._edit = snapshot.originalToCurrentEdit; this._updateDiffInfoSeq(); @@ -208,9 +213,9 @@ export class ChatEditingModifiedDocumentEntry extends AbstractChatEditingModifie this._setDocValue(this.initialContent); } - override async acceptStreamingEditsEnd(tx: ITransaction) { - await this._diffOperation; - super.acceptStreamingEditsEnd(tx); + protected override async _areOriginalAndModifiedIdentical(): Promise { + const diff = await this._diffOperation; + return diff ? diff.identical : false; } protected override _resetEditsState(tx: ITransaction): void { @@ -246,21 +251,21 @@ export class ChatEditingModifiedDocumentEntry extends AbstractChatEditingModifie const e_ai = this._edit; const e_user = edit; - const e_user_r = e_user.tryRebase(e_ai.inverse(this.docSnapshot.getValue()), true); + const e_user_r = e_user.tryRebase(e_ai.inverse(this.originalModel.getValue()), true); if (e_user_r === undefined) { // user edits overlaps/conflicts with AI edits this._edit = e_ai.compose(e_user); } else { - const edits = OffsetEdits.asEditOperations(e_user_r, this.docSnapshot); - this.docSnapshot.applyEdits(edits); + const edits = OffsetEdits.asEditOperations(e_user_r, this.originalModel); + this.originalModel.applyEdits(edits); this._edit = e_ai.tryRebase(e_user_r); } this._allEditsAreFromUs = false; this._updateDiffInfoSeq(); - const didResetToOriginalContent = this.doc.getValue() === this.initialContent; + const didResetToOriginalContent = this.modifiedModel.getValue() === this.initialContent; const currentState = this._stateObs.get(); switch (currentState) { case WorkingSetEntryState.Modified: @@ -272,19 +277,17 @@ export class ChatEditingModifiedDocumentEntry extends AbstractChatEditingModifie } } - acceptAgentEdits(resource: URI, textEdits: (TextEdit | ICellEditOperation)[], isLastEdits: boolean, responseModel: IChatResponseModel): void { + protected override _createUndoRedoElement(response: IChatResponseModel): IUndoRedoElement { + const request = response.session.getRequests().find(req => req.id === response.requestId); + const label = request?.message.text ? localize('chatEditing1', "Chat Edit: '{0}'", request.message.text) : localize('chatEditing2', "Chat Edit"); + return new SingleModelEditStackElement(label, 'chat.edit', this.modifiedModel, null); + } + + async acceptAgentEdits(resource: URI, textEdits: (TextEdit | ICellEditOperation)[], isLastEdits: boolean, responseModel: IChatResponseModel): Promise { assertType(textEdits.every(TextEdit.isTextEdit), 'INVALID args, can only handle text edits'); assert(isEqual(resource, this.modifiedURI), ' INVALID args, can only edit THIS document'); - // push stack element for the first edit - if (this._isFirstEditAfterStartOrSnapshot) { - this._isFirstEditAfterStartOrSnapshot = false; - const request = this._chatService.getSession(this._telemetryInfo.sessionId)?.getRequests().at(-1); - const label = request?.message.text ? localize('chatEditing1', "Chat Edit: '{0}'", request.message.text) : localize('chatEditing2', "Chat Edit"); - this._undoRedoService.pushElement(new SingleModelEditStackElement(label, 'chat.edit', this.doc, null)); - } - const ops = textEdits.map(TextEdit.asEditOperation); const undoEdits = this._applyEdits(ops); @@ -306,14 +309,14 @@ export class ChatEditingModifiedDocumentEntry extends AbstractChatEditingModifie }); } - this._editDecorations = this.doc.deltaDecorations(this._editDecorations, newDecorations); + this._editDecorations = this.modifiedModel.deltaDecorations(this._editDecorations, newDecorations); transaction((tx) => { if (!isLastEdits) { this._stateObs.set(WorkingSetEntryState.Modified, tx); this._isCurrentlyBeingModifiedByObs.set(responseModel, tx); - const lineCount = this.doc.getLineCount(); + const lineCount = this.modifiedModel.getLineCount(); this._rewriteRatioObs.set(Math.min(1, maxLineNumber / lineCount), tx); } else { @@ -335,7 +338,7 @@ export class ChatEditingModifiedDocumentEntry extends AbstractChatEditingModifie const newText = this.modifiedModel.getValueInRange(edit.modifiedRange); edits.push(EditOperation.replace(edit.originalRange, newText)); } - this.docSnapshot.pushEditOperations(null, edits, _ => null); + this.originalModel.pushEditOperations(null, edits, _ => null); await this._updateDiffInfoSeq(); if (this._diffInfo.get().identical) { this._stateObs.set(WorkingSetEntryState.Accepted, undefined); @@ -349,10 +352,10 @@ export class ChatEditingModifiedDocumentEntry extends AbstractChatEditingModifie } const edits: ISingleEditOperation[] = []; for (const edit of change.innerChanges ?? []) { - const newText = this.docSnapshot.getValueInRange(edit.originalRange); + const newText = this.originalModel.getValueInRange(edit.originalRange); edits.push(EditOperation.replace(edit.modifiedRange, newText)); } - this.doc.pushEditOperations(null, edits, _ => null); + this.modifiedModel.pushEditOperations(null, edits, _ => null); await this._updateDiffInfoSeq(); if (this._diffInfo.get().identical) { this._stateObs.set(WorkingSetEntryState.Rejected, undefined); @@ -365,7 +368,7 @@ export class ChatEditingModifiedDocumentEntry extends AbstractChatEditingModifie this._isEditFromUs = true; try { let result: ISingleEditOperation[] = []; - this.doc.pushEditOperations(null, edits, (undoEdits) => { + this.modifiedModel.pushEditOperations(null, edits, (undoEdits) => { result = undoEdits; return null; }); @@ -385,38 +388,40 @@ export class ChatEditingModifiedDocumentEntry extends AbstractChatEditingModifie } } - private async _updateDiffInfo(): Promise { + private async _updateDiffInfo(): Promise { - if (this.docSnapshot.isDisposed() || this.doc.isDisposed()) { - return; + if (this.originalModel.isDisposed() || this.modifiedModel.isDisposed()) { + return undefined; } - const docVersionNow = this.doc.getVersionId(); - const snapshotVersionNow = this.docSnapshot.getVersionId(); + const docVersionNow = this.modifiedModel.getVersionId(); + const snapshotVersionNow = this.originalModel.getVersionId(); const ignoreTrimWhitespace = this._diffTrimWhitespace.get(); const diff = await this._editorWorkerService.computeDiff( - this.docSnapshot.uri, - this.doc.uri, + this.originalModel.uri, + this.modifiedModel.uri, { ignoreTrimWhitespace, computeMoves: false, maxComputationTimeMs: 3000 }, 'advanced' ); - if (this.docSnapshot.isDisposed() || this.doc.isDisposed()) { - return; + if (this.originalModel.isDisposed() || this.modifiedModel.isDisposed()) { + return undefined; } // only update the diff if the documents didn't change in the meantime - if (this.doc.getVersionId() === docVersionNow && this.docSnapshot.getVersionId() === snapshotVersionNow) { + if (this.modifiedModel.getVersionId() === docVersionNow && this.originalModel.getVersionId() === snapshotVersionNow) { const diff2 = diff ?? nullDocumentDiff; this._diffInfo.set(diff2, undefined); - this._edit = OffsetEdits.fromLineRangeMapping(this.docSnapshot, this.doc, diff2.changes); + this._edit = OffsetEdits.fromLineRangeMapping(this.originalModel, this.modifiedModel, diff2.changes); + return diff2; } + return undefined; } protected override async _doAccept(tx: ITransaction | undefined): Promise { - this.docSnapshot.setValue(this.doc.createSnapshot()); + this.originalModel.setValue(this.modifiedModel.createSnapshot()); this._diffInfo.set(nullDocumentDiff, tx); this._edit = OffsetEdit.empty; await this._collapse(tx); @@ -428,7 +433,7 @@ export class ChatEditingModifiedDocumentEntry extends AbstractChatEditingModifie await this._fileService.del(this.modifiedURI); this._onDidDelete.fire(); } else { - this._setDocValue(this.docSnapshot.getValue()); + this._setDocValue(this.originalModel.getValue()); if (this._allEditsAreFromUs) { // save the file after discarding so that the dirty indicator goes away // and so that an intermediate saved state gets reverted @@ -439,14 +444,14 @@ export class ChatEditingModifiedDocumentEntry extends AbstractChatEditingModifie } private _setDocValue(value: string): void { - if (this.doc.getValue() !== value) { + if (this.modifiedModel.getValue() !== value) { - this.doc.pushStackElement(); - const edit = EditOperation.replace(this.doc.getFullModelRange(), value); + this.modifiedModel.pushStackElement(); + const edit = EditOperation.replace(this.modifiedModel.getFullModelRange(), value); this._applyEdits([edit]); this._updateDiffInfoSeq(); - this.doc.pushStackElement(); + this.modifiedModel.pushStackElement(); } } diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedFileEntry.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedFileEntry.ts index fb1b7d362740..73bd3f496953 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedFileEntry.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedFileEntry.ts @@ -17,6 +17,7 @@ import { IFileService } from '../../../../../platform/files/common/files.js'; import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; import { observableConfigValue } from '../../../../../platform/observable/common/platformObservableUtils.js'; import { editorBackground, registerColor, transparent } from '../../../../../platform/theme/common/colorRegistry.js'; +import { IUndoRedoElement, IUndoRedoService } from '../../../../../platform/undoRedo/common/undoRedo.js'; import { IEditorPane } from '../../../../common/editor.js'; import { IFilesConfigurationService } from '../../../../services/filesConfiguration/common/filesConfigurationService.js'; import { ICellEditOperation } from '../../../notebook/common/notebookCommon.js'; @@ -33,7 +34,7 @@ class AutoAcceptControl { ) { } } -export const pendingRewriteMinimap = registerColor('chatEdits.minimapColor', +export const pendingRewriteMinimap = registerColor('minimap.chatEditHighlight', transparent(editorBackground, 0.6), localize('editorSelectionBackground', "Color of pending edit regions in the minimap")); @@ -49,7 +50,7 @@ export abstract class AbstractChatEditingModifiedFileEntry extends Disposable im protected readonly _onDidDelete = this._register(new Emitter()); readonly onDidDelete = this._onDidDelete.event; - protected readonly _stateObs = observableValue(this, WorkingSetEntryState.Modified); + protected readonly _stateObs = observableValue(this, WorkingSetEntryState.Attached); readonly state: IObservable = this._stateObs; protected readonly _isCurrentlyBeingModifiedByObs = observableValue(this, undefined); @@ -88,6 +89,7 @@ export abstract class AbstractChatEditingModifiedFileEntry extends Disposable im @IFilesConfigurationService fileConfigService: IFilesConfigurationService, @IChatService protected readonly _chatService: IChatService, @IFileService protected readonly _fileService: IFileService, + @IUndoRedoService private readonly _undoRedoService: IUndoRedoService, @IInstantiationService protected readonly _instantiationService: IInstantiationService, ) { super(); @@ -223,15 +225,26 @@ export abstract class AbstractChatEditingModifiedFileEntry extends Disposable im this._resetEditsState(tx); this._isCurrentlyBeingModifiedByObs.set(responseModel, tx); this._autoAcceptCtrl.get()?.cancel(); + + const undoRedoElement = this._createUndoRedoElement(responseModel); + if (undoRedoElement) { + this._undoRedoService.pushElement(undoRedoElement); + } } - abstract acceptAgentEdits(uri: URI, edits: (TextEdit | ICellEditOperation)[], isLastEdits: boolean, responseModel: IChatResponseModel): void; + protected abstract _createUndoRedoElement(response: IChatResponseModel): IUndoRedoElement | undefined; + + abstract acceptAgentEdits(uri: URI, edits: (TextEdit | ICellEditOperation)[], isLastEdits: boolean, responseModel: IChatResponseModel): Promise; async acceptStreamingEditsEnd(tx: ITransaction) { this._resetEditsState(tx); - // AUTO accept mode - if (!this.reviewMode.get() && !this._autoAcceptCtrl.get()) { + if (await this._areOriginalAndModifiedIdentical()) { + // ACCEPT if identical + this.accept(tx); + + } else if (!this.reviewMode.get() && !this._autoAcceptCtrl.get()) { + // AUTO accept mode const acceptTimeout = this._autoAcceptTimeout.get() * 1000; const future = Date.now() + acceptTimeout; @@ -259,6 +272,8 @@ export abstract class AbstractChatEditingModifiedFileEntry extends Disposable im } } + protected abstract _areOriginalAndModifiedIdentical(): Promise; + protected _resetEditsState(tx: ITransaction): void { this._isCurrentlyBeingModifiedByObs.set(undefined, tx); this._rewriteRatioObs.set(0, tx); diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedNotebookEntry.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedNotebookEntry.ts index be5bfde342a8..b21def982b1c 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedNotebookEntry.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedNotebookEntry.ts @@ -3,63 +3,919 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { assert } from '../../../../../base/common/assert.js'; +import { streamToBuffer } from '../../../../../base/common/buffer.js'; +import { CancellationToken } from '../../../../../base/common/cancellation.js'; +import { DisposableStore, IReference } from '../../../../../base/common/lifecycle.js'; +import { ResourceMap, ResourceSet } from '../../../../../base/common/map.js'; import { Schemas } from '../../../../../base/common/network.js'; -import { ITransaction, IObservable, constObservable } from '../../../../../base/common/observable.js'; +import { ITransaction, IObservable, observableValue, autorun, transaction, ObservablePromise } from '../../../../../base/common/observable.js'; import { isEqual } from '../../../../../base/common/resources.js'; import { assertType } from '../../../../../base/common/types.js'; import { URI } from '../../../../../base/common/uri.js'; +import { generateUuid } from '../../../../../base/common/uuid.js'; +import { LineRange } from '../../../../../editor/common/core/lineRange.js'; +import { OffsetEdit } from '../../../../../editor/common/core/offsetEdit.js'; +import { Range } from '../../../../../editor/common/core/range.js'; +import { nullDocumentDiff } from '../../../../../editor/common/diff/documentDiffProvider.js'; +import { DetailedLineRangeMapping, RangeMapping } from '../../../../../editor/common/diff/rangeMapping.js'; import { TextEdit } from '../../../../../editor/common/languages.js'; -import { IEditorPane } from '../../../../common/editor.js'; -import { ICellEditOperation } from '../../../notebook/common/notebookCommon.js'; -import { IModifiedFileEntryEditorIntegration } from '../../common/chatEditingService.js'; +import { ITextModel } from '../../../../../editor/common/model.js'; +import { IModelService } from '../../../../../editor/common/services/model.js'; +import { ITextModelService } from '../../../../../editor/common/services/resolverService.js'; +import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; +import { IFileService } from '../../../../../platform/files/common/files.js'; +import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; +import { IUndoRedoElement, IUndoRedoService } from '../../../../../platform/undoRedo/common/undoRedo.js'; +import { IEditorPane, SaveReason } from '../../../../common/editor.js'; +import { IFilesConfigurationService } from '../../../../services/filesConfiguration/common/filesConfigurationService.js'; +import { SnapshotContext } from '../../../../services/workingCopy/common/fileWorkingCopy.js'; +import { NotebookTextDiffEditor } from '../../../notebook/browser/diff/notebookDiffEditor.js'; +import { INotebookTextDiffEditor } from '../../../notebook/browser/diff/notebookDiffEditorBrowser.js'; +import { CellDiffInfo } from '../../../notebook/browser/diff/notebookDiffViewModel.js'; +import { getNotebookEditorFromEditorPane } from '../../../notebook/browser/notebookBrowser.js'; +import { NotebookCellTextModel } from '../../../notebook/common/model/notebookCellTextModel.js'; +import { NotebookTextModel } from '../../../notebook/common/model/notebookTextModel.js'; +import { CellEditType, ICellDto2, ICellEditOperation, ICellReplaceEdit, IResolvedNotebookEditorModel, NotebookCellsChangeType, NotebookTextModelChangedEvent, TransientOptions } from '../../../notebook/common/notebookCommon.js'; +import { computeDiff } from '../../../notebook/common/notebookDiff.js'; +import { INotebookEditorModelResolverService } from '../../../notebook/common/notebookEditorModelResolverService.js'; +import { INotebookLoggingService } from '../../../notebook/common/notebookLoggingService.js'; +import { INotebookService } from '../../../notebook/common/notebookService.js'; +import { INotebookEditorWorkerService } from '../../../notebook/common/services/notebookWorkerService.js'; +import { ChatEditKind, IModifiedFileEntryEditorIntegration, WorkingSetEntryState } from '../../common/chatEditingService.js'; import { IChatResponseModel } from '../../common/chatModel.js'; -import { AbstractChatEditingModifiedFileEntry, ISnapshotEntry } from './chatEditingModifiedFileEntry.js'; +import { IChatService } from '../../common/chatService.js'; +import { AbstractChatEditingModifiedFileEntry, IModifiedEntryTelemetryInfo, ISnapshotEntry } from './chatEditingModifiedFileEntry.js'; +import { createSnapshot, deserializeSnapshot, getNotebookSnapshotFileURI, restoreSnapshot, SnapshotComparer } from './notebook/chatEditingModifiedNotebookSnapshot.js'; +import { ChatEditingNewNotebookContentEdits } from './notebook/chatEditingNewNotebookContentEdits.js'; +import { ChatEditingNotebookCellEntry } from './notebook/chatEditingNotebookCellEntry.js'; +import { ChatEditingNotebookDiffEditorIntegration, ChatEditingNotebookEditorIntegration } from './notebook/chatEditingNotebookEditorIntegration.js'; +import { ChatEditingNotebookFileSystemProvider } from './notebook/chatEditingNotebookFileSystemProvider.js'; +import { adjustCellDiffAndOriginalModelBasedOnCellAddDelete, adjustCellDiffAndOriginalModelBasedOnCellMovements, adjustCellDiffForKeepingAnInsertedCell, adjustCellDiffForRevertingADeletedCell, adjustCellDiffForRevertingAnInsertedCell, calculateNotebookRewriteRatio, getCorrespondingOriginalCellIndex, isTransientIPyNbExtensionEvent } from './notebook/helpers.js'; +import { countChanges, ICellDiffInfo, sortCellChanges } from './notebook/notebookCellChanges.js'; + + +const SnapshotLanguageId = 'VSCodeChatNotebookSnapshotLanguage'; export class ChatEditingModifiedNotebookEntry extends AbstractChatEditingModifiedFileEntry { + static NewModelCounter: number = 0; + private readonly modifiedModel: NotebookTextModel; + private readonly originalModel: NotebookTextModel; + override originalURI: URI; + /** + * JSON stringified version of the original notebook. + */ + override initialContent: string; + /** + * Whether we're still generating diffs from a response. + */ + private _isProcessingResponse = observableValue('isProcessingResponse', false); + get isProcessingResponse(): IObservable { + return this._isProcessingResponse; + } + private _isEditFromUs: boolean = false; + /** + * Whether all edits are from us, e.g. is possible a user has made edits, then this will be false. + */ + private _allEditsAreFromUs: boolean = true; + private readonly _changesCount = observableValue(this, 0); + override changesCount: IObservable = this._changesCount; + + private readonly cellEntryMap = new ResourceMap(); + private modifiedToOriginalCell = new ResourceMap(); + private readonly _cellsDiffInfo = observableValue('diffInfo', []); + + get cellsDiffInfo(): IObservable { + return this._cellsDiffInfo; + } + + /** + * List of Cell URIs that are edited, + * Will be cleared once all edits have been accepted. + * I.e. this will only contain URIS while acceptAgentEdits is being called & before `isLastEdit` is sent. + * I.e. this is populated only when edits are being streamed. + */ + private readonly editedCells = new ResourceSet(); + + public static async create(uri: URI, _multiDiffEntryDelegate: { collapse: (transaction: ITransaction | undefined) => void }, telemetryInfo: IModifiedEntryTelemetryInfo, chatKind: ChatEditKind, initialContent: string | undefined, instantiationService: IInstantiationService): Promise { + return instantiationService.invokeFunction(async accessor => { + const notebookService = accessor.get(INotebookService); + const resolver = accessor.get(INotebookEditorModelResolverService); + const configurationServie = accessor.get(IConfigurationService); + const resourceRef: IReference = await resolver.resolve(uri); + const notebook = resourceRef.object.notebook; + const originalUri = getNotebookSnapshotFileURI(telemetryInfo.sessionId, telemetryInfo.requestId, generateUuid(), notebook.uri.scheme === Schemas.untitled ? `/${notebook.uri.path}` : notebook.uri.path, notebook.viewType); + const [options, buffer] = await Promise.all([ + notebookService.withNotebookDataProvider(resourceRef.object.notebook.notebookType), + notebookService.createNotebookTextDocumentSnapshot(notebook.uri, SnapshotContext.Backup, CancellationToken.None).then(s => streamToBuffer(s)) + ]); + const disposables = new DisposableStore(); + // Register so that we can load this from file system. + disposables.add(ChatEditingNotebookFileSystemProvider.registerFile(originalUri, buffer)); + const originalRef = await resolver.resolve(originalUri, notebook.viewType); + if (initialContent) { + restoreSnapshot(originalRef.object.notebook, initialContent); + } else { + initialContent = createSnapshot(notebook, options.serializer.options, configurationServie); + // Both models are the same, ensure the cell ids are the same, this way we get a perfect diffing. + // No need to generate edits for this. + // We want to ensure they are identitcal, possible original notebook was open and got modified. + // Or something gets changed between serialization & deserialization of the snapshot into the original. + // E.g. in jupyter notebooks the metadata contains transient data that gets updated after deserialization. + restoreSnapshot(originalRef.object.notebook, initialContent); + const edits: ICellEditOperation[] = []; + notebook.cells.forEach((cell, index) => { + const cellId = cell.internalMetadata?.cellId; + if (cellId) { + edits.push({ editType: CellEditType.PartialInternalMetadata, index, internalMetadata: { cellId } }); + } + }); + originalRef.object.notebook.applyEdits(edits, true, undefined, () => undefined, undefined, false); + } + const instance = instantiationService.createInstance(ChatEditingModifiedNotebookEntry, resourceRef, originalRef, _multiDiffEntryDelegate, options.serializer.options, telemetryInfo, chatKind, initialContent); + instance._register(disposables); + return instance; + }); + } + + public static canHandleSnapshotContent(initialContent: string | undefined): boolean { + if (!initialContent) { + return false; + } + + try { + deserializeSnapshot(initialContent); + return true; + } catch (ex) { + // not a valid snapshot + return false; + } + } + + public static canHandleSnapshot(snapshot: ISnapshotEntry): boolean { + if (snapshot.languageId === SnapshotLanguageId && ChatEditingModifiedNotebookEntry.canHandleSnapshotContent(snapshot.current)) { + return true; + } + return false; + } + + private readonly initialContentComparer: SnapshotComparer; + + constructor( + private readonly modifiedResourceRef: IReference, + originalResourceRef: IReference, + private readonly _multiDiffEntryDelegate: { collapse: (transaction: ITransaction | undefined) => void }, + private readonly transientOptions: TransientOptions | undefined, + telemetryInfo: IModifiedEntryTelemetryInfo, + kind: ChatEditKind, + initialContent: string, + @IConfigurationService private readonly configurationService: IConfigurationService, + @IFilesConfigurationService fileConfigService: IFilesConfigurationService, + @IChatService chatService: IChatService, + @IFileService fileService: IFileService, + @IInstantiationService instantiationService: IInstantiationService, + @ITextModelService private readonly textModelService: ITextModelService, + @IModelService private readonly modelService: IModelService, + @IUndoRedoService undoRedoService: IUndoRedoService, + @INotebookEditorWorkerService private readonly notebookEditorWorkerService: INotebookEditorWorkerService, + @INotebookLoggingService private readonly loggingService: INotebookLoggingService, + ) { + super(modifiedResourceRef.object.notebook.uri, telemetryInfo, kind, configurationService, fileConfigService, chatService, fileService, undoRedoService, instantiationService); + this.initialContentComparer = new SnapshotComparer(initialContent); + this.modifiedModel = this._register(modifiedResourceRef).object.notebook; + this.originalModel = this._register(originalResourceRef).object.notebook; + this.originalURI = this.originalModel.uri; + this.initialContent = initialContent; + this.initializeModelsFromDiff(); + this._register(this.modifiedModel.onDidChangeContent(this.mirrorNotebookEdits, this)); + } + + initializeModelsFromDiffImpl(cellsDiffInfo: CellDiffInfo[]) { + this.cellEntryMap.forEach(entry => entry.dispose()); + this.cellEntryMap.clear(); + const diffs = cellsDiffInfo.map((cellDiff, i) => { + switch (cellDiff.type) { + case 'delete': + return this.createDeleteCellDiffInfo(cellDiff.originalCellIndex); + case 'insert': + return this.createInsertedCellDiffInfo(cellDiff.modifiedCellIndex); + default: + return this.createModifiedCellDiffInfo(cellDiff.modifiedCellIndex, cellDiff.originalCellIndex); + } + }); + this._cellsDiffInfo.set(diffs, undefined); + this._changesCount.set(countChanges(diffs), undefined); + } + + private computeRequestId: number = 0; + async initializeModelsFromDiff() { + const id = ++this.computeRequestId; + if (this._areOriginalAndModifiedIdenticalImpl()) { + const cellsDiffInfo: CellDiffInfo[] = this.modifiedModel.cells.map((_, index) => { + return { type: 'unchanged', originalCellIndex: index, modifiedCellIndex: index } satisfies CellDiffInfo; + }); + this.initializeModelsFromDiffImpl(cellsDiffInfo); + return; + } + const cellsDiffInfo: CellDiffInfo[] = []; + try { + this._isProcessingResponse.set(true, undefined); + const notebookDiff = await this.notebookEditorWorkerService.computeDiff(this.originalURI, this.modifiedURI); + if (id !== this.computeRequestId) { + return; + } + const result = computeDiff(this.originalModel, this.modifiedModel, notebookDiff); + if (result.cellDiffInfo.length) { + cellsDiffInfo.push(...result.cellDiffInfo); + } + } catch (ex) { + this.loggingService.error('Notebook Chat', 'Error computing diff:\n' + ex); + } finally { + this._isProcessingResponse.set(false, undefined); + } + this.initializeModelsFromDiffImpl(cellsDiffInfo); + } + updateCellDiffInfo(cellsDiffInfo: ICellDiffInfo[], transcation: ITransaction | undefined) { + this._cellsDiffInfo.set(sortCellChanges(cellsDiffInfo), transcation); + this._changesCount.set(countChanges(cellsDiffInfo), transcation); - override originalURI: URI = URI.parse('todo://todo/todo'); + } + + mirrorNotebookEdits(e: NotebookTextModelChangedEvent) { + if (this._isEditFromUs || Array.from(this.cellEntryMap.values()).some(entry => entry.isEditFromUs)) { + return; + } + + // Possible user reverted the changes from SCM or the like. + // Or user just reverted the changes made via edits (e.g. edit made a change in a cell and user undid that change either by typing over or other). + // Computing snapshot is too slow, as this event gets triggered for every key stroke in a cell, + // const didResetToOriginalContent = createSnapshot(this.modifiedModel, this.transientOptions, this.configurationService) === this.initialContent; + let didResetToOriginalContent = this.initialContentComparer.isEqual(this.modifiedModel); + const currentState = this._stateObs.get(); + if (currentState === WorkingSetEntryState.Rejected) { + return; + } + if (currentState === WorkingSetEntryState.Modified && didResetToOriginalContent) { + this._stateObs.set(WorkingSetEntryState.Rejected, undefined); + this.updateCellDiffInfo([], undefined); + return; + } - override initialContent: string = 'JSON.stringify(NotebookData)'; + if (!e.rawEvents.length) { + return; + } + if (isTransientIPyNbExtensionEvent(this.modifiedModel.notebookType, e)) { + return; + } - override changesCount: IObservable = constObservable(Number.MAX_SAFE_INTEGER); + this._allEditsAreFromUs = false; + + // Changes to cell text is sync'ed and handled separately. + // See ChatEditingNotebookCellEntry._mirrorEdits + for (const event of e.rawEvents.filter(event => event.kind !== NotebookCellsChangeType.ChangeCellContent)) { + switch (event.kind) { + case NotebookCellsChangeType.ChangeDocumentMetadata: { + const edit: ICellEditOperation = { + editType: CellEditType.DocumentMetadata, + metadata: this.modifiedModel.metadata + }; + this.originalModel.applyEdits([edit], true, undefined, () => undefined, undefined, true); + break; + } + case NotebookCellsChangeType.ModelChange: { + let cellDiffs = sortCellChanges(this._cellsDiffInfo.get()); + event.changes.forEach(change => { + cellDiffs = adjustCellDiffAndOriginalModelBasedOnCellAddDelete(change, + cellDiffs, + this.modifiedModel.cells.length, + this.originalModel.cells.length, + this.originalModel.applyEdits.bind(this.originalModel), + this.createModifiedCellDiffInfo.bind(this)); + }); + this.updateCellDiffInfo(cellDiffs, undefined); + this.disposeDeletedCellEntries(); + break; + } + case NotebookCellsChangeType.ChangeCellLanguage: { + const index = getCorrespondingOriginalCellIndex(event.index, this._cellsDiffInfo.get()); + if (typeof index === 'number') { + const edit: ICellEditOperation = { + editType: CellEditType.CellLanguage, + index, + language: event.language + }; + this.originalModel.applyEdits([edit], true, undefined, () => undefined, undefined, true); + } + break; + } + case NotebookCellsChangeType.ChangeCellMetadata: { + // ipynb and other extensions can alter metadata, ensure we update the original model in the corresponding cell. + const index = getCorrespondingOriginalCellIndex(event.index, this._cellsDiffInfo.get()); + if (typeof index === 'number') { + const edit: ICellEditOperation = { + editType: CellEditType.Metadata, + index, + metadata: event.metadata + }; + this.originalModel.applyEdits([edit], true, undefined, () => undefined, undefined, true); + } + break; + } + case NotebookCellsChangeType.ChangeCellMime: + break; + case NotebookCellsChangeType.ChangeCellInternalMetadata: { + const index = getCorrespondingOriginalCellIndex(event.index, this._cellsDiffInfo.get()); + if (typeof index === 'number') { + const edit: ICellEditOperation = { + editType: CellEditType.PartialInternalMetadata, + index, + internalMetadata: event.internalMetadata + }; + this.originalModel.applyEdits([edit], true, undefined, () => undefined, undefined, true); + } + break; + } + case NotebookCellsChangeType.Output: { + // User can run cells. + const index = getCorrespondingOriginalCellIndex(event.index, this._cellsDiffInfo.get()); + if (typeof index === 'number') { + const edit: ICellEditOperation = { + editType: CellEditType.Output, + index, + append: event.append, + outputs: event.outputs + }; + this.originalModel.applyEdits([edit], true, undefined, () => undefined, undefined, true); + } + break; + } + case NotebookCellsChangeType.OutputItem: { + const index = getCorrespondingOriginalCellIndex(event.index, this._cellsDiffInfo.get()); + if (typeof index === 'number') { + const edit: ICellEditOperation = { + editType: CellEditType.OutputItems, + outputId: event.outputId, + append: event.append, + items: event.outputItems + }; + this.originalModel.applyEdits([edit], true, undefined, () => undefined, undefined, true); + } + break; + } + case NotebookCellsChangeType.Move: { + const result = adjustCellDiffAndOriginalModelBasedOnCellMovements(event, this._cellsDiffInfo.get().slice()); + if (result) { + this.originalModel.applyEdits(result[1], true, undefined, () => undefined, undefined, true); + this._cellsDiffInfo.set(result[0], undefined); + } + break; + } + default: { + break; + } + } + } + + didResetToOriginalContent = this.initialContentComparer.isEqual(this.modifiedModel); + if (currentState === WorkingSetEntryState.Modified && didResetToOriginalContent) { + this._stateObs.set(WorkingSetEntryState.Rejected, undefined); + this.updateCellDiffInfo([], undefined); + return; + } + } - protected override _doAccept(tx: ITransaction | undefined): Promise { - throw new Error('Method not implemented.'); + protected override async _doAccept(tx: ITransaction | undefined): Promise { + this.updateCellDiffInfo([], tx); + const snapshot = createSnapshot(this.modifiedModel, this.transientOptions, this.configurationService); + restoreSnapshot(this.originalModel, snapshot); + this.initializeModelsFromDiff(); + await this._collapse(tx); } - protected override _doReject(tx: ITransaction | undefined): Promise { - throw new Error('Method not implemented.'); + protected override async _doReject(tx: ITransaction | undefined): Promise { + this.updateCellDiffInfo([], tx); + if (this.createdInRequestId === this._telemetryInfo.requestId) { + await this._applyEdits(async () => { + await this.modifiedResourceRef.object.revert({ soft: true }); + await this._fileService.del(this.modifiedURI); + }); + this._onDidDelete.fire(); + } else { + await this._applyEdits(async () => { + const snapshot = createSnapshot(this.originalModel, this.transientOptions, this.configurationService); + this.restoreSnapshotInModifiedModel(snapshot); + if (this._allEditsAreFromUs && Array.from(this.cellEntryMap.values()).every(entry => entry.allEditsAreFromUs)) { + // save the file after discarding so that the dirty indicator goes away + // and so that an intermediate saved state gets reverted + await this.modifiedResourceRef.object.save({ reason: SaveReason.EXPLICIT, skipSaveParticipants: true }); + } + }); + this.initializeModelsFromDiff(); + await this._collapse(tx); + } + } + + private async _collapse(transaction: ITransaction | undefined): Promise { + this._multiDiffEntryDelegate.collapse(transaction); } protected override _createEditorIntegration(editor: IEditorPane): IModifiedFileEntryEditorIntegration { - throw new Error('Method not implemented.'); + const notebookEditor = getNotebookEditorFromEditorPane(editor); + if (!notebookEditor && editor.getId() === NotebookTextDiffEditor.ID) { + const diffEditor = (editor.getControl() as INotebookTextDiffEditor); + return this._instantiationService.createInstance(ChatEditingNotebookDiffEditorIntegration, diffEditor, this._cellsDiffInfo); + } + assertType(notebookEditor); + return this._instantiationService.createInstance(ChatEditingNotebookEditorIntegration, this, editor, this.modifiedModel, this.originalModel, this._cellsDiffInfo); } - override acceptAgentEdits(resource: URI, edits: (TextEdit | ICellEditOperation)[], isLastEdits: boolean, responseModel: IChatResponseModel): void { + protected override _resetEditsState(tx: ITransaction): void { + super._resetEditsState(tx); + this.cellEntryMap.forEach(entry => !entry.disposed && entry.clearCurrentEditLineDecoration()); + } + + protected override _createUndoRedoElement(_response: IChatResponseModel): IUndoRedoElement | undefined { + // TODO@amunger + return undefined; + } + protected override async _areOriginalAndModifiedIdentical(): Promise { + return this._areOriginalAndModifiedIdenticalImpl(); + } + + private _areOriginalAndModifiedIdenticalImpl(): boolean { + const snapshot = createSnapshot(this.originalModel, this.transientOptions, this.configurationService); + return new SnapshotComparer(snapshot).isEqual(this.modifiedModel); + } + + private newNotebookEditGenerator?: ChatEditingNewNotebookContentEdits; + override async acceptAgentEdits(resource: URI, edits: (TextEdit | ICellEditOperation)[], isLastEdits: boolean, responseModel: IChatResponseModel): Promise { const isCellUri = resource.scheme === Schemas.vscodeNotebookCell; - assert(isCellUri || isEqual(resource, this.modifiedURI)); - assertType(edits.every(edit => !TextEdit.isTextEdit(edit) || isCellUri)); + const cell = isCellUri && this.modifiedModel.cells.find(cell => isEqual(cell.uri, resource)); + let cellEntry: ChatEditingNotebookCellEntry | undefined; + if (cell) { + const index = this.modifiedModel.cells.indexOf(cell); + const entry = this._cellsDiffInfo.get().slice().find(entry => entry.modifiedCellIndex === index); + if (!entry) { + // Not possible. + console.error('Original cell model not found'); + return; + } + + cellEntry = this.getOrCreateModifiedTextFileEntryForCell(cell, await entry.modifiedModel.promise, await entry.originalModel.promise); + } + + // For all cells that were edited, send the `isLastEdits` flag. + const finishPreviousCells = () => { + this.editedCells.forEach(uri => { + const cell = this.modifiedModel.cells.find(cell => isEqual(cell.uri, uri)); + const cellEntry = cell && this.cellEntryMap.get(cell.uri); + cellEntry?.acceptAgentEdits([], true, responseModel); + }); + this.editedCells.clear(); + }; + + await this._applyEdits(async () => { + edits.map(edit => { + if (TextEdit.isTextEdit(edit)) { + // Possible we're getting the raw content for the notebook. + if (isEqual(resource, this.modifiedModel.uri)) { + this.newNotebookEditGenerator ??= this._instantiationService.createInstance(ChatEditingNewNotebookContentEdits, this.modifiedModel); + this.newNotebookEditGenerator.acceptTextEdits([edit]); + } else { + // If we get cell edits, its impossible to get text edits for the notebook uri. + this.newNotebookEditGenerator = undefined; + if (!this.editedCells.has(resource)) { + finishPreviousCells(); + this.editedCells.add(resource); + } + cellEntry?.acceptAgentEdits([edit], isLastEdits, responseModel); + } + } else { + // If we notebook edits, its impossible to get text edits for the notebook uri. + this.newNotebookEditGenerator = undefined; + this.acceptNotebookEdit(edit); + } + }); + }); + + // If the last edit for a cell was sent, then handle it + if (isLastEdits) { + finishPreviousCells(); + } + + // isLastEdits can be true for cell Uris, but when its true for Cells edits. + // It cannot be true for the notebook itself. + isLastEdits = !isCellUri && isLastEdits; + + // If this is the last edit and & we got regular text edits for generating new notebook content + // Then generate notebook edits from those text edits & apply those notebook edits. + if (isLastEdits && this.newNotebookEditGenerator) { + const notebookEdits = await this.newNotebookEditGenerator.generateEdits(); + this.newNotebookEditGenerator = undefined; + notebookEdits.forEach(edit => this.acceptNotebookEdit(edit)); + } - // needs to handle notebook and textual cell edits + transaction((tx) => { + if (!isLastEdits) { + this._stateObs.set(WorkingSetEntryState.Modified, tx); + this._isCurrentlyBeingModifiedByObs.set(responseModel, tx); + const newRewriteRation = Math.max(this._rewriteRatioObs.get(), calculateNotebookRewriteRatio(this._cellsDiffInfo.get(), this.originalModel, this.modifiedModel)); + this._rewriteRatioObs.set(Math.min(1, newRewriteRation), tx); - throw new Error('Method not implemented.'); + } else { + finishPreviousCells(); + this.editedCells.clear(); + this._resetEditsState(tx); + this._rewriteRatioObs.set(1, tx); + } + }); + } + + private disposeDeletedCellEntries() { + const cellsUris = new ResourceSet(this.modifiedModel.cells.map(cell => cell.uri)); + Array.from(this.cellEntryMap.keys()).forEach(uri => { + if (cellsUris.has(uri)) { + return; + } + this.cellEntryMap.get(uri)?.dispose(); + this.cellEntryMap.delete(uri); + }); + } + + acceptNotebookEdit(edit: ICellEditOperation): void { + // make the actual edit + this.modifiedModel.applyEdits([edit], true, undefined, () => undefined, undefined, true); + this.disposeDeletedCellEntries(); + + if (edit.editType !== CellEditType.Replace) { + return; + } + if (edit.count === 0) { + // All existing indexes are shifted by number of cells added. + const diff = sortCellChanges(this._cellsDiffInfo.get()); + diff.forEach(d => { + if (d.type !== 'delete' && d.modifiedCellIndex >= edit.index) { + d.modifiedCellIndex += edit.cells.length; + } + }); + const diffInsert = edit.cells.map((_, i) => this.createInsertedCellDiffInfo(edit.index + i)); + diff.splice(edit.index, 0, ...diffInsert); + this.updateCellDiffInfo(diff, undefined); + } else { + // All existing indexes are shifted by number of cells removed. + // And unchanged cells should be converted to deleted cells. + const diff = sortCellChanges(this._cellsDiffInfo.get()).map((d) => { + if (d.type === 'unchanged' && d.modifiedCellIndex >= edit.index && d.modifiedCellIndex <= (edit.index + edit.count - 1)) { + return this.createDeleteCellDiffInfo(d.originalCellIndex); + } + if (d.type !== 'delete' && d.modifiedCellIndex >= (edit.index + edit.count)) { + d.modifiedCellIndex -= edit.count; + return d; + } + return d; + }); + this.updateCellDiffInfo(diff, undefined); + } + } + + private computeStateAfterAcceptingRejectingChanges(accepted: boolean) { + const currentSnapshot = createSnapshot(this.modifiedModel, this.transientOptions, this.configurationService); + if (new SnapshotComparer(currentSnapshot).isEqual(this.originalModel)) { + const state = accepted ? WorkingSetEntryState.Accepted : WorkingSetEntryState.Rejected; + this._stateObs.set(state, undefined); + } + } + + createModifiedCellDiffInfo(modifiedCellIndex: number, originalCellIndex: number): ICellDiffInfo { + const modifiedCell = this.modifiedModel.cells[modifiedCellIndex]; + const originalCell = this.originalModel.cells[originalCellIndex]; + this.modifiedToOriginalCell.set(modifiedCell.uri, originalCell.uri); + const modifiedCellModelPromise = this.resolveCellModel(modifiedCell.uri); + const originalCellModelPromise = this.resolveCellModel(originalCell.uri); + + Promise.all([modifiedCellModelPromise, originalCellModelPromise]).then(([modifiedCellModel, originalCellModel]) => { + this.getOrCreateModifiedTextFileEntryForCell(modifiedCell, modifiedCellModel, originalCellModel); + }); + + const diff = observableValue('diff', nullDocumentDiff); + const unchangedCell: ICellDiffInfo = { + type: 'unchanged', + modifiedCellIndex, + originalCellIndex, + keep: async (changes: DetailedLineRangeMapping) => { + const [modifiedCellModel, originalCellModel] = await Promise.all([modifiedCellModelPromise, originalCellModelPromise]); + const entry = this.getOrCreateModifiedTextFileEntryForCell(modifiedCell, modifiedCellModel, originalCellModel); + return entry ? entry.keep(changes) : false; + }, + undo: async (changes: DetailedLineRangeMapping) => { + const [modifiedCellModel, originalCellModel] = await Promise.all([modifiedCellModelPromise, originalCellModelPromise]); + const entry = this.getOrCreateModifiedTextFileEntryForCell(modifiedCell, modifiedCellModel, originalCellModel); + return entry ? entry.undo(changes) : false; + }, + modifiedModel: new ObservablePromise(modifiedCellModelPromise), + originalModel: new ObservablePromise(originalCellModelPromise), + diff + }; + + return unchangedCell; + + } + createInsertedCellDiffInfo(modifiedCellIndex: number): ICellDiffInfo { + const cell = this.modifiedModel.cells[modifiedCellIndex]; + const lines = cell.getValue().split(/\r?\n/); + const originalRange = new Range(1, 0, 1, 0); + const modifiedRange = new Range(1, 0, lines.length, lines[lines.length - 1].length); + const innerChanges = new RangeMapping(originalRange, modifiedRange); + const changes = [new DetailedLineRangeMapping(new LineRange(1, 1), new LineRange(1, lines.length), [innerChanges])]; + // When a new cell is inserted, we use the ChatEditingCodeEditorIntegration to handle the edits. + // & to also display undo/redo and decorations. + // However that needs a modified and original model. + // For inserted cells there's no original model, so we create a new empty text model and pass that as the original. + const originalModelUri = this.modifiedModel.uri.with({ query: (ChatEditingModifiedNotebookEntry.NewModelCounter++).toString(), scheme: 'emptyCell' }); + const originalModel = this.modelService.getModel(originalModelUri) || this._register(this.modelService.createModel('', null, originalModelUri)); + this.modifiedToOriginalCell.set(cell.uri, originalModelUri); + const keep = async () => { + this._applyEditsSync(() => this.keepPreviouslyInsertedCell(cell)); + this.computeStateAfterAcceptingRejectingChanges(true); + return true; + }; + const undo = async () => { + this._applyEditsSync(() => this.undoPreviouslyInsertedCell(cell)); + this.computeStateAfterAcceptingRejectingChanges(false); + return true; + }; + this.resolveCellModel(cell.uri).then(modifiedModel => { + // We want decorators for the cell just as we display decorators for modified cells. + // This way we have the ability to accept/reject the entire cell. + this.getOrCreateModifiedTextFileEntryForCell(cell, modifiedModel, originalModel); + }); + return { + type: 'insert' as const, + originalCellIndex: undefined, + modifiedCellIndex: modifiedCellIndex, + keep, + undo, + modifiedModel: new ObservablePromise(this.resolveCellModel(cell.uri)), + originalModel: new ObservablePromise(Promise.resolve(originalModel)), + diff: observableValue('deletedCellDiff', { + changes, + identical: false, + moves: [], + quitEarly: false, + }) + } satisfies ICellDiffInfo; + } + createDeleteCellDiffInfo(originalCellIndex: number): ICellDiffInfo { + const originalCell = this.originalModel.cells[originalCellIndex]; + const lines = new Array(originalCell.textBuffer.getLineCount()).fill(0).map((_, i) => originalCell.textBuffer.getLineContent(i + 1)); + const originalRange = new Range(1, 0, lines.length, lines[lines.length - 1].length); + const modifiedRange = new Range(1, 0, 1, 0); + const innerChanges = new RangeMapping(modifiedRange, originalRange); + const changes = [new DetailedLineRangeMapping(new LineRange(1, lines.length), new LineRange(1, 1), [innerChanges])]; + const modifiedModelUri = this.modifiedModel.uri.with({ query: (ChatEditingModifiedNotebookEntry.NewModelCounter++).toString(), scheme: 'emptyCell' }); + const modifiedModel = this.modelService.getModel(modifiedModelUri) || this._register(this.modelService.createModel('', null, modifiedModelUri)); + const keep = async () => { + this._applyEditsSync(() => this.keepPreviouslyDeletedCell(this.originalModel.cells.indexOf(originalCell))); + this.computeStateAfterAcceptingRejectingChanges(true); + return true; + }; + const undo = async () => { + this._applyEditsSync(() => this.undoPreviouslyDeletedCell(this.originalModel.cells.indexOf(originalCell), originalCell)); + this.computeStateAfterAcceptingRejectingChanges(false); + return true; + }; + + // This will be deleted. + return { + type: 'delete' as const, + modifiedCellIndex: undefined, + originalCellIndex, + originalModel: new ObservablePromise(this.resolveCellModel(originalCell.uri)), + modifiedModel: new ObservablePromise(Promise.resolve(modifiedModel)), + keep, + undo, + diff: observableValue('cellDiff', { + changes, + identical: false, + moves: [], + quitEarly: false, + }) + } satisfies ICellDiffInfo; + } + + private undoPreviouslyInsertedCell(cell: NotebookCellTextModel) { + const index = this.modifiedModel.cells.indexOf(cell); + const diffs = adjustCellDiffForRevertingAnInsertedCell(index, + this._cellsDiffInfo.get(), + this.modifiedModel.applyEdits.bind(this.modifiedModel)); + this.disposeDeletedCellEntries(); + this.updateCellDiffInfo(diffs, undefined); + } + + private keepPreviouslyInsertedCell(cell: NotebookCellTextModel) { + const modifiedCellIndex = this.modifiedModel.cells.indexOf(cell); + if (modifiedCellIndex === -1) { + // Not possible. + return; + } + const cellToInsert: ICellDto2 = { + cellKind: cell.cellKind, + language: cell.language, + metadata: cell.metadata, + outputs: cell.outputs, + source: cell.getValue(), + mime: cell.mime, + internalMetadata: { + cellId: cell.internalMetadata.cellId + } + }; + this.cellEntryMap.get(cell.uri)?.dispose(); + this.cellEntryMap.delete(cell.uri); + const cellDiffs = adjustCellDiffForKeepingAnInsertedCell( + modifiedCellIndex, + this._cellsDiffInfo.get().slice(), + cellToInsert, + this.originalModel.applyEdits.bind(this.originalModel), + this.createModifiedCellDiffInfo.bind(this) + ); + this.updateCellDiffInfo(cellDiffs, undefined); + } + + private undoPreviouslyDeletedCell(deletedOriginalIndex: number, originalCell: NotebookCellTextModel) { + const cellToInsert: ICellDto2 = { + cellKind: originalCell.cellKind, + language: originalCell.language, + metadata: originalCell.metadata, + outputs: originalCell.outputs, + source: originalCell.getValue(), + mime: originalCell.mime, + internalMetadata: { + cellId: originalCell.internalMetadata.cellId + } + }; + const cellDiffs = adjustCellDiffForRevertingADeletedCell( + deletedOriginalIndex, + this._cellsDiffInfo.get(), + cellToInsert, + this.modifiedModel.applyEdits.bind(this.modifiedModel), + this.createModifiedCellDiffInfo.bind(this) + ); + this.updateCellDiffInfo(cellDiffs, undefined); + } + + + private keepPreviouslyDeletedCell(deletedOriginalIndex: number) { + // Delete this cell from original as well. + const edit: ICellReplaceEdit = { cells: [], count: 1, editType: CellEditType.Replace, index: deletedOriginalIndex, }; + this.originalModel.applyEdits([edit], true, undefined, () => undefined, undefined, true); + const diffs = sortCellChanges(this._cellsDiffInfo.get()) + .filter(d => !(d.type === 'delete' && d.originalCellIndex === deletedOriginalIndex)) + .map(diff => { + if (diff.type !== 'insert' && diff.originalCellIndex > deletedOriginalIndex) { + return { + ...diff, + originalCellIndex: diff.originalCellIndex - 1, + }; + } + return diff; + }); + this.updateCellDiffInfo(diffs, undefined); + } + + private async _applyEdits(operation: () => Promise) { + // make the actual edit + this._isEditFromUs = true; + try { + await operation(); + } finally { + this._isEditFromUs = false; + } + } + + private _applyEditsSync(operation: () => void) { + // make the actual edit + this._isEditFromUs = true; + try { + operation(); + } finally { + this._isEditFromUs = false; + } } override createSnapshot(requestId: string | undefined, undoStop: string | undefined): ISnapshotEntry { - throw new Error('Method not implemented.'); + return { + resource: this.modifiedURI, + languageId: SnapshotLanguageId, + snapshotUri: getNotebookSnapshotFileURI(this._telemetryInfo.sessionId, requestId, undoStop, this.modifiedURI.path, this.modifiedModel.viewType), + original: createSnapshot(this.originalModel, this.transientOptions, this.configurationService), + current: createSnapshot(this.modifiedModel, this.transientOptions, this.configurationService), + originalToCurrentEdit: OffsetEdit.empty, + state: this.state.get(), + telemetryInfo: this.telemetryInfo, + }; } override equalsSnapshot(snapshot: ISnapshotEntry | undefined): boolean { - throw new Error('Method not implemented.'); + return !!snapshot && + isEqual(this.modifiedURI, snapshot.resource) && + this.state.get() === snapshot.state && + new SnapshotComparer(snapshot.original).isEqual(this.originalModel) && + new SnapshotComparer(snapshot.current).isEqual(this.modifiedModel); + } override restoreFromSnapshot(snapshot: ISnapshotEntry): void { - throw new Error('Method not implemented.'); + this.updateCellDiffInfo([], undefined); + this._stateObs.set(snapshot.state, undefined); + restoreSnapshot(this.originalModel, snapshot.original); + this.restoreSnapshotInModifiedModel(snapshot.current); + this.initializeModelsFromDiff(); } override resetToInitialContent(): void { - throw new Error('Method not implemented.'); + this.updateCellDiffInfo([], undefined); + this.restoreSnapshotInModifiedModel(this.initialContent); + this.initializeModelsFromDiff(); + } + + private restoreSnapshotInModifiedModel(snapshot: string) { + if (snapshot === createSnapshot(this.modifiedModel, this.transientOptions, this.configurationService)) { + return; + } + + this._applyEditsSync(() => { + // See private _setDocValue in chatEditingModifiedDocumentEntry.ts + this.modifiedModel.pushStackElement(); + restoreSnapshot(this.modifiedModel, snapshot); + this.modifiedModel.pushStackElement(); + }); + } + + private async resolveCellModel(cellURI: URI): Promise { + const cell = this.originalModel.cells.concat(this.modifiedModel.cells).find(cell => isEqual(cell.uri, cellURI)); + if (!cell) { + throw new Error('Cell not found'); + } + if (cell.textModel) { + return cell.textModel; + } + return this._register(await this.textModelService.createModelReference(cell.uri)).object.textEditorModel; + } + + getOrCreateModifiedTextFileEntryForCell(cell: NotebookCellTextModel, modifiedCellModel: ITextModel, originalCellModel: ITextModel): ChatEditingNotebookCellEntry | undefined { + let cellEntry = this.cellEntryMap.get(cell.uri); + if (cellEntry) { + return cellEntry; + } + + const disposables = new DisposableStore(); + cellEntry = this._register(this._instantiationService.createInstance(ChatEditingNotebookCellEntry, this.modifiedResourceRef.object.resource, cell, modifiedCellModel, originalCellModel, disposables)); + this.cellEntryMap.set(cell.uri, cellEntry); + disposables.add(autorun(r => { + if (this.modifiedModel.cells.indexOf(cell) === -1) { + return; + } + const diffs = this.cellsDiffInfo.get().slice(); + const index = this.modifiedModel.cells.indexOf(cell); + let entry = diffs.find(entry => entry.modifiedCellIndex === index); + if (!entry) { + // Not possible. + return; + } + const entryIndex = diffs.indexOf(entry); + entry.diff.set(cellEntry.diffInfo.read(r), undefined); + if (cellEntry.diffInfo.get().identical && entry.type === 'modified') { + entry = { + ...entry, + type: 'unchanged', + }; + } + if (!cellEntry.diffInfo.get().identical && entry.type === 'unchanged') { + entry = { + ...entry, + type: 'modified', + }; + } + diffs.splice(entryIndex, 1, { ...entry }); + + transaction(tx => { + this.updateCellDiffInfo(diffs, tx); + }); + })); + + disposables.add(autorun(r => { + if (this.modifiedModel.cells.indexOf(cell) === -1) { + return; + } + + const cellState = cellEntry.state.read(r); + if (cellState === WorkingSetEntryState.Accepted) { + this.computeStateAfterAcceptingRejectingChanges(true); + } else if (cellState === WorkingSetEntryState.Rejected) { + this.computeStateAfterAcceptingRejectingChanges(false); + } + })); + + return cellEntry; } } diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingServiceImpl.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingServiceImpl.ts index 6f31c9083355..40e2e0d81ea2 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingServiceImpl.ts @@ -4,7 +4,6 @@ *--------------------------------------------------------------------------------------------*/ import { coalesce, compareBy, delta } from '../../../../../base/common/arrays.js'; -import { findLastIdx } from '../../../../../base/common/arraysFind.js'; import { CancellationToken } from '../../../../../base/common/cancellation.js'; import { Codicon } from '../../../../../base/common/codicons.js'; import { ErrorNoTelemetry } from '../../../../../base/common/errors.js'; @@ -33,11 +32,12 @@ import { IEditorService } from '../../../../services/editor/common/editorService import { IExtensionService } from '../../../../services/extensions/common/extensions.js'; import { ILifecycleService } from '../../../../services/lifecycle/common/lifecycle.js'; import { IMultiDiffSourceResolver, IMultiDiffSourceResolverService, IResolvedMultiDiffSource, MultiDiffEditorItem } from '../../../multiDiffEditor/browser/multiDiffSourceResolverService.js'; -import { CellUri, ICellEditOperation } from '../../../notebook/common/notebookCommon.js'; -import { ChatAgentLocation, IChatAgentService } from '../../common/chatAgents.js'; +import { CellUri } from '../../../notebook/common/notebookCommon.js'; +import { IChatAgentService } from '../../common/chatAgents.js'; import { CHAT_EDITING_MULTI_DIFF_SOURCE_RESOLVER_SCHEME, chatEditingAgentSupportsReadonlyReferencesContextKey, chatEditingResourceContextKey, ChatEditingSessionState, chatEditingSnapshotScheme, IChatEditingService, IChatEditingSession, IChatRelatedFile, IChatRelatedFilesProvider, IModifiedFileEntry, inChatEditingSessionContextKey, IStreamingEdits, WorkingSetEntryState } from '../../common/chatEditingService.js'; -import { IChatResponseModel } from '../../common/chatModel.js'; +import { IChatResponseModel, isCellTextEditOperation } from '../../common/chatModel.js'; import { IChatService } from '../../common/chatService.js'; +import { ChatAgentLocation } from '../../common/constants.js'; import { AbstractChatEditingModifiedFileEntry } from './chatEditingModifiedFileEntry.js'; import { ChatEditingSession } from './chatEditingSession.js'; import { ChatEditingSnapshotTextModelContentProvider, ChatEditingTextModelContentProvider } from './chatEditingTextModelContentProviders.js'; @@ -245,11 +245,6 @@ export class ChatEditingService extends Disposable implements IChatEditingServic // each of them. Note that text edit groups can be updated // multiple times during the process of response streaming. const editsSeen: ({ seen: number; streaming: IStreamingEdits } | undefined)[] = []; - // Same deal as above, but for code block URIs. Code block URIs preceed a - // text edit group, and are used to allow us to start an editing state - // prior to the code actually streaming in. When a new edit block it seen, - // it'll look back and 'claim' the last unclaimed matchin codeblock URI. - const codeBlockUrisSeen: ({ uri: URI; streaming?: IStreamingEdits } | undefined)[] = []; const editedFilesExist = new ResourceMap>(); const ensureEditorOpen = (partUri: URI) => { @@ -263,7 +258,7 @@ export class ChatEditingService extends Disposable implements IChatEditingServic return; } const activeUri = this._editorService.activeEditorPane?.input.resource; - const inactive = activeUri && session.workingSet.has(activeUri); + const inactive = Boolean(activeUri && session.entries.get().find(entry => isEqual(activeUri, entry.modifiedURI))); this._editorService.openEditor({ resource: uri, options: { inactive, preserveFocus: true, pinned: true } }); })); }; @@ -295,28 +290,16 @@ export class ChatEditingService extends Disposable implements IChatEditingServic continue; } - if (part.kind === 'codeblockUri') { - ensureEditorOpen(part.uri); - codeBlockUrisSeen[i] ??= { uri: part.uri, streaming: session.startStreamingEdits(part.uri, responseModel, undoStop) }; - continue; - } - if (part.kind !== 'textEditGroup' && part.kind !== 'notebookEditGroup') { continue; } + ensureEditorOpen(part.uri); // get new edits and start editing session let entry = editsSeen[i]; if (!entry) { - const codeBlockIndex = findLastIdx(codeBlockUrisSeen, e => e?.streaming && isEqual(e.uri, part.uri), i - 1); - if (codeBlockIndex !== -1) { - entry = { seen: 0, streaming: codeBlockUrisSeen[codeBlockIndex]!.streaming! }; - codeBlockUrisSeen[codeBlockIndex]!.streaming = undefined; - } else { - entry = { seen: 0, streaming: session.startStreamingEdits(part.uri, responseModel, undoStop) }; - } - + entry = { seen: 0, streaming: session.startStreamingEdits(CellUri.parse(part.uri)?.notebook ?? part.uri, responseModel, undoStop) }; editsSeen[i] = entry; } @@ -326,7 +309,16 @@ export class ChatEditingService extends Disposable implements IChatEditingServic if (newEdits.length > 0 || isFirst) { if (part.kind === 'notebookEditGroup') { - entry.streaming.pushNotebook(newEdits as ICellEditOperation[]); + newEdits.forEach(edit => { + if (TextEdit.isTextEdit(edit)) { + // Not possible, as Notebooks would have a different type. + return; + } else if (isCellTextEditOperation(edit)) { + entry.streaming.pushNotebookCellText(edit.uri, [edit.edit]); + } else { + entry.streaming.pushNotebook([edit]); + } + }); } else if (part.kind === 'textEditGroup') { entry.streaming.pushText(newEdits as TextEdit[]); } diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingSession.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingSession.ts index a8f60cd6404e..714f256b7bea 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingSession.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingSession.ts @@ -10,9 +10,8 @@ import { BugIndicatingError } from '../../../../../base/common/errors.js'; import { Emitter } from '../../../../../base/common/event.js'; import { StringSHA1 } from '../../../../../base/common/hash.js'; import { Iterable } from '../../../../../base/common/iterator.js'; -import { Disposable, DisposableMap, DisposableStore, dispose } from '../../../../../base/common/lifecycle.js'; -import { ResourceMap, ResourceSet } from '../../../../../base/common/map.js'; -import { Schemas } from '../../../../../base/common/network.js'; +import { Disposable, DisposableMap, dispose } from '../../../../../base/common/lifecycle.js'; +import { ResourceMap } from '../../../../../base/common/map.js'; import { asyncTransaction, autorun, derived, derivedOpts, derivedWithStore, IObservable, IReader, ITransaction, ObservablePromise, observableValue, transaction } from '../../../../../base/common/observable.js'; import { autorunDelta, autorunIterableDelta } from '../../../../../base/common/observableInternal/autorun.js'; import { isEqual, joinPath } from '../../../../../base/common/resources.js'; @@ -49,6 +48,9 @@ import { AbstractChatEditingModifiedFileEntry, IModifiedEntryTelemetryInfo, ISna import { ChatEditingModifiedDocumentEntry } from './chatEditingModifiedDocumentEntry.js'; import { ChatEditingTextModelContentProvider } from './chatEditingTextModelContentProviders.js'; import { CellUri, ICellEditOperation } from '../../../notebook/common/notebookCommon.js'; +import { ChatEditingModifiedNotebookEntry } from './chatEditingModifiedNotebookEntry.js'; +import { CancellationToken } from '../../../../../base/common/cancellation.js'; +import { ChatEditingModifiedNotebookDiff } from './notebook/chatEditingModifiedNotebookDiff.js'; const STORAGE_CONTENTS_FOLDER = 'contents'; const STORAGE_STATE_FILE = 'state.json'; @@ -76,7 +78,7 @@ class ThrottledSequencer extends Sequencer { const p1 = promiseTask(); const p2 = noDelay ? Promise.resolve(undefined) - : timeout(this._minDuration); + : timeout(this._minDuration, CancellationToken.None); const [result] = await Promise.all([p1, p2]); return result; @@ -139,19 +141,6 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio } private _workingSet = new ResourceMap(); - get workingSet() { - this._assertNotDisposed(); - - // Return here a reunion between the AI modified entries and the user built working set - const result = new ResourceMap(this._workingSet); - for (const entry of this._entriesObs.get()) { - result.set(entry.modifiedURI, { state: entry.state.get() }); - } - - return result; - } - - private _removedTransientEntries = new ResourceSet(); private _editorPane: MultiDiffEditor | undefined; @@ -239,6 +228,7 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio } private _getEntry(uri: URI): AbstractChatEditingModifiedFileEntry | undefined { + uri = CellUri.parse(uri)?.notebook ?? uri; return this._entriesObs.get().find(e => isEqual(e.modifiedURI, uri)); } @@ -247,6 +237,7 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio } public readEntry(uri: URI, reader: IReader | undefined): IModifiedFileEntry | undefined { + uri = CellUri.parse(uri)?.notebook ?? uri; return this._entriesObs.read(reader).find(e => isEqual(e.modifiedURI, uri)); } @@ -299,8 +290,13 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio return this._linearHistory.get().find(s => s.requestId === requestId); } - private _findEditStop(requestId: string, undoStop: string | undefined): IChatEditingSessionStop | undefined { - return this._findSnapshot(requestId)?.stops.find(s => s.stopId === undoStop); + private _findEditStop(requestId: string, undoStop: string | undefined) { + const snapshot = this._findSnapshot(requestId); + if (!snapshot) { + return undefined; + } + const idx = snapshot.stops.findIndex(s => s.stopId === undoStop); + return idx === -1 ? undefined : { stop: snapshot.stops[idx], snapshot, historyIndex: snapshot.startIndex + idx }; } private _ensurePendingSnapshot() { @@ -343,8 +339,13 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio return; } - entriesContent.read(reader); // trigger re-diffing when contents change + const entries = entriesContent.read(reader); // trigger re-diffing when contents change + if (entries?.before && ChatEditingModifiedNotebookEntry.canHandleSnapshot(entries.before)) { + const diffService = this._instantiationService.createInstance(ChatEditingModifiedNotebookDiff, entries.before, entries.after); + return new ObservablePromise(diffService.computeDiff()); + + } const ignoreTrimWhitespace = this._ignoreTrimWhitespaceObservable.read(reader); const promise = this._editorWorkerService.computeDiff( refs[0].object.textEditorModel.uri, @@ -396,7 +397,6 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio return [entriesValue.before.snapshotUri, entriesValue.after.snapshotUri]; }); - // todo@DonJayamanne support notebooks here too const diff = this._entryDiffBetweenTextStops(entries, modelUrisObservable); return derived(reader => { @@ -417,10 +417,8 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio public createSnapshot(requestId: string, undoStop: string | undefined): void { const snapshot = this._createSnapshot(requestId, undoStop); - for (const [uri, data] of this._workingSet) { - if (data.state !== WorkingSetEntryState.Suggested) { - this._workingSet.set(uri, { state: WorkingSetEntryState.Sent, isMarkedReadonly: data.isMarkedReadonly }); - } + for (const [uri, _] of this._workingSet) { + this._workingSet.set(uri, { state: WorkingSetEntryState.Sent }); } const linearHistoryPtr = this._linearHistoryIndex.get(); @@ -461,15 +459,15 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio }; } - public async getSnapshotModel(requestId: string, undoStop: string | undefined, snapshotUri: URI): Promise { + public getSnapshot(requestId: string, undoStop: string | undefined, snapshotUri: URI): ISnapshotEntry | undefined { const entries = undoStop === POST_EDIT_STOP_ID ? this._findSnapshot(requestId)?.postEdit - : this._findEditStop(requestId, undoStop)?.entries; - if (!entries) { - return null; - } + : this._findEditStop(requestId, undoStop)?.stop.entries; + return entries && [...entries.values()].find((e) => isEqual(e.snapshotUri, snapshotUri)); + } - const snapshotEntry = [...entries.values()].find((e) => isEqual(e.snapshotUri, snapshotUri)); + public async getSnapshotModel(requestId: string, undoStop: string | undefined, snapshotUri: URI): Promise { + const snapshotEntry = this.getSnapshot(requestId, undoStop, snapshotUri); if (!snapshotEntry) { return null; } @@ -488,10 +486,14 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio private _pendingSnapshot: IChatEditingSessionStop | undefined; public async restoreSnapshot(requestId: string | undefined, stopId: string | undefined): Promise { if (requestId !== undefined) { - const snapshot = this._findEditStop(requestId, stopId); - if (snapshot) { + const stopRef = this._findEditStop(requestId, stopId); + if (stopRef) { this._ensurePendingSnapshot(); - await this._restoreSnapshot(snapshot, undefined); + await asyncTransaction(async tx => { + this._linearHistoryIndex.set(stopRef.historyIndex, tx); + await this._restoreSnapshot(stopRef.stop, tx); + }); + this._updateRequestHiddenState(); } } else { const pendingSnapshot = this._pendingSnapshot; @@ -544,9 +546,6 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio const state = this._workingSet.get(uri); if (state !== undefined) { didRemoveUris = this._workingSet.delete(uri) || didRemoveUris; - if (reason === WorkingSetEntryRemovalReason.User && state.state === WorkingSetEntryState.Suggested) { - this._removedTransientEntries.add(uri); - } } } @@ -557,22 +556,6 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio this._onDidChange.fire(ChatEditingSessionChangeType.WorkingSet); } - markIsReadonly(resource: URI, isReadonly?: boolean): void { - const entry = this._workingSet.get(resource); - if (entry) { - if (entry.state === WorkingSetEntryState.Suggested) { - entry.state = WorkingSetEntryState.Attached; - } - entry.isMarkedReadonly = isReadonly ?? !entry.isMarkedReadonly; - } else { - this._workingSet.set(resource, { - state: WorkingSetEntryState.Attached, - isMarkedReadonly: isReadonly ?? true - }); - } - this._onDidChange.fire(ChatEditingSessionChangeType.WorkingSet); - } - private _assertNotDisposed(): void { if (this._state.get() === ChatEditingSessionState.Disposed) { throw new BugIndicatingError(`Cannot access a disposed editing session`); @@ -705,13 +688,20 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio let didComplete = false; return { - pushText: edits => { + pushText: (edits) => { sequencer.queue(async () => { if (!this.isDisposed) { await this._acceptEdits(resource, edits, false, responseModel); } }); }, + pushNotebookCellText: (cell, edits) => { + sequencer.queue(async () => { + if (!this.isDisposed) { + await this._acceptEdits(cell, edits, false, responseModel); + } + }); + }, pushNotebook: edits => { sequencer.queue(async () => { if (!this.isDisposed) { @@ -736,61 +726,6 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio }; } - private _trackUntitledWorkingSetEntry(resource: URI) { - if (resource.scheme !== Schemas.untitled) { - return; - } - const untitled = this._textFileService.untitled.get(resource); - if (!untitled) { // Shouldn't happen - return; - } - - // Track this file until - // 1. it is removed from the working set - // 2. it is closed - // 3. we are disposed - const store = new DisposableStore(); - store.add(this.onDidChange(e => { - if (e === ChatEditingSessionChangeType.WorkingSet && !this._workingSet.get(resource)) { - // The user has removed the file from the working set - store.dispose(); - } - })); - store.add(this._textFileService.untitled.onDidSave(e => { - const existing = this._workingSet.get(resource); - if (isEqual(e.source, resource) && existing) { - this._workingSet.delete(resource); - this._workingSet.set(e.target, existing); - store.dispose(); - this._onDidChange.fire(ChatEditingSessionChangeType.WorkingSet); - } - })); - store.add(this._editorService.onDidCloseEditor((e) => { - if (isEqual(e.editor.resource, resource)) { - this._workingSet.delete(resource); - store.dispose(); - this._onDidChange.fire(ChatEditingSessionChangeType.WorkingSet); - } - })); - this._store.add(store); - } - - addFileToWorkingSet(resource: URI, description?: string, proposedState?: WorkingSetEntryState.Suggested): void { - const state = this._workingSet.get(resource); - if (proposedState === WorkingSetEntryState.Suggested) { - if (state !== undefined || this._removedTransientEntries.has(resource)) { - return; - } - this._workingSet.set(resource, { description, state: WorkingSetEntryState.Suggested }); - this._trackUntitledWorkingSetEntry(resource); - this._onDidChange.fire(ChatEditingSessionChangeType.WorkingSet); - } else if (state === undefined || state.state === WorkingSetEntryState.Suggested) { - this._workingSet.set(resource, { description, state: WorkingSetEntryState.Attached }); - this._trackUntitledWorkingSetEntry(resource); - this._onDidChange.fire(ChatEditingSessionChangeType.WorkingSet); - } - } - private _getHistoryEntryByLinearIndex(index: number) { const history = this._linearHistory.get(); const searchedIndex = binarySearch2(history.length, (e) => history[e].startIndex - index); @@ -930,7 +865,7 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio private async _acceptEdits(resource: URI, textEdits: (TextEdit | ICellEditOperation)[], isLastEdits: boolean, responseModel: IChatResponseModel): Promise { const entry = await this._getOrCreateModifiedFileEntry(resource, this._getTelemetryInfoForModel(responseModel)); - entry.acceptAgentEdits(resource, textEdits, isLastEdits, responseModel); + await entry.acceptAgentEdits(resource, textEdits, isLastEdits, responseModel); } private _getTelemetryInfoForModel(responseModel: IChatResponseModel): IModifiedEntryTelemetryInfo { @@ -1020,12 +955,17 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio } private async _createModifiedFileEntry(resource: URI, telemetryInfo: IModifiedEntryTelemetryInfo, mustExist = false, initialContent: string | undefined): Promise { + const multiDiffEntryDelegate = { collapse: (transaction: ITransaction | undefined) => this._collapse(resource, transaction) }; + const chatKind = mustExist ? ChatEditKind.Created : ChatEditKind.Modified; + const notebookUri = CellUri.parse(resource)?.notebook || resource; try { - const ref = await this._textModelService.createModelReference(resource); - const ctor = this._notebookService.hasSupportedNotebooks(resource) - ? ChatEditingModifiedDocumentEntry // TODO@DonJayamanne use NotebookEntry once ready - : ChatEditingModifiedDocumentEntry; - return this._instantiationService.createInstance(ctor, ref, { collapse: (transaction: ITransaction | undefined) => this._collapse(resource, transaction) }, telemetryInfo, mustExist ? ChatEditKind.Created : ChatEditKind.Modified, initialContent); + // If a notebook isn't open, then use the old synchronization approach. + if (this._notebookService.hasSupportedNotebooks(notebookUri) && (this._notebookService.getNotebookTextModel(notebookUri) || ChatEditingModifiedNotebookEntry.canHandleSnapshotContent(initialContent))) { + return await ChatEditingModifiedNotebookEntry.create(notebookUri, multiDiffEntryDelegate, telemetryInfo, chatKind, initialContent, this._instantiationService); + } else { + const ref = await this._textModelService.createModelReference(resource); + return this._instantiationService.createInstance(ChatEditingModifiedDocumentEntry, ref, multiDiffEntryDelegate, telemetryInfo, chatKind, initialContent); + } } catch (err) { if (mustExist) { throw err; @@ -1033,7 +973,11 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio // this file does not exist yet, create it and try again await this._bulkEditService.apply({ edits: [{ newResource: resource }] }); this._editorService.openEditor({ resource, options: { inactive: true, preserveFocus: true, pinned: true } }); - return this._createModifiedFileEntry(resource, telemetryInfo, true, initialContent); + if (this._notebookService.hasSupportedNotebooks(notebookUri)) { + return await ChatEditingModifiedNotebookEntry.create(resource, multiDiffEntryDelegate, telemetryInfo, ChatEditKind.Created, initialContent, this._instantiationService); + } else { + return this._createModifiedFileEntry(resource, telemetryInfo, true, initialContent); + } } } @@ -1125,7 +1069,7 @@ class ChatEditingSessionStorage { this._logService.debug(`chatEditingSession: Restoring editing session at ${stateFilePath.toString()}`); const stateFileContent = await this._fileService.readFile(stateFilePath); const data = JSON.parse(stateFileContent.value.toString()) as IChatEditingSessionDTO; - if (data.version !== STORAGE_VERSION) { + if (!COMPATIBLE_STORAGE_VERSIONS.includes(data.version)) { return undefined; } @@ -1322,7 +1266,8 @@ interface IModifiedEntryTelemetryInfoDTO { type ResourceMapDTO = [string, T][]; -const STORAGE_VERSION = 1; +const COMPATIBLE_STORAGE_VERSIONS = [1, 2]; +const STORAGE_VERSION = 2; /** Old history uses IChatEditingSessionSnapshotDTO, new history uses IChatEditingSessionSnapshotDTO. */ interface IChatEditingSessionDTO { diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingModifiedNotebookDiff.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingModifiedNotebookDiff.ts new file mode 100644 index 000000000000..5521a1533185 --- /dev/null +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingModifiedNotebookDiff.ts @@ -0,0 +1,69 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { DisposableStore } from '../../../../../../base/common/lifecycle.js'; +import { computeDiff } from '../../../../notebook/common/notebookDiff.js'; +import { INotebookEditorModelResolverService } from '../../../../notebook/common/notebookEditorModelResolverService.js'; +import { INotebookLoggingService } from '../../../../notebook/common/notebookLoggingService.js'; +import { INotebookEditorWorkerService } from '../../../../notebook/common/services/notebookWorkerService.js'; +import { IEditSessionEntryDiff } from '../../../common/chatEditingService.js'; +import { ISnapshotEntry } from '../chatEditingModifiedFileEntry.js'; + +export class ChatEditingModifiedNotebookDiff { + static NewModelCounter: number = 0; + constructor( + private readonly original: ISnapshotEntry, + private readonly modified: ISnapshotEntry, + @INotebookEditorWorkerService private readonly notebookEditorWorkerService: INotebookEditorWorkerService, + @INotebookLoggingService private readonly notebookLoggingService: INotebookLoggingService, + @INotebookEditorModelResolverService private readonly notebookEditorModelService: INotebookEditorModelResolverService, + ) { + + } + + async computeDiff(): Promise { + + let added = 0; + let removed = 0; + + const disposables = new DisposableStore(); + try { + const [modifiedRef, originalRef] = await Promise.all([ + this.notebookEditorModelService.resolve(this.modified.snapshotUri), + this.notebookEditorModelService.resolve(this.original.snapshotUri) + ]); + disposables.add(modifiedRef); + disposables.add(originalRef); + const notebookDiff = await this.notebookEditorWorkerService.computeDiff(this.original.snapshotUri, this.modified.snapshotUri); + const result = computeDiff(originalRef.object.notebook, modifiedRef.object.notebook, notebookDiff); + result.cellDiffInfo.forEach(diff => { + switch (diff.type) { + case 'modified': + case 'insert': + added++; + break; + case 'delete': + removed++; + break; + default: + break; + } + }); + } catch (e) { + this.notebookLoggingService.error('Notebook Chat', 'Error computing diff:\n' + e); + } finally { + disposables.dispose(); + } + + return { + added, + removed, + identical: added === 0 && removed === 0, + quitEarly: false, + modifiedURI: this.modified.snapshotUri, + originalURI: this.original.snapshotUri, + }; + } +} diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingModifiedNotebookSnapshot.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingModifiedNotebookSnapshot.ts new file mode 100644 index 000000000000..5066d63b7c18 --- /dev/null +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingModifiedNotebookSnapshot.ts @@ -0,0 +1,182 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { decodeBase64, encodeBase64, VSBuffer } from '../../../../../../base/common/buffer.js'; +import { filter } from '../../../../../../base/common/objects.js'; +import { URI } from '../../../../../../base/common/uri.js'; +import { IConfigurationService } from '../../../../../../platform/configuration/common/configuration.js'; +import { SnapshotContext } from '../../../../../services/workingCopy/common/fileWorkingCopy.js'; +import { NotebookCellTextModel } from '../../../../notebook/common/model/notebookCellTextModel.js'; +import { NotebookTextModel } from '../../../../notebook/common/model/notebookTextModel.js'; +import { CellEditType, ICellDto2, ICellEditOperation, IOutputItemDto, NotebookData, NotebookSetting, TransientOptions } from '../../../../notebook/common/notebookCommon.js'; + +const BufferMarker = 'ArrayBuffer-4f56482b-5a03-49ba-8356-210d3b0c1c3d'; + +type ChatEditingSnapshotNotebookContentQueryData = { sessionId: string; requestId: string | undefined; undoStop: string | undefined; viewType: string }; +export const ChatEditingNotebookSnapshotScheme = 'chat-editing-notebook-snapshot-model'; + +export function getNotebookSnapshotFileURI(chatSessionId: string, requestId: string | undefined, undoStop: string | undefined, path: string, viewType: string): URI { + return URI.from({ + scheme: ChatEditingNotebookSnapshotScheme, + path, + query: JSON.stringify({ sessionId: chatSessionId, requestId: requestId ?? '', undoStop: undoStop ?? '', viewType } satisfies ChatEditingSnapshotNotebookContentQueryData), + }); +} + +export function parseNotebookSnapshotFileURI(resource: URI): ChatEditingSnapshotNotebookContentQueryData { + const data: ChatEditingSnapshotNotebookContentQueryData = JSON.parse(resource.query); + return { sessionId: data.sessionId ?? '', requestId: data.requestId ?? '', undoStop: data.undoStop ?? '', viewType: data.viewType }; +} + +export function createSnapshot(notebook: NotebookTextModel, transientOptions: TransientOptions | undefined, configurationService: IConfigurationService): string { + const outputSizeLimit = configurationService.getValue(NotebookSetting.outputBackupSizeLimit) * 1024; + return serializeSnapshot(notebook.createSnapshot({ context: SnapshotContext.Backup, outputSizeLimit, transientOptions }), transientOptions); +} + +export function restoreSnapshot(notebook: NotebookTextModel, snapshot: string): void { + try { + const { transientOptions, data } = deserializeSnapshot(snapshot); + notebook.restoreSnapshot(data, transientOptions); + const edits: ICellEditOperation[] = []; + data.cells.forEach((cell, index) => { + const cellId = cell.internalMetadata?.cellId; + if (cellId) { + edits.push({ editType: CellEditType.PartialInternalMetadata, index, internalMetadata: { cellId } }); + } + }); + notebook.applyEdits(edits, true, undefined, () => undefined, undefined, false); + } + catch (ex) { + console.error('Error restoring Notebook snapshot', ex); + } +} + +export class SnapshotComparer { + private readonly data: NotebookData; + private readonly transientOptions: TransientOptions | undefined; + constructor(initialCotent: string) { + this.transientOptions = deserializeSnapshot(initialCotent).transientOptions; + this.data = deserializeSnapshot(initialCotent).data; + } + + isEqual(notebook: NotebookData | NotebookTextModel): boolean { + if (notebook.cells.length !== this.data.cells.length) { + return false; + } + const transientDocumentMetadata = this.transientOptions?.transientDocumentMetadata || {}; + const notebookMetadata = filter(notebook.metadata || {}, key => !transientDocumentMetadata[key]); + const comparerMetadata = filter(this.data.metadata || {}, key => !transientDocumentMetadata[key]); + // When comparing ignore transient items. + if (JSON.stringify(notebookMetadata) !== JSON.stringify(comparerMetadata)) { + return false; + } + const transientCellMetadata = this.transientOptions?.transientCellMetadata || {}; + for (let i = 0; i < notebook.cells.length; i++) { + const notebookCell = notebook.cells[i]; + const comparerCell = this.data.cells[i]; + if (notebookCell instanceof NotebookCellTextModel) { + if (!notebookCell.fastEqual(comparerCell, true)) { + return false; + } + } else { + if (notebookCell.cellKind !== comparerCell.cellKind) { + return false; + } + if (notebookCell.language !== comparerCell.language) { + return false; + } + if (notebookCell.mime !== comparerCell.mime) { + return false; + } + if (notebookCell.source !== comparerCell.source) { + return false; + } + if (!this.transientOptions?.transientOutputs && notebookCell.outputs.length !== comparerCell.outputs.length) { + return false; + } + // When comparing ignore transient items. + const cellMetadata = filter(notebookCell.metadata || {}, key => !transientCellMetadata[key]); + const comparerCellMetadata = filter(comparerCell.metadata || {}, key => !transientCellMetadata[key]); + if (JSON.stringify(cellMetadata) !== JSON.stringify(comparerCellMetadata)) { + return false; + } + + // When comparing ignore transient items. + if (JSON.stringify(sanitizeCellDto2(notebookCell, true, this.transientOptions)) !== JSON.stringify(sanitizeCellDto2(comparerCell, true, this.transientOptions))) { + return false; + } + } + } + + return true; + } +} + +function sanitizeCellDto2(cell: ICellDto2, ignoreInternalMetadata?: boolean, transientOptions?: TransientOptions): ICellDto2 { + const transientCellMetadata = transientOptions?.transientCellMetadata || {}; + const outputs = transientOptions?.transientOutputs ? [] : cell.outputs.map(output => { + // Ensure we're in full control of the data being stored. + // Possible we have classes instead of plain objects. + return { + outputId: output.outputId, + metadata: output.metadata, + outputs: output.outputs.map(item => { + return { + data: item.data, + mime: item.mime, + } satisfies IOutputItemDto; + }), + }; + }); + // Ensure we're in full control of the data being stored. + // Possible we have classes instead of plain objects. + return { + cellKind: cell.cellKind, + language: cell.language, + metadata: cell.metadata ? filter(cell.metadata, key => !transientCellMetadata[key]) : cell.metadata, + outputs, + mime: cell.mime, + source: cell.source, + collapseState: cell.collapseState, + internalMetadata: ignoreInternalMetadata ? undefined : cell.internalMetadata + } satisfies ICellDto2; +} + +function serializeSnapshot(data: NotebookData, transientOptions: TransientOptions | undefined): string { + const dataDto: NotebookData = { + // Never pass transient options, as we're after a backup here. + // Else we end up stripping outputs from backups. + // Whether its persisted or not is up to the serializer. + // However when reloading/restoring we need to preserve outputs. + cells: data.cells.map(cell => sanitizeCellDto2(cell)), + metadata: data.metadata, + }; + return JSON.stringify([ + JSON.stringify(transientOptions) + , JSON.stringify(dataDto, (_key, value) => { + if (value instanceof VSBuffer) { + return { + type: BufferMarker, + data: encodeBase64(value) + }; + } + return value; + }) + ]); +} + +export function deserializeSnapshot(snapshot: string): { transientOptions: TransientOptions | undefined; data: NotebookData } { + const [transientOptionsStr, dataStr] = JSON.parse(snapshot); + const transientOptions = transientOptionsStr ? JSON.parse(transientOptionsStr) as TransientOptions : undefined; + + const data: NotebookData = JSON.parse(dataStr, (_key, value) => { + if (value && value.type === BufferMarker) { + return decodeBase64(value.data); + } + return value; + }); + + return { transientOptions, data }; +} diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingNewNotebookContentEdits.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingNewNotebookContentEdits.ts new file mode 100644 index 000000000000..b516adce76ef --- /dev/null +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingNewNotebookContentEdits.ts @@ -0,0 +1,86 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { VSBuffer } from '../../../../../../base/common/buffer.js'; +import { TextEdit } from '../../../../../../editor/common/languages.js'; +import { NotebookTextModel } from '../../../../notebook/common/model/notebookTextModel.js'; +import { CellEditType, ICellEditOperation } from '../../../../notebook/common/notebookCommon.js'; +import { INotebookService } from '../../../../notebook/common/notebookService.js'; + + +/** + * When asking LLM to generate a new notebook, LLM might end up generating the notebook + * using the raw file format. + * E.g. assume we ask LLM to generate a new Github Issues notebook, LLM might end up + * genrating the notebook using the JSON format of github issues file. + * Such a format is not known to copilot extension and those are sent over as regular + * text edits for the Notebook URI. + * + * In such cases we should accumulate all of the edits, generate the content and deserialize the content + * into a notebook, then generate notebooke edits to insert these cells. + */ +export class ChatEditingNewNotebookContentEdits { + private readonly textEdits: TextEdit[] = []; + constructor( + private readonly notebook: NotebookTextModel, + @INotebookService private readonly _notebookService: INotebookService, + ) { + } + + acceptTextEdits(edits: TextEdit[]): void { + if (edits.length) { + this.textEdits.push(...edits); + } + } + + async generateEdits(): Promise { + if (this.notebook.cells.length) { + console.error(`Notebook edits not generated as notebook already has cells`); + return []; + } + const content = this.generateContent(); + if (!content) { + return []; + } + + const notebookEdits: ICellEditOperation[] = []; + try { + const { serializer } = await this._notebookService.withNotebookDataProvider(this.notebook.viewType); + const data = await serializer.dataToNotebook(VSBuffer.fromString(content)); + for (let i = 0; i < data.cells.length; i++) { + notebookEdits.push({ + editType: CellEditType.Replace, + index: i, + count: 0, + cells: [data.cells[i]] + }); + } + } catch (ex) { + console.error(`Failed to generate notebook edits from text edits ${content}`, ex); + return []; + } + + return notebookEdits; + } + + private generateContent() { + try { + return applyTextEdits(this.textEdits); + } catch (ex) { + console.error('Failed to generate content from text edits', ex); + return ''; + } + } +} + +function applyTextEdits(edits: TextEdit[]): string { + let output = ''; + for (const edit of edits) { + output = output.slice(0, edit.range.startColumn) + + edit.text + + output.slice(edit.range.endColumn); + } + return output; +} diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingNotebookCellEntry.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingNotebookCellEntry.ts new file mode 100644 index 000000000000..9418dc7c6bd0 --- /dev/null +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingNotebookCellEntry.ts @@ -0,0 +1,345 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { RunOnceScheduler } from '../../../../../../base/common/async.js'; +import { DisposableStore, toDisposable } from '../../../../../../base/common/lifecycle.js'; +import { ITransaction, IObservable, observableValue, autorun, transaction } from '../../../../../../base/common/observable.js'; +import { ObservableDisposable } from '../../../../../../base/common/observableDisposable.js'; +import { themeColorFromId } from '../../../../../../base/common/themables.js'; +import { URI } from '../../../../../../base/common/uri.js'; +import { EditOperation, ISingleEditOperation } from '../../../../../../editor/common/core/editOperation.js'; +import { OffsetEdit } from '../../../../../../editor/common/core/offsetEdit.js'; +import { Range } from '../../../../../../editor/common/core/range.js'; +import { IDocumentDiff, nullDocumentDiff } from '../../../../../../editor/common/diff/documentDiffProvider.js'; +import { DetailedLineRangeMapping } from '../../../../../../editor/common/diff/rangeMapping.js'; +import { TextEdit } from '../../../../../../editor/common/languages.js'; +import { IModelDeltaDecoration, ITextModel, MinimapPosition, OverviewRulerLane } from '../../../../../../editor/common/model.js'; +import { ModelDecorationOptions } from '../../../../../../editor/common/model/textModel.js'; +import { OffsetEdits } from '../../../../../../editor/common/model/textModelOffsetEdit.js'; +import { IEditorWorkerService } from '../../../../../../editor/common/services/editorWorker.js'; +import { IModelContentChangedEvent } from '../../../../../../editor/common/textModelEvents.js'; +import { IConfigurationService } from '../../../../../../platform/configuration/common/configuration.js'; +import { observableConfigValue } from '../../../../../../platform/observable/common/platformObservableUtils.js'; +import { editorSelectionBackground } from '../../../../../../platform/theme/common/colorRegistry.js'; +import { CellEditState } from '../../../../notebook/browser/notebookBrowser.js'; +import { INotebookEditorService } from '../../../../notebook/browser/services/notebookEditorService.js'; +import { NotebookCellTextModel } from '../../../../notebook/common/model/notebookCellTextModel.js'; +import { WorkingSetEntryState } from '../../../common/chatEditingService.js'; +import { IChatResponseModel } from '../../../common/chatModel.js'; +import { pendingRewriteMinimap } from '../chatEditingModifiedFileEntry.js'; + + +/** + * This is very closely similar to the ChatEditingModifiedDocumentEntry class. + * Most of the code has been borrowed from there, as a cell is effectively a document. + * Hence most of the same functionality applies. + */ +export class ChatEditingNotebookCellEntry extends ObservableDisposable { + private static readonly _lastEditDecorationOptions = ModelDecorationOptions.register({ + isWholeLine: true, + description: 'chat-last-edit', + className: 'chat-editing-last-edit-line', + marginClassName: 'chat-editing-last-edit', + overviewRuler: { + position: OverviewRulerLane.Full, + color: themeColorFromId(editorSelectionBackground) + }, + }); + + private static readonly _pendingEditDecorationOptions = ModelDecorationOptions.register({ + isWholeLine: true, + description: 'chat-pending-edit', + className: 'chat-editing-pending-edit', + minimap: { + position: MinimapPosition.Inline, + color: themeColorFromId(pendingRewriteMinimap) + } + }); + + + private _edit: OffsetEdit = OffsetEdit.empty; + private _isEditFromUs: boolean = false; + public get isEditFromUs(): boolean { + return this._isEditFromUs; + } + + private _allEditsAreFromUs: boolean = true; + public get allEditsAreFromUs(): boolean { + return this._allEditsAreFromUs; + } + private _diffOperation: Promise | undefined; + private _diffOperationIds: number = 0; + + private readonly _diffInfo = observableValue(this, nullDocumentDiff); + public get diffInfo(): IObservable { + return this._diffInfo; + } + private readonly _maxModifiedLineNumber = observableValue(this, 0); + readonly maxModifiedLineNumber = this._maxModifiedLineNumber; + + private readonly _editDecorationClear = this._register(new RunOnceScheduler(() => { this._editDecorations = this.modifiedModel.deltaDecorations(this._editDecorations, []); }, 500)); + private _editDecorations: string[] = []; + + private readonly _diffTrimWhitespace: IObservable; + protected readonly _stateObs = observableValue(this, WorkingSetEntryState.Modified); + readonly state: IObservable = this._stateObs; + protected readonly _isCurrentlyBeingModifiedByObs = observableValue(this, undefined); + readonly isCurrentlyBeingModifiedBy: IObservable = this._isCurrentlyBeingModifiedByObs; + private readonly initialContent: string; + + constructor( + public readonly notebookUri: URI, + public readonly cell: NotebookCellTextModel, + private readonly modifiedModel: ITextModel, + private readonly originalModel: ITextModel, + disposables: DisposableStore, + @IConfigurationService configService: IConfigurationService, + @IEditorWorkerService private readonly _editorWorkerService: IEditorWorkerService, + @INotebookEditorService private readonly notebookEditorService: INotebookEditorService + ) { + super(); + this.initialContent = this.originalModel.getValue(); + this._register(disposables); + this._register(this.modifiedModel.onDidChangeContent(e => { + this._mirrorEdits(e); + })); + this._register(toDisposable(() => { + this.clearCurrentEditLineDecoration(); + })); + + this._diffTrimWhitespace = observableConfigValue('diffEditor.ignoreTrimWhitespace', true, configService); + this._register(autorun(r => { + this._diffTrimWhitespace.read(r); + this._updateDiffInfoSeq(); + })); + } + + public clearCurrentEditLineDecoration() { + if (this.modifiedModel.isDisposed()) { + return; + } + this._editDecorations = this.modifiedModel.deltaDecorations(this._editDecorations, []); + } + + + private _mirrorEdits(event: IModelContentChangedEvent) { + const edit = OffsetEdits.fromContentChanges(event.changes); + + if (this._isEditFromUs) { + const e_sum = this._edit; + const e_ai = edit; + this._edit = e_sum.compose(e_ai); + + } else { + + // e_ai + // d0 ---------------> s0 + // | | + // | | + // | e_user_r | e_user + // | | + // | | + // v e_ai_r v + /// d1 ---------------> s1 + // + // d0 - document snapshot + // s0 - document + // e_ai - ai edits + // e_user - user edits + // + const e_ai = this._edit; + const e_user = edit; + + const e_user_r = e_user.tryRebase(e_ai.inverse(this.originalModel.getValue()), true); + + if (e_user_r === undefined) { + // user edits overlaps/conflicts with AI edits + this._edit = e_ai.compose(e_user); + } else { + const edits = OffsetEdits.asEditOperations(e_user_r, this.originalModel); + this.originalModel.applyEdits(edits); + this._edit = e_ai.tryRebase(e_user_r); + } + + this._allEditsAreFromUs = false; + this._updateDiffInfoSeq(); + + const didResetToOriginalContent = this.modifiedModel.getValue() === this.initialContent; + const currentState = this._stateObs.get(); + switch (currentState) { + case WorkingSetEntryState.Modified: + if (didResetToOriginalContent) { + this._stateObs.set(WorkingSetEntryState.Rejected, undefined); + break; + } + } + + } + } + + acceptAgentEdits(textEdits: TextEdit[], isLastEdits: boolean, responseModel: IChatResponseModel): void { + const notebookEditor = this.notebookEditorService.retrieveExistingWidgetFromURI(this.notebookUri)?.value; + if (notebookEditor) { + const vm = notebookEditor.getCellByHandle(this.cell.handle); + vm?.updateEditState(CellEditState.Editing, 'chatEdit'); + } + + const ops = textEdits.map(TextEdit.asEditOperation); + const undoEdits = this._applyEdits(ops); + + const maxLineNumber = undoEdits.reduce((max, op) => Math.max(max, op.range.startLineNumber), 0); + + const newDecorations: IModelDeltaDecoration[] = [ + // decorate pending edit (region) + { + options: ChatEditingNotebookCellEntry._pendingEditDecorationOptions, + range: new Range(maxLineNumber + 1, 1, Number.MAX_SAFE_INTEGER, Number.MAX_SAFE_INTEGER) + } + ]; + + if (maxLineNumber > 0) { + // decorate last edit + newDecorations.push({ + options: ChatEditingNotebookCellEntry._lastEditDecorationOptions, + range: new Range(maxLineNumber, 1, maxLineNumber, Number.MAX_SAFE_INTEGER) + }); + } + + this._editDecorations = this.modifiedModel.deltaDecorations(this._editDecorations, newDecorations); + + + transaction((tx) => { + if (!isLastEdits) { + this._stateObs.set(WorkingSetEntryState.Modified, tx); + this._isCurrentlyBeingModifiedByObs.set(responseModel, tx); + this._maxModifiedLineNumber.set(maxLineNumber, tx); + + } else { + this._resetEditsState(tx); + this._updateDiffInfoSeq(); + this._maxModifiedLineNumber.set(0, tx); + this._editDecorationClear.schedule(); + } + }); + } + + scheduleEditDecorations() { + this._editDecorationClear.schedule(); + } + + protected _resetEditsState(tx: ITransaction): void { + this._isCurrentlyBeingModifiedByObs.set(undefined, tx); + this._maxModifiedLineNumber.set(0, tx); + } + + public async keep(change: DetailedLineRangeMapping): Promise { + return this._acceptHunk(change); + } + + private async _acceptHunk(change: DetailedLineRangeMapping): Promise { + this._isEditFromUs = true; + try { + if (!this._diffInfo.get().changes.includes(change)) { + // diffInfo should have model version ids and check them (instead of the caller doing that) + return false; + } + const edits: ISingleEditOperation[] = []; + for (const edit of change.innerChanges ?? []) { + const newText = this.modifiedModel.getValueInRange(edit.modifiedRange); + edits.push(EditOperation.replace(edit.originalRange, newText)); + } + this.originalModel.pushEditOperations(null, edits, _ => null); + } + finally { + this._isEditFromUs = false; + } + await this._updateDiffInfoSeq(); + if (this._diffInfo.get().identical) { + this._stateObs.set(WorkingSetEntryState.Accepted, undefined); + } + return true; + } + + public async undo(change: DetailedLineRangeMapping): Promise { + return this._rejectHunk(change); + } + + private async _rejectHunk(change: DetailedLineRangeMapping): Promise { + this._isEditFromUs = true; + try { + if (!this._diffInfo.get().changes.includes(change)) { + return false; + } + const edits: ISingleEditOperation[] = []; + for (const edit of change.innerChanges ?? []) { + const newText = this.originalModel.getValueInRange(edit.originalRange); + edits.push(EditOperation.replace(edit.modifiedRange, newText)); + } + this.modifiedModel.pushEditOperations(null, edits, _ => null); + } finally { + this._isEditFromUs = false; + } + await this._updateDiffInfoSeq(); + if (this._diffInfo.get().identical) { + this._stateObs.set(WorkingSetEntryState.Rejected, undefined); + } + return true; + } + + private _applyEdits(edits: ISingleEditOperation[]) { + // make the actual edit + this._isEditFromUs = true; + try { + let result: ISingleEditOperation[] = []; + this.modifiedModel.pushEditOperations(null, edits, (undoEdits) => { + result = undoEdits; + return null; + }); + return result; + } finally { + this._isEditFromUs = false; + } + } + + private async _updateDiffInfoSeq() { + const myDiffOperationId = ++this._diffOperationIds; + await Promise.resolve(this._diffOperation); + if (this._diffOperationIds === myDiffOperationId) { + const thisDiffOperation = this._updateDiffInfo(); + this._diffOperation = thisDiffOperation; + await thisDiffOperation; + } + } + + private async _updateDiffInfo(): Promise { + + if (this.originalModel.isDisposed() || this.modifiedModel.isDisposed()) { + return; + } + + const docVersionNow = this.modifiedModel.getVersionId(); + const snapshotVersionNow = this.originalModel.getVersionId(); + + const ignoreTrimWhitespace = this._diffTrimWhitespace.get(); + + const diff = await this._editorWorkerService.computeDiff( + this.originalModel.uri, + this.modifiedModel.uri, + { ignoreTrimWhitespace, computeMoves: false, maxComputationTimeMs: 3000 }, + 'advanced' + ); + + if (this.originalModel.isDisposed() || this.modifiedModel.isDisposed()) { + return; + } + + // only update the diff if the documents didn't change in the meantime + if (this.modifiedModel.getVersionId() === docVersionNow && this.originalModel.getVersionId() === snapshotVersionNow) { + const diff2 = diff ?? nullDocumentDiff; + this._diffInfo.set(diff2, undefined); + this._edit = OffsetEdits.fromLineRangeMapping(this.originalModel, this.modifiedModel, diff2.changes); + } + } +} diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingNotebookEditorIntegration.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingNotebookEditorIntegration.ts new file mode 100644 index 000000000000..43693cec20c1 --- /dev/null +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingNotebookEditorIntegration.ts @@ -0,0 +1,609 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable } from '../../../../../../base/common/lifecycle.js'; +import { autorun, derivedWithStore, IObservable, ISettableObservable, observableFromEvent, observableValue } from '../../../../../../base/common/observable.js'; +import { debouncedObservable } from '../../../../../../base/common/observableInternal/utils.js'; +import { basename } from '../../../../../../base/common/resources.js'; +import { assertType } from '../../../../../../base/common/types.js'; +import { LineRange } from '../../../../../../editor/common/core/lineRange.js'; +import { Range } from '../../../../../../editor/common/core/range.js'; +import { nullDocumentDiff } from '../../../../../../editor/common/diff/documentDiffProvider.js'; +import { localize } from '../../../../../../nls.js'; +import { MenuId } from '../../../../../../platform/actions/common/actions.js'; +import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; +import { IEditorPane, IResourceDiffEditorInput } from '../../../../../common/editor.js'; +import { IEditorService } from '../../../../../services/editor/common/editorService.js'; +import { NotebookDeletedCellDecorator } from '../../../../notebook/browser/diff/inlineDiff/notebookDeletedCellDecorator.js'; +import { NotebookInsertedCellDecorator } from '../../../../notebook/browser/diff/inlineDiff/notebookInsertedCellDecorator.js'; +import { INotebookTextDiffEditor } from '../../../../notebook/browser/diff/notebookDiffEditorBrowser.js'; +import { getNotebookEditorFromEditorPane, ICellViewModel, INotebookEditor } from '../../../../notebook/browser/notebookBrowser.js'; +import { INotebookEditorService } from '../../../../notebook/browser/services/notebookEditorService.js'; +import { NotebookCellTextModel } from '../../../../notebook/common/model/notebookCellTextModel.js'; +import { NotebookTextModel } from '../../../../notebook/common/model/notebookTextModel.js'; +import { IChatAgentService } from '../../../common/chatAgents.js'; +import { IModifiedFileEntryChangeHunk, IModifiedFileEntryEditorIntegration } from '../../../common/chatEditingService.js'; +import { ChatAgentLocation } from '../../../common/constants.js'; +import { ChatEditingCodeEditorIntegration, IDocumentDiff2 } from '../chatEditingCodeEditorIntegration.js'; +import { ChatEditingModifiedNotebookEntry } from '../chatEditingModifiedNotebookEntry.js'; +import { countChanges, ICellDiffInfo, sortCellChanges } from './notebookCellChanges.js'; + +export class ChatEditingNotebookEditorIntegration extends Disposable implements IModifiedFileEntryEditorIntegration { + private integration: ChatEditingNotebookEditorWidgetIntegration; + private notebookEditor: INotebookEditor; + constructor( + _entry: ChatEditingModifiedNotebookEntry, + editor: IEditorPane, + notebookModel: NotebookTextModel, + originalModel: NotebookTextModel, + cellChanges: IObservable, + @IInstantiationService private readonly instantiationService: IInstantiationService, + ) { + super(); + + const notebookEditor = getNotebookEditorFromEditorPane(editor); + assertType(notebookEditor); + this.notebookEditor = notebookEditor; + this.integration = this.instantiationService.createInstance(ChatEditingNotebookEditorWidgetIntegration, _entry, notebookEditor, notebookModel, originalModel, cellChanges); + this._register(editor.onDidChangeControl(() => { + const notebookEditor = getNotebookEditorFromEditorPane(editor); + if (notebookEditor && notebookEditor !== this.notebookEditor) { + this.notebookEditor = notebookEditor; + this.integration.dispose(); + this.integration = this.instantiationService.createInstance(ChatEditingNotebookEditorWidgetIntegration, _entry, notebookEditor, notebookModel, originalModel, cellChanges); + } + })); + } + public get currentIndex(): IObservable { + return this.integration.currentIndex; + } + reveal(firstOrLast: boolean): void { + return this.integration.reveal(firstOrLast); + } + next(wrap: boolean): boolean { + return this.integration.next(wrap); + } + previous(wrap: boolean): boolean { + return this.integration.previous(wrap); + } + enableAccessibleDiffView(): void { + this.integration.enableAccessibleDiffView(); + } + acceptNearestChange(change: IModifiedFileEntryChangeHunk): void { + this.integration.acceptNearestChange(change); + } + rejectNearestChange(change: IModifiedFileEntryChangeHunk): void { + this.integration.rejectNearestChange(change); + } + toggleDiff(change: IModifiedFileEntryChangeHunk | undefined): Promise { + return this.integration.toggleDiff(change); + } +} + +class ChatEditingNotebookEditorWidgetIntegration extends Disposable implements IModifiedFileEntryEditorIntegration { + private readonly _currentIndex = observableValue(this, -1); + readonly currentIndex: IObservable = this._currentIndex; + + private readonly _currentChange = observableValue<{ change: ICellDiffInfo; index: number } | undefined>(this, undefined); + readonly currentChange: IObservable<{ change: ICellDiffInfo; index: number } | undefined> = this._currentChange; + + private readonly cellEditorIntegrations = new Map }>(); + + private readonly insertDeleteDecorators: IObservable<{ insertedCellDecorator: NotebookInsertedCellDecorator; deletedCellDecorator: NotebookDeletedCellDecorator } | undefined>; + + constructor( + private readonly _entry: ChatEditingModifiedNotebookEntry, + private readonly notebookEditor: INotebookEditor, + private readonly notebookModel: NotebookTextModel, + originalModel: NotebookTextModel, + private readonly cellChanges: IObservable, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @IEditorService private readonly _editorService: IEditorService, + @IChatAgentService private readonly _chatAgentService: IChatAgentService, + @INotebookEditorService notebookEditorService: INotebookEditorService, + ) { + super(); + + const onDidChangeVisibleRanges = debouncedObservable(observableFromEvent(notebookEditor.onDidChangeVisibleRanges, () => notebookEditor.visibleRanges), 50); + const notebookEdotirViewModelAttached = observableFromEvent(notebookEditor.onDidAttachViewModel, () => notebookEditor.getViewModel()); + + let originalReadonly: boolean | undefined = undefined; + const shouldBeReadonly = _entry.isCurrentlyBeingModifiedBy.map(value => !!value); + this._register(autorun(r => { + const isReadOnly = shouldBeReadonly.read(r); + const notebookEditor = notebookEditorService.retrieveExistingWidgetFromURI(_entry.modifiedURI)?.value; + if (!notebookEditor) { + return; + } + originalReadonly ??= notebookEditor.isReadOnly; + if (isReadOnly) { + if (!notebookEditor.isReadOnly) { + notebookEditor.setOptions({ isReadOnly: true }); + } + } else { + if (notebookEditor.isReadOnly && originalReadonly === false) { + notebookEditor.setOptions({ isReadOnly: false }); + } + } + })); + + // INIT when not streaming nor diffing the response anymore, once per request, and when having changes + let lastModifyingRequestId: string | undefined; + this._store.add(autorun(r => { + + if (!_entry.isCurrentlyBeingModifiedBy.read(r) + && !_entry.isProcessingResponse.read(r) + && lastModifyingRequestId !== _entry.lastModifyingRequestId + && cellChanges.read(r).some(c => c.type !== 'unchanged' && !c.diff.read(r).identical) + ) { + this.reveal(true); + } + })); + + // Build cell integrations (responsible for navigating changes within a cell and decorating cell text changes) + this._register(autorun(r => { + const sortedCellChanges = sortCellChanges(cellChanges.read(r)); + + const changes = sortedCellChanges.filter(c => c.type !== 'delete'); + onDidChangeVisibleRanges.read(r); + if (!changes.length) { + this.cellEditorIntegrations.forEach(({ diff }) => { + diff.set({ ...diff.get(), ...nullDocumentDiff }, undefined); + }); + return; + } + + const validCells = new Set(); + changes.forEach((change) => { + if (change.modifiedCellIndex === undefined || change.modifiedCellIndex >= notebookModel.cells.length) { + return; + } + const cell = notebookModel.cells[change.modifiedCellIndex]; + const editor = notebookEditor.codeEditors.find(([vm,]) => vm.handle === notebookModel.cells[change.modifiedCellIndex].handle)?.[1]; + const modifiedModel = change.modifiedModel.promiseResult.read(r)?.data; + const originalModel = change.originalModel.promiseResult.read(r)?.data; + if (!editor || !cell || !originalModel || !modifiedModel) { + return; + } + const diff = { + ...change.diff.read(r), + modifiedModel, + originalModel, + keep: change.keep, + undo: change.undo + } satisfies IDocumentDiff2; + validCells.add(cell); + const currentDiff = this.cellEditorIntegrations.get(cell); + if (currentDiff) { + // Do not unnecessarily trigger a change event + if (!areDocumentDiff2Equal(currentDiff.diff.get(), diff)) { + currentDiff.diff.set(diff, undefined); + } + } else { + const diff2 = observableValue(`diff${cell.handle}`, diff); + const integration = this.instantiationService.createInstance(ChatEditingCodeEditorIntegration, _entry, editor, diff2); + this.cellEditorIntegrations.set(cell, { integration, diff: diff2 }); + this._register(integration); + this._register(editor.onDidDispose(() => { + this.cellEditorIntegrations.get(cell)?.integration.dispose(); + this.cellEditorIntegrations.delete(cell); + })); + this._register(editor.onDidChangeModel(() => { + if (editor.getModel() !== cell.textModel) { + this.cellEditorIntegrations.get(cell)?.integration.dispose(); + this.cellEditorIntegrations.delete(cell); + } + })); + } + }); + + // Dispose old integrations as the editors are no longer valid. + this.cellEditorIntegrations.forEach((v, cell) => { + if (!validCells.has(cell)) { + v.integration.dispose(); + this.cellEditorIntegrations.delete(cell); + } + }); + })); + + this._register(autorun(r => { + const currentChange = this.currentChange.read(r); + if (!currentChange) { + this._currentIndex.set(-1, undefined); + return; + } + + let index = 0; + const sortedCellChanges = sortCellChanges(cellChanges.read(r)); + for (const change of sortedCellChanges) { + if (currentChange && currentChange.change === change) { + if (change.type === 'modified') { + index += currentChange.index; + } + break; + } + if (change.type === 'insert' || change.type === 'delete') { + index++; + } else if (change.type === 'modified') { + index += change.diff.read(r).changes.length; + } + } + + this._currentIndex.set(index, undefined); + })); + + this.insertDeleteDecorators = derivedWithStore((r, store) => { + if (!notebookEdotirViewModelAttached.read(r)) { + return; + } + + const insertedCellDecorator = store.add(this.instantiationService.createInstance(NotebookInsertedCellDecorator, this.notebookEditor)); + const deletedCellDecorator = store.add(this.instantiationService.createInstance(NotebookDeletedCellDecorator, this.notebookEditor, { + className: 'chat-diff-change-content-widget', + telemetrySource: 'chatEditingNotebookHunk', + menuId: MenuId.ChatEditingEditorHunk, + argFactory: (deletedCellIndex: number) => { + return { + accept() { + const entry = cellChanges.get().find(c => c.type === 'delete' && c.originalCellIndex === deletedCellIndex); + if (entry) { + return entry.keep(entry.diff.get().changes[0]); + } + return Promise.resolve(true); + }, + reject() { + const entry = cellChanges.get().find(c => c.type === 'delete' && c.originalCellIndex === deletedCellIndex); + if (entry) { + return entry.undo(entry.diff.get().changes[0]); + } + return Promise.resolve(true); + }, + } satisfies IModifiedFileEntryChangeHunk; + } + })); + + return { + insertedCellDecorator, + deletedCellDecorator + }; + }); + + const cellsAreVisible = onDidChangeVisibleRanges.map(v => v.length > 0); + this._register(autorun(r => { + if (!cellsAreVisible.read(r)) { + return; + } + // We can have inserted cells that have been accepted, in those cases we do not wany any decorators on them. + const changes = debouncedObservable(cellChanges, 10).read(r).filter(c => c.type === 'insert' ? !c.diff.read(r).identical : true); + const decorators = debouncedObservable(this.insertDeleteDecorators, 10).read(r); + if (decorators) { + decorators.insertedCellDecorator.apply(changes); + decorators.deletedCellDecorator.apply(changes, originalModel); + } + })); + } + + getCell(modifiedCellIndex: number) { + const cell = this.notebookModel.cells[modifiedCellIndex]; + const integration = this.cellEditorIntegrations.get(cell)?.integration; + return integration; + } + + reveal(firstOrLast: boolean): void { + const changes = sortCellChanges(this.cellChanges.get().filter(c => c.type !== 'unchanged')); + if (!changes.length) { + return undefined; + } + const change = firstOrLast ? changes[0] : changes[changes.length - 1]; + this._revealFirstOrLast(change, firstOrLast); + } + + private _revealFirstOrLast(change: ICellDiffInfo, firstOrLast: boolean = true) { + switch (change.type) { + case 'insert': + case 'modified': + { + const index = firstOrLast || change.type === 'insert' ? 0 : change.diff.get().changes.length - 1; + const cellIntegration = this.getCell(change.modifiedCellIndex); + if (cellIntegration) { + cellIntegration.reveal(firstOrLast); + this._currentChange.set({ change: change, index }, undefined); + return true; + } else { + return this._revealChange(change, index); + } + } + case 'delete': + // reveal the deleted cell decorator + this.insertDeleteDecorators.get()?.deletedCellDecorator.reveal(change.originalCellIndex); + this._currentChange.set({ change: change, index: 0 }, undefined); + return true; + default: + break; + } + + return false; + } + + private _revealChange(change: ICellDiffInfo, indexInCell: number) { + switch (change.type) { + case 'insert': + case 'modified': + { + const textChange = change.diff.get().changes[indexInCell]; + const cellViewModel = this.getCellViewModel(change); + if (cellViewModel) { + this.revealChangeInView(cellViewModel, textChange.modified); + this._currentChange.set({ change: change, index: indexInCell }, undefined); + } + + return true; + } + case 'delete': + // reveal the deleted cell decorator + this.insertDeleteDecorators.get()?.deletedCellDecorator.reveal(change.originalCellIndex); + this._currentChange.set({ change: change, index: 0 }, undefined); + return true; + default: + break; + } + + return false; + } + + private getCellViewModel(change: ICellDiffInfo) { + if (change.type === 'delete' || change.modifiedCellIndex === undefined) { + return undefined; + } + const cell = this.notebookModel.cells[change.modifiedCellIndex]; + const cellViewModel = this.notebookEditor.getViewModel()?.viewCells.find(c => c.handle === cell.handle); + return cellViewModel; + } + + private async revealChangeInView(cell: ICellViewModel, lines: LineRange): Promise { + await this.notebookEditor.focusNotebookCell(cell, 'editor', { focusEditorLine: lines.startLineNumber }); + await this.notebookEditor.revealRangeInCenterIfOutsideViewportAsync(cell, new Range(lines.startLineNumber, 0, lines.endLineNumberExclusive, 0)); + } + + next(wrap: boolean): boolean { + const changes = sortCellChanges(this.cellChanges.get().filter(c => c.type !== 'unchanged')); + const currentChange = this.currentChange.get(); + if (!currentChange) { + const firstChange = changes[0]; + + if (firstChange) { + return this._revealFirstOrLast(firstChange); + } + + return false; + } + + // go to next + switch (currentChange.change.type) { + case 'modified': + { + // first check if we are at the end of the current change + const isLastChangeInCell = currentChange.index === this.lastChangeIndex(currentChange.change); + const index = isLastChangeInCell ? 0 : currentChange.index + 1; + const change = isLastChangeInCell ? changes[changes.indexOf(currentChange.change) + 1] : currentChange.change; + + const isLastChangeInCell = currentChange.index === lastChangeIndex(currentChange.change); + const index = isLastChangeInCell ? 0 : currentChange.index + 1; + const change = isLastChangeInCell ? changes[changes.indexOf(currentChange.change) + 1] : currentChange.change; + + if (change) { + return this._revealChange(change, index); + } + } + break; + case 'insert': + case 'delete': + { + // go to next change directly + const nextChange = changes[changes.indexOf(currentChange.change) + 1]; + if (nextChange) { + return this._revealFirstOrLast(nextChange, true); + } + } + break; + default: + break; + } + + if (wrap) { + return this.next(false); + } + + return false; + } + + previous(wrap: boolean): boolean { + const changes = sortCellChanges(this.cellChanges.get().filter(c => c.type !== 'unchanged')); + const currentChange = this.currentChange.get(); + if (!currentChange) { + const lastChange = changes[changes.length - 1]; + if (lastChange) { + return this._revealFirstOrLast(lastChange, false); + } + + return false; + } + + // go to previous + // first check if we are at the start of the current change + switch (currentChange.change.type) { + case 'modified': + { + // first check if we are at the first change within the cell + const isFirstChangeInCell = currentChange.index === 0; + const change = isFirstChangeInCell ? changes[changes.indexOf(currentChange.change) - 1] : currentChange.change; + + const isFirstChangeInCell = currentChange.index === 0; + const index = isFirstChangeInCell ? 0 : currentChange.index - 1; + const change = isFirstChangeInCell ? changes[changes.indexOf(currentChange.change) - 1] : currentChange.change; + + if (change) { + return this._revealChange(change, index); + } + } + break; + case 'insert': + case 'delete': + { + // go to previous change directly + const prevChange = changes[changes.indexOf(currentChange.change) - 1]; + if (prevChange) { + return this._revealFirstOrLast(prevChange, false); + } + } + break; + default: + break; + } + + if (wrap) { + const lastChange = changes[changes.length - 1]; + if (lastChange) { + return this._revealFirstOrLast(lastChange, false); + } + } + + return false; + } + + enableAccessibleDiffView(): void { + const cell = this.notebookEditor.getActiveCell()?.model; + if (cell) { + const integration = this.cellEditorIntegrations.get(cell)?.integration; + integration?.enableAccessibleDiffView(); + } + } + acceptNearestChange(change: IModifiedFileEntryChangeHunk): void { + change.accept(); + this.next(true); + } + rejectNearestChange(change: IModifiedFileEntryChangeHunk): void { + change.reject(); + this.next(true); + } + async toggleDiff(_change: IModifiedFileEntryChangeHunk | undefined): Promise { + const defaultAgentName = this._chatAgentService.getDefaultAgent(ChatAgentLocation.EditingSession)?.fullName; + const diffInput = { + original: { resource: this._entry.originalURI, options: { selection: undefined } }, + modified: { resource: this._entry.modifiedURI, options: { selection: undefined } }, + label: defaultAgentName + ? localize('diff.agent', '{0} (changes from {1})', basename(this._entry.modifiedURI), defaultAgentName) + : localize('diff.generic', '{0} (changes from chat)', basename(this._entry.modifiedURI)) + } satisfies IResourceDiffEditorInput; + await this._editorService.openEditor(diffInput); + + } +} + +export class ChatEditingNotebookDiffEditorIntegration extends Disposable implements IModifiedFileEntryEditorIntegration { + private readonly _currentIndex = observableValue(this, -1); + readonly currentIndex: IObservable = this._currentIndex; + + constructor( + private readonly notebookDiffEditor: INotebookTextDiffEditor, + private readonly cellChanges: IObservable + ) { + super(); + + this._store.add(autorun(r => { + const index = notebookDiffEditor.currentChangedIndex.read(r); + const numberOfCellChanges = cellChanges.read(r).filter(c => !c.diff.read(r).identical); + if (numberOfCellChanges.length && index >= 0 && index < numberOfCellChanges.length) { + // Notebook Diff editor only supports navigating through changes to cells. + // However in chat we take changes to lines in the cells into account. + // So if we're on the second cell and first cell has 3 changes, then we're on the 4th change. + const changesSoFar = countChanges(numberOfCellChanges.slice(0, index + 1)); + this._currentIndex.set(changesSoFar - 1, undefined); + } else { + this._currentIndex.set(-1, undefined); + } + })); + } + + reveal(firstOrLast: boolean): void { + const changes = sortCellChanges(this.cellChanges.get().filter(c => c.type !== 'unchanged')); + if (!changes.length) { + return undefined; + } + if (firstOrLast) { + this.notebookDiffEditor.firstChange(); + } else { + this.notebookDiffEditor.lastChange(); + } + } + + next(_wrap: boolean): boolean { + const changes = this.cellChanges.get().filter(c => !c.diff.get().identical).length; + if (this.notebookDiffEditor.currentChangedIndex.get() === changes - 1) { + return false; + } + this.notebookDiffEditor.nextChange(); + return true; + } + + previous(_wrap: boolean): boolean { + const changes = this.cellChanges.get().filter(c => !c.diff.get().identical).length; + if (this.notebookDiffEditor.currentChangedIndex.get() === changes - 1) { + return false; + } + this.notebookDiffEditor.nextChange(); + return true; + } + + enableAccessibleDiffView(): void { + // + } + acceptNearestChange(change: IModifiedFileEntryChangeHunk): void { + change.accept(); + this.next(true); + } + rejectNearestChange(change: IModifiedFileEntryChangeHunk): void { + change.reject(); + this.next(true); + } + async toggleDiff(_change: IModifiedFileEntryChangeHunk | undefined): Promise { + // + } +} + +function areDocumentDiff2Equal(diff1: IDocumentDiff2, diff2: IDocumentDiff2): boolean { + if (diff1.changes !== diff2.changes) { + return false; + } + if (diff1.identical !== diff2.identical) { + return false; + } + if (diff1.moves !== diff2.moves) { + return false; + } + if (diff1.originalModel !== diff2.originalModel) { + return false; + } + if (diff1.modifiedModel !== diff2.modifiedModel) { + return false; + } + if (diff1.keep !== diff2.keep) { + return false; + } + if (diff1.undo !== diff2.undo) { + return false; + } + if (diff1.quitEarly !== diff2.quitEarly) { + return false; + } + return true; +} + +function lastChangeIndex(change: ICellDiffInfo): number { + if (change.type === 'modified') { + return change.diff.get().changes.length - 1; + } + return 0; +} diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingNotebookFileSystemProvider.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingNotebookFileSystemProvider.ts new file mode 100644 index 000000000000..b79dd102d43b --- /dev/null +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingNotebookFileSystemProvider.ts @@ -0,0 +1,126 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { VSBuffer } from '../../../../../../base/common/buffer.js'; +import { CancellationToken } from '../../../../../../base/common/cancellation.js'; +import { Event } from '../../../../../../base/common/event.js'; +import { Disposable, IDisposable } from '../../../../../../base/common/lifecycle.js'; +import { ResourceMap } from '../../../../../../base/common/map.js'; +import { ReadableStreamEvents } from '../../../../../../base/common/stream.js'; +import { URI } from '../../../../../../base/common/uri.js'; +import { FileSystemProviderCapabilities, FileType, IFileChange, IFileDeleteOptions, IFileOpenOptions, IFileOverwriteOptions, IFileReadStreamOptions, IFileService, IFileSystemProvider, IFileWriteOptions, IStat, IWatchOptions } from '../../../../../../platform/files/common/files.js'; +import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; +import { IWorkbenchContribution } from '../../../../../common/contributions.js'; +import { INotebookService } from '../../../../notebook/common/notebookService.js'; +import { IChatEditingService } from '../../../common/chatEditingService.js'; +import { ChatEditingNotebookSnapshotScheme, deserializeSnapshot } from './chatEditingModifiedNotebookSnapshot.js'; +import { ChatEditingSession } from '../chatEditingSession.js'; + + +export class ChatEditingNotebookFileSystemProviderContrib extends Disposable implements IWorkbenchContribution { + static ID = 'chatEditingNotebookFileSystemProviderContribution'; + constructor( + @IFileService private readonly fileService: IFileService, + @IInstantiationService instantiationService: IInstantiationService, + ) { + + super(); + const fileSystemProvider = instantiationService.createInstance(ChatEditingNotebookFileSystemProvider); + this._register(this.fileService.registerProvider(ChatEditingNotebookSnapshotScheme, fileSystemProvider)); + } +} + +type ChatEditingSnapshotNotebookContentQueryData = { sessionId: string; requestId: string | undefined; undoStop: string | undefined; viewType: string }; + +export class ChatEditingNotebookFileSystemProvider implements IFileSystemProvider { + private static registeredFiles = new ResourceMap(); + public readonly capabilities: FileSystemProviderCapabilities = FileSystemProviderCapabilities.Readonly | FileSystemProviderCapabilities.FileAtomicRead | FileSystemProviderCapabilities.FileReadWrite; + public static registerFile(resource: URI, buffer: VSBuffer): IDisposable { + ChatEditingNotebookFileSystemProvider.registeredFiles.set(resource, buffer); + return { + dispose() { + if (ChatEditingNotebookFileSystemProvider.registeredFiles.get(resource) === buffer) { + ChatEditingNotebookFileSystemProvider.registeredFiles.delete(resource); + } + } + }; + } + + constructor( + @IChatEditingService private readonly _chatEditingService: IChatEditingService, + @INotebookService private readonly notebookService: INotebookService) { } + readonly onDidChangeCapabilities = Event.None; + readonly onDidChangeFile: Event = Event.None; + watch(_resource: URI, _opts: IWatchOptions): IDisposable { + return Disposable.None; + } + async stat(_resource: URI): Promise { + return { + type: FileType.File, + ctime: 0, + mtime: 0, + size: 0 + }; + } + mkdir(_resource: URI): Promise { + throw new Error('Method not implemented1.'); + } + readdir(_resource: URI): Promise<[string, FileType][]> { + throw new Error('Method not implemented2.'); + } + delete(_resource: URI, _opts: IFileDeleteOptions): Promise { + throw new Error('Method not implemented3.'); + } + rename(_from: URI, _to: URI, _opts: IFileOverwriteOptions): Promise { + throw new Error('Method not implemented4.'); + } + copy?(_from: URI, _to: URI, _opts: IFileOverwriteOptions): Promise { + throw new Error('Method not implemented5.'); + } + async readFile(resource: URI): Promise { + const buffer = ChatEditingNotebookFileSystemProvider.registeredFiles.get(resource); + if (buffer) { + return buffer.buffer; + } + const queryData = JSON.parse(resource.query) as ChatEditingSnapshotNotebookContentQueryData; + if (!queryData.viewType) { + throw new Error('File not found, viewType not found'); + } + const session = this._chatEditingService.getEditingSession(queryData.sessionId); + if (!(session instanceof ChatEditingSession) || !queryData.requestId) { + throw new Error('File not found, session not found'); + } + const snapshotEntry = session.getSnapshot(queryData.requestId, queryData.undoStop || undefined, resource); + if (!snapshotEntry) { + throw new Error('File not found, snapshot not found'); + } + + const { data } = deserializeSnapshot(snapshotEntry.current); + const { serializer } = await this.notebookService.withNotebookDataProvider(queryData.viewType); + return serializer.notebookToData(data).then(s => s.buffer); + } + + writeFile?(__resource: URI, _content: Uint8Array, _opts: IFileWriteOptions): Promise { + throw new Error('Method not implemented7.'); + } + readFileStream?(__resource: URI, _opts: IFileReadStreamOptions, _token: CancellationToken): ReadableStreamEvents { + throw new Error('Method not implemented8.'); + } + open?(__resource: URI, _opts: IFileOpenOptions): Promise { + throw new Error('Method not implemented9.'); + } + close?(_fd: number): Promise { + throw new Error('Method not implemented10.'); + } + read?(_fd: number, _pos: number, _data: Uint8Array, _offset: number, _length: number): Promise { + throw new Error('Method not implemented11.'); + } + write?(_fd: number, _pos: number, _data: Uint8Array, _offset: number, _length: number): Promise { + throw new Error('Method not implemented12.'); + } + cloneFile?(_from: URI, __to: URI): Promise { + throw new Error('Method not implemented13.'); + } +} diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/helpers.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/helpers.ts new file mode 100644 index 000000000000..23a647019cb9 --- /dev/null +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/helpers.ts @@ -0,0 +1,405 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { NotebookTextModel } from '../../../../notebook/common/model/notebookTextModel.js'; +import { CellEditType, ICell, ICellDto2, ICellEditOperation, ICellReplaceEdit, NotebookCellsChangeType, NotebookCellsModelMoveEvent, NotebookCellTextModelSplice, NotebookTextModelChangedEvent } from '../../../../notebook/common/notebookCommon.js'; +import { ICellDiffInfo, sortCellChanges } from './notebookCellChanges.js'; + + +export function adjustCellDiffForKeepingADeletedCell(originalCellIndex: number, + cellDiffInfo: ICellDiffInfo[], + applyEdits: typeof NotebookTextModel.prototype.applyEdits, +): ICellDiffInfo[] { + // Delete this cell from original as well. + const edit: ICellReplaceEdit = { cells: [], count: 1, editType: CellEditType.Replace, index: originalCellIndex, }; + applyEdits([edit], true, undefined, () => undefined, undefined, true); + const diffs = sortCellChanges(cellDiffInfo) + .filter(d => !(d.type === 'delete' && d.originalCellIndex === originalCellIndex)) + .map(diff => { + if (diff.type !== 'insert' && diff.originalCellIndex > originalCellIndex) { + return { + ...diff, + originalCellIndex: diff.originalCellIndex - 1, + }; + } + return diff; + }); + return diffs; +} + +export function adjustCellDiffForRevertingADeletedCell(originalCellIndex: number, + cellDiffInfo: ICellDiffInfo[], + cellToInsert: ICellDto2, + applyEdits: typeof NotebookTextModel.prototype.applyEdits, + createModifiedCellDiffInfo: (modifiedCellIndex: number, originalCellIndex: number) => ICellDiffInfo, +): ICellDiffInfo[] { + cellDiffInfo = sortCellChanges(cellDiffInfo); + const indexOfEntry = cellDiffInfo.findIndex(d => d.originalCellIndex === originalCellIndex); + if (indexOfEntry === -1) { + // Not possible. + return cellDiffInfo; + } + + let modifiedCellIndex = -1; + for (let i = 0; i < cellDiffInfo.length; i++) { + const diff = cellDiffInfo[i]; + if (i < indexOfEntry) { + modifiedCellIndex = Math.max(modifiedCellIndex, diff.modifiedCellIndex ?? modifiedCellIndex); + continue; + } + if (i === indexOfEntry) { + const edit: ICellReplaceEdit = { cells: [cellToInsert], count: 0, editType: CellEditType.Replace, index: modifiedCellIndex + 1, }; + applyEdits([edit], true, undefined, () => undefined, undefined, true); + cellDiffInfo[i] = createModifiedCellDiffInfo(modifiedCellIndex + 1, originalCellIndex); + continue; + } else { + // Increase the original index for all entries after this. + if (typeof diff.modifiedCellIndex === 'number') { + diff.modifiedCellIndex++; + cellDiffInfo[i] = { ...diff }; + } + } + } + + return cellDiffInfo; +} + +export function adjustCellDiffForRevertingAnInsertedCell(modifiedCellIndex: number, + cellDiffInfo: ICellDiffInfo[], + applyEdits: typeof NotebookTextModel.prototype.applyEdits, +): ICellDiffInfo[] { + if (modifiedCellIndex === -1) { + // Not possible. + return cellDiffInfo; + } + cellDiffInfo = sortCellChanges(cellDiffInfo) + .filter(d => !(d.type === 'insert' && d.modifiedCellIndex === modifiedCellIndex)) + .map(d => { + if (d.type === 'insert' && d.modifiedCellIndex === modifiedCellIndex) { + return d; + } + if (d.type !== 'delete' && d.modifiedCellIndex > modifiedCellIndex) { + return { + ...d, + modifiedCellIndex: d.modifiedCellIndex - 1, + }; + } + return d; + }); + const edit: ICellReplaceEdit = { cells: [], count: 1, editType: CellEditType.Replace, index: modifiedCellIndex, }; + applyEdits([edit], true, undefined, () => undefined, undefined, true); + return cellDiffInfo; +} + +export function adjustCellDiffForKeepingAnInsertedCell(modifiedCellIndex: number, + cellDiffInfo: ICellDiffInfo[], + cellToInsert: ICellDto2, + applyEdits: typeof NotebookTextModel.prototype.applyEdits, + createModifiedCellDiffInfo: (modifiedCellIndex: number, originalCellIndex: number) => ICellDiffInfo, +): ICellDiffInfo[] { + cellDiffInfo = sortCellChanges(cellDiffInfo); + if (modifiedCellIndex === -1) { + // Not possible. + return cellDiffInfo; + } + const indexOfEntry = cellDiffInfo.findIndex(d => d.modifiedCellIndex === modifiedCellIndex); + if (indexOfEntry === -1) { + // Not possible. + return cellDiffInfo; + } + let originalCellIndex = -1; + for (let i = 0; i < cellDiffInfo.length; i++) { + const diff = cellDiffInfo[i]; + if (i < indexOfEntry) { + originalCellIndex = Math.max(originalCellIndex, diff.originalCellIndex ?? originalCellIndex); + continue; + } + if (i === indexOfEntry) { + const edit: ICellReplaceEdit = { cells: [cellToInsert], count: 0, editType: CellEditType.Replace, index: originalCellIndex + 1 }; + applyEdits([edit], true, undefined, () => undefined, undefined, true); + cellDiffInfo[i] = createModifiedCellDiffInfo(modifiedCellIndex, originalCellIndex + 1); + continue; + } else { + // Increase the original index for all entries after this. + if (typeof diff.originalCellIndex === 'number') { + diff.originalCellIndex++; + cellDiffInfo[i] = { ...diff }; + } + } + } + return cellDiffInfo; +} + +export function adjustCellDiffAndOriginalModelBasedOnCellAddDelete(change: NotebookCellTextModelSplice, + cellDiffInfo: ICellDiffInfo[], + modifiedModelCellCount: number, + originalModelCellCount: number, + applyEdits: typeof NotebookTextModel.prototype.applyEdits, + createModifiedCellDiffInfo: (modifiedCellIndex: number, originalCellIndex: number) => ICellDiffInfo, +): ICellDiffInfo[] { + cellDiffInfo = sortCellChanges(cellDiffInfo); + const numberOfCellsInserted = change[2].length; + const numberOfCellsDeleted = change[1]; + const cells = change[2].map(cell => { + return { + cellKind: cell.cellKind, + language: cell.language, + metadata: cell.metadata, + outputs: cell.outputs, + source: cell.getValue(), + mime: undefined, + internalMetadata: cell.internalMetadata + } satisfies ICellDto2; + }); + const wasInsertedAsFirstCell = change[0] === 0; + const wasInsertedAsLastCell = change[0] === modifiedModelCellCount - 1; + const diffEntryIndex = wasInsertedAsFirstCell ? 0 : (wasInsertedAsLastCell ? cellDiffInfo.length - 1 : (cellDiffInfo.findIndex(d => d.modifiedCellIndex === change[0]))); + const indexToInsertInOriginalModel = (wasInsertedAsFirstCell || diffEntryIndex === -1) ? 0 : (wasInsertedAsLastCell ? originalModelCellCount : (((cellDiffInfo.slice(0, diffEntryIndex).reverse().find(c => typeof c.originalCellIndex === 'number')?.originalCellIndex ?? -1) + 1))); + if (cells.length) { + const edit: ICellEditOperation = { + editType: CellEditType.Replace, + cells, + index: indexToInsertInOriginalModel, + count: change[1] + }; + applyEdits([edit], true, undefined, () => undefined, undefined, true); + } + // If cells were deleted we handled that with this.disposeDeletedCellEntries(); + if (numberOfCellsDeleted) { + // Adjust the indexes. + let numberOfOriginalCellsRemovedSoFar = 0; + let numberOfModifiedCellsRemovedSoFar = 0; + const modifiedIndexesToRemove = new Set(); + for (let i = 0; i < numberOfCellsDeleted; i++) { + modifiedIndexesToRemove.add(change[0] + i); + } + const itemsToRemove = new Set(); + for (let i = 0; i < cellDiffInfo.length; i++) { + const diff = cellDiffInfo[i]; + if (i < diffEntryIndex) { + continue; + } + + let changed = false; + if (typeof diff.modifiedCellIndex === 'number' && modifiedIndexesToRemove.has(diff.modifiedCellIndex)) { + // This will be removed. + numberOfModifiedCellsRemovedSoFar++; + if (typeof diff.originalCellIndex === 'number') { + numberOfOriginalCellsRemovedSoFar++; + } + itemsToRemove.add(diff); + continue; + } + if (typeof diff.modifiedCellIndex === 'number' && numberOfModifiedCellsRemovedSoFar) { + diff.modifiedCellIndex -= numberOfModifiedCellsRemovedSoFar; + changed = true; + } + if (typeof diff.originalCellIndex === 'number' && numberOfOriginalCellsRemovedSoFar) { + diff.originalCellIndex -= numberOfOriginalCellsRemovedSoFar; + changed = true; + } + if (changed) { + cellDiffInfo[i] = { ...diff }; + } + } + if (itemsToRemove.size) { + Array.from(itemsToRemove) + .filter(diff => typeof diff.originalCellIndex === 'number') + .forEach(diff => { + const edit: ICellEditOperation = { + editType: CellEditType.Replace, + cells: [], + index: diff.originalCellIndex, + count: 1 + }; + applyEdits([edit], true, undefined, () => undefined, undefined, true); + }); + } + cellDiffInfo = cellDiffInfo.filter(d => !itemsToRemove.has(d)); + } + + if (numberOfCellsInserted) { + for (let i = 0; i < cellDiffInfo.length; i++) { + const diff = cellDiffInfo[i]; + if (i < diffEntryIndex) { + continue; + } + let changed = false; + if (typeof diff.modifiedCellIndex === 'number') { + diff.modifiedCellIndex += numberOfCellsInserted; + changed = true; + } + if (typeof diff.originalCellIndex === 'number') { + diff.originalCellIndex += numberOfCellsInserted; + changed = true; + } + if (changed) { + cellDiffInfo[i] = { ...diff }; + } + } + } + + // For inserted cells, we need to ensure that we create a corresponding CellEntry. + // So that any edits to the inserted cell is handled and mirrored over to the corresponding cell in original model. + cells.forEach((_, i) => { + const originalCellIndex = i + indexToInsertInOriginalModel; + const modifiedCellIndex = change[0] + i; + const unchangedCell = createModifiedCellDiffInfo(modifiedCellIndex, originalCellIndex); + cellDiffInfo.splice((diffEntryIndex === -1 ? 0 : diffEntryIndex) + i, 0, unchangedCell); + }); + return cellDiffInfo; +} + +/** + * Given the movements of cells in modified notebook, adjust the ICellDiffInfo[] array + * and generate edits for the old notebook (if required). + * TODO@DonJayamanne Handle bulk moves (movements of more than 1 cell). + */ +export function adjustCellDiffAndOriginalModelBasedOnCellMovements(event: NotebookCellsModelMoveEvent, cellDiffInfo: ICellDiffInfo[]): [ICellDiffInfo[], ICellEditOperation[]] | undefined { + const minimumIndex = Math.min(event.index, event.newIdx); + const maximumIndex = Math.max(event.index, event.newIdx); + const cellDiffs = cellDiffInfo.slice(); + const indexOfEntry = cellDiffs.findIndex(d => d.modifiedCellIndex === event.index); + const indexOfEntryToPlaceBelow = cellDiffs.findIndex(d => d.modifiedCellIndex === event.newIdx); + if (indexOfEntry === -1 || indexOfEntryToPlaceBelow === -1) { + return undefined; + } + // Create a new object so that the observable value is triggered. + // Besides we'll be updating the values of this object in place. + const entryToBeMoved = { ...cellDiffs[indexOfEntry] }; + const moveDirection = event.newIdx > event.index ? 'down' : 'up'; + + + const startIndex = cellDiffs.findIndex(d => d.modifiedCellIndex === minimumIndex); + const endIndex = cellDiffs.findIndex(d => d.modifiedCellIndex === maximumIndex); + const movingExistingCell = typeof entryToBeMoved.originalCellIndex === 'number'; + let originalCellsWereEffected = false; + for (let i = 0; i < cellDiffs.length; i++) { + const diff = cellDiffs[i]; + let changed = false; + if (moveDirection === 'down') { + if (i > startIndex && i <= endIndex) { + if (typeof diff.modifiedCellIndex === 'number') { + changed = true; + diff.modifiedCellIndex = diff.modifiedCellIndex - 1; + } + if (typeof diff.originalCellIndex === 'number' && movingExistingCell) { + diff.originalCellIndex = diff.originalCellIndex - 1; + originalCellsWereEffected = true; + changed = true; + } + } + } else { + if (i >= startIndex && i < endIndex) { + if (typeof diff.modifiedCellIndex === 'number') { + changed = true; + diff.modifiedCellIndex = diff.modifiedCellIndex + 1; + } + if (typeof diff.originalCellIndex === 'number' && movingExistingCell) { + diff.originalCellIndex = diff.originalCellIndex + 1; + originalCellsWereEffected = true; + changed = true; + } + } + } + // Create a new object so that the observable value is triggered. + // Do only if there's a change. + if (changed) { + cellDiffs[i] = { ...diff }; + } + } + entryToBeMoved.modifiedCellIndex = event.newIdx; + const originalCellIndex = entryToBeMoved.originalCellIndex; + if (moveDirection === 'down') { + cellDiffs.splice(endIndex + 1, 0, entryToBeMoved); + cellDiffs.splice(startIndex, 1); + // If we're moving a new cell up/down, then we need just adjust just the modified indexes of the cells in between. + // If we're moving an existing up/down, then we need to adjust the original indexes as well. + if (typeof entryToBeMoved.originalCellIndex === 'number') { + entryToBeMoved.originalCellIndex = cellDiffs.slice(0, endIndex).reduce((lastOriginalIndex, diff) => typeof diff.originalCellIndex === 'number' ? Math.max(lastOriginalIndex, diff.originalCellIndex) : lastOriginalIndex, -1) + 1; + } + } else { + cellDiffs.splice(endIndex, 1); + cellDiffs.splice(startIndex, 0, entryToBeMoved); + // If we're moving a new cell up/down, then we need just adjust just the modified indexes of the cells in between. + // If we're moving an existing up/down, then we need to adjust the original indexes as well. + if (typeof entryToBeMoved.originalCellIndex === 'number') { + entryToBeMoved.originalCellIndex = cellDiffs.slice(0, startIndex).reduce((lastOriginalIndex, diff) => typeof diff.originalCellIndex === 'number' ? Math.max(lastOriginalIndex, diff.originalCellIndex) : lastOriginalIndex, -1) + 1; + } + } + + // If this is a new cell that we're moving, and there are no existing cells in between, then we can just move the new cell. + // I.e. no need to update the original notebook model. + if (typeof entryToBeMoved.originalCellIndex === 'number' && originalCellsWereEffected && typeof originalCellIndex === 'number' && entryToBeMoved.originalCellIndex !== originalCellIndex) { + const edit: ICellEditOperation = { + editType: CellEditType.Move, + index: originalCellIndex, + length: event.length, + newIdx: entryToBeMoved.originalCellIndex + }; + + return [cellDiffs, [edit]]; + } + + return [cellDiffs, []]; +} + +export function getCorrespondingOriginalCellIndex(modifiedCellIndex: number, cellDiffInfo: ICellDiffInfo[]): number | undefined { + const entry = cellDiffInfo.find(d => d.modifiedCellIndex === modifiedCellIndex); + return entry?.originalCellIndex; +} + +/** + * + * This isn't great, but necessary. + * ipynb extension updates metadata when new cells are inserted (to ensure the metadata is correct) + * Details of why thats required is in ipynb extension, but its necessary. + * However as a result of this, those edits appear here and are assumed to be user edits. + * As a result `_allEditsAreFromUs` is set to false. + */ +export function isTransientIPyNbExtensionEvent(notebookKind: string, e: NotebookTextModelChangedEvent) { + if (notebookKind !== 'jupyter-notebook') { + return false; + } + if (e.rawEvents.every(event => { + if (event.kind !== NotebookCellsChangeType.ChangeCellMetadata) { + return false; + } + if (JSON.stringify(event.metadata || {}) === JSON.stringify({ execution_count: null, metadata: {} })) { + return true; + } + return true; + + })) { + return true; + } + + return false; +} + +export function calculateNotebookRewriteRatio(cellsDiff: ICellDiffInfo[], originalModel: NotebookTextModel, modifiedModel: NotebookTextModel): number { + const totalNumberOfUpdatedLines = cellsDiff.reduce((totalUpdatedLines, value) => { + const getUpadtedLineCount = () => { + if (value.type === 'unchanged') { + return 0; + } + if (value.type === 'delete') { + return originalModel.cells[value.originalCellIndex].textModel?.getLineCount() ?? 0; + } + if (value.type === 'insert') { + return modifiedModel.cells[value.modifiedCellIndex].textModel?.getLineCount() ?? 0; + } + return value.diff.get().changes.reduce((maxLineNumber, change) => { + return Math.max(maxLineNumber, change.modified.endLineNumberExclusive); + }, 0); + }; + + return totalUpdatedLines + getUpadtedLineCount(); + }, 0); + + const totalNumberOfLines = modifiedModel.cells.reduce((totalLines, cell) => totalLines + (cell.textModel?.getLineCount() ?? 0), 0); + return totalNumberOfLines === 0 ? 0 : Math.min(1, totalNumberOfUpdatedLines / totalNumberOfLines); + +} diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/notebookCellChanges.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/notebookCellChanges.ts new file mode 100644 index 000000000000..376d0ed8d5f2 --- /dev/null +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/notebookCellChanges.ts @@ -0,0 +1,122 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ISettableObservable, ObservablePromise } from '../../../../../../base/common/observable.js'; +import { IDocumentDiff } from '../../../../../../editor/common/diff/documentDiffProvider.js'; +import { DetailedLineRangeMapping } from '../../../../../../editor/common/diff/rangeMapping.js'; +import { ITextModel } from '../../../../../../editor/common/model.js'; + + +/** + * This structure is used to represent the state of a Notebook document compared to the original. + * Its similar to the IDocumentDiff object, that tells us what cells are unmodified, modified, inserted or deleted. + * + * All entries will contain a IDocumentDiff + * Even when there are no changes, diff will contain the number of lines in the document. + * This way we can always calculate the total number of lines in the document. + */ +export type ICellDiffInfo = | + { + originalCellIndex: number; + modifiedCellIndex: number; + type: 'unchanged'; + } & IDocumentDiffWithModelsAndActions | + { + originalCellIndex: number; + modifiedCellIndex: number; + type: 'modified'; + } & IDocumentDiffWithModelsAndActions | + { + modifiedCellIndex: undefined; + originalCellIndex: number; + type: 'delete'; + } & IDocumentDiffWithModelsAndActions | + { + modifiedCellIndex: number; + originalCellIndex: undefined; + type: 'insert'; + } & IDocumentDiffWithModelsAndActions; + + + +interface IDocumentDiffWithModelsAndActions { + /** + * The changes between the original and modified document. + */ + diff: ISettableObservable; + /** + * The original model. + * Cell text models load asynchronously, so this is an observable promise. + */ + originalModel: ObservablePromise; + /** + * The modified model. + * Cell text models load asynchronously, so this is an observable promise. + */ + modifiedModel: ObservablePromise; + keep(changes: DetailedLineRangeMapping): Promise; + undo(changes: DetailedLineRangeMapping): Promise; +} + + +export function countChanges(changes: ICellDiffInfo[]): number { + return changes.reduce((count, change) => { + const diff = change.diff.get(); + // When we accept some of the cell insert/delete the items might still be in the list. + if (diff.identical) { + return count; + } + switch (change.type) { + case 'delete': + return count + 1; // We want to see 1 deleted entry in the pill for navigation + case 'insert': + return count + 1; // We want to see 1 new entry in the pill for navigation + case 'modified': + return count + diff.changes.length; + default: + return count; + } + }, 0); + +} + +export function sortCellChanges(changes: ICellDiffInfo[]): ICellDiffInfo[] { + return [...changes].sort((a, b) => { + // For unchanged and modified, use modifiedCellIndex + if ((a.type === 'unchanged' || a.type === 'modified') && + (b.type === 'unchanged' || b.type === 'modified')) { + return a.modifiedCellIndex - b.modifiedCellIndex; + } + + // For delete entries, use originalCellIndex + if (a.type === 'delete' && b.type === 'delete') { + return a.originalCellIndex - b.originalCellIndex; + } + + // For insert entries, use modifiedCellIndex + if (a.type === 'insert' && b.type === 'insert') { + return a.modifiedCellIndex - b.modifiedCellIndex; + } + + if (a.type === 'delete' && b.type === 'insert') { + return -1; + } + if (a.type === 'insert' && b.type === 'delete') { + return 1; + } + + if ((a.type === 'delete' && b.type !== 'insert') || (a.type !== 'insert' && b.type === 'delete')) { + return a.originalCellIndex - b.originalCellIndex; + } + + // Mixed types: compare based on available indices + const aIndex = a.type === 'delete' ? a.originalCellIndex : + (a.type === 'insert' ? a.modifiedCellIndex : a.modifiedCellIndex); + const bIndex = b.type === 'delete' ? b.originalCellIndex : + (b.type === 'insert' ? b.modifiedCellIndex : b.modifiedCellIndex); + + return aIndex - bIndex; + }); +} diff --git a/src/vs/workbench/contrib/chat/browser/chatEditor.ts b/src/vs/workbench/contrib/chat/browser/chatEditor.ts index 7b89c3d65a62..cccd397d87ed 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditor.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditor.ts @@ -19,11 +19,11 @@ import { Memento } from '../../../common/memento.js'; import { clearChatEditor } from './actions/chatClear.js'; import { ChatEditorInput } from './chatEditorInput.js'; import { ChatWidget, IChatViewState } from './chatWidget.js'; -import { ChatAgentLocation } from '../common/chatAgents.js'; import { IChatModel, IExportableChatData, ISerializableChatData } from '../common/chatModel.js'; import { CHAT_PROVIDER_ID } from '../common/chatParticipantContribTypes.js'; import { IEditorGroup } from '../../../services/editor/common/editorGroupsService.js'; import { EDITOR_DRAG_AND_DROP_BACKGROUND } from '../../../common/theme.js'; +import { ChatAgentLocation } from '../common/constants.js'; export interface IChatEditorOptions extends IEditorOptions { target?: { sessionId: string } | { data: IExportableChatData | ISerializableChatData }; @@ -68,6 +68,7 @@ export class ChatEditor extends EditorPane { undefined, { supportsFileReferences: true, + enableImplicitContext: true, }, { listForeground: editorForeground, diff --git a/src/vs/workbench/contrib/chat/browser/chatEditorInput.ts b/src/vs/workbench/contrib/chat/browser/chatEditorInput.ts index 5bac1b4a5a9e..4f539b30d19a 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditorInput.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditorInput.ts @@ -16,9 +16,9 @@ import { registerIcon } from '../../../../platform/theme/common/iconRegistry.js' import { EditorInputCapabilities, IEditorSerializer, IUntypedEditorInput } from '../../../common/editor.js'; import { EditorInput } from '../../../common/editor/editorInput.js'; import type { IChatEditorOptions } from './chatEditor.js'; -import { ChatAgentLocation } from '../common/chatAgents.js'; import { IChatModel } from '../common/chatModel.js'; import { IChatService } from '../common/chatService.js'; +import { ChatAgentLocation } from '../common/constants.js'; const ChatEditorIcon = registerIcon('chat-editor-label-icon', Codicon.commentDiscussion, nls.localize('chatEditorLabelIcon', 'Icon of the chat editor label.')); diff --git a/src/vs/workbench/contrib/chat/browser/chatFollowups.ts b/src/vs/workbench/contrib/chat/browser/chatFollowups.ts index d600d02a7905..f4d55df96e4a 100644 --- a/src/vs/workbench/contrib/chat/browser/chatFollowups.ts +++ b/src/vs/workbench/contrib/chat/browser/chatFollowups.ts @@ -8,9 +8,10 @@ import { Button, IButtonStyles } from '../../../../base/browser/ui/button/button import { MarkdownString } from '../../../../base/common/htmlContent.js'; import { Disposable } from '../../../../base/common/lifecycle.js'; import { localize } from '../../../../nls.js'; -import { ChatAgentLocation, IChatAgentService } from '../common/chatAgents.js'; +import { IChatAgentService } from '../common/chatAgents.js'; import { formatChatQuestion } from '../common/chatParserTypes.js'; import { IChatFollowup } from '../common/chatService.js'; +import { ChatAgentLocation } from '../common/constants.js'; const $ = dom.$; diff --git a/src/vs/workbench/contrib/chat/browser/chatInlineAnchorWidget.ts b/src/vs/workbench/contrib/chat/browser/chatInlineAnchorWidget.ts index 6849ca7b0817..3752bff4c6be 100644 --- a/src/vs/workbench/contrib/chat/browser/chatInlineAnchorWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/chatInlineAnchorWidget.ts @@ -7,10 +7,8 @@ import * as dom from '../../../../base/browser/dom.js'; import { StandardMouseEvent } from '../../../../base/browser/mouseEvent.js'; import { getDefaultHoverDelegate } from '../../../../base/browser/ui/hover/hoverDelegateFactory.js'; import { KeyCode, KeyMod } from '../../../../base/common/keyCodes.js'; -import { Lazy } from '../../../../base/common/lazy.js'; import { Disposable } from '../../../../base/common/lifecycle.js'; import { URI } from '../../../../base/common/uri.js'; -import { generateUuid } from '../../../../base/common/uuid.js'; import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js'; import { IRange } from '../../../../editor/common/core/range.js'; import { EditorContextKeys } from '../../../../editor/common/editorContextKeys.js'; @@ -92,8 +90,6 @@ export class InlineAnchorWidget extends Disposable { const contextKeyService = this._register(originalContextKeyService.createScoped(element)); this._chatResourceContext = chatAttachmentResourceContextKey.bindTo(contextKeyService); - const anchorId = new Lazy(generateUuid); - element.classList.add(InlineAnchorWidget.className, 'show-file-icons'); let iconText: string; @@ -109,18 +105,6 @@ export class InlineAnchorWidget extends Disposable { iconText = this.data.symbol.name; iconClasses = ['codicon', ...getIconClasses(modelService, languageService, undefined, undefined, SymbolKinds.toIcon(symbol.kind))]; - this._register(dom.addDisposableListener(element, 'click', () => { - telemetryService.publicLog2<{ - anchorId: string; - }, { - anchorId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Unique identifier for the current anchor.' }; - owner: 'mjbvz'; - comment: 'Provides insight into the usage of Chat features.'; - }>('chat.inlineAnchor.openSymbol', { - anchorId: anchorId.value - }); - })); - this._store.add(instantiationService.invokeFunction(accessor => hookUpSymbolAttachmentDragAndContextMenu(accessor, element, contextKeyService, { value: symbol.location, name: symbol.name, kind: symbol.kind }, MenuId.ChatInlineSymbolAnchorContext))); } else { location = this.data; @@ -130,36 +114,32 @@ export class InlineAnchorWidget extends Disposable { `${label}#${location.range.startLineNumber}-${location.range.endLineNumber}` : label; - const fileKind = location.uri.path.endsWith('/') ? FileKind.FOLDER : FileKind.FILE; + let fileKind = location.uri.path.endsWith('/') ? FileKind.FOLDER : FileKind.FILE; const recomputeIconClasses = () => getIconClasses(modelService, languageService, location.uri, fileKind, fileKind === FileKind.FOLDER && !themeService.getFileIconTheme().hasFolderIcons ? FolderThemeIcon : undefined); iconClasses = recomputeIconClasses(); - this._register(themeService.onDidFileIconThemeChange(() => { + const refreshIconClasses = () => { iconEl.classList.remove(...iconClasses); iconClasses = recomputeIconClasses(); iconEl.classList.add(...iconClasses); + }; + + this._register(themeService.onDidFileIconThemeChange(() => { + refreshIconClasses(); })); const isFolderContext = ExplorerFolderContext.bindTo(contextKeyService); fileService.stat(location.uri) .then(stat => { isFolderContext.set(stat.isDirectory); + if (stat.isDirectory) { + fileKind = FileKind.FOLDER; + refreshIconClasses(); + } }) .catch(() => { }); - this._register(dom.addDisposableListener(element, 'click', () => { - telemetryService.publicLog2<{ - anchorId: string; - }, { - anchorId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Unique identifier for the current anchor.' }; - owner: 'mjbvz'; - comment: 'Provides insight into the usage of Chat features.'; - }>('chat.inlineAnchor.openResource', { - anchorId: anchorId.value - }); - })); - // Context menu this._register(dom.addDisposableListener(element, dom.EventType.CONTEXT_MENU, async domEvent => { const event = new StandardMouseEvent(dom.getWindow(domEvent), domEvent); diff --git a/src/vs/workbench/contrib/chat/browser/chatInputPart.ts b/src/vs/workbench/contrib/chat/browser/chatInputPart.ts index e8885e46d438..3d17180ac1d2 100644 --- a/src/vs/workbench/contrib/chat/browser/chatInputPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatInputPart.ts @@ -8,22 +8,19 @@ import { addDisposableListener } from '../../../../base/browser/dom.js'; import { DEFAULT_FONT_FAMILY } from '../../../../base/browser/fonts.js'; import { IHistoryNavigationWidget } from '../../../../base/browser/history.js'; import { StandardKeyboardEvent } from '../../../../base/browser/keyboardEvent.js'; +import { ActionViewItem, IActionViewItemOptions } from '../../../../base/browser/ui/actionbar/actionViewItems.js'; import * as aria from '../../../../base/browser/ui/aria/aria.js'; import { Button } from '../../../../base/browser/ui/button/button.js'; import { IActionProvider } from '../../../../base/browser/ui/dropdown/dropdown.js'; -import { IManagedHoverTooltipMarkdownString } from '../../../../base/browser/ui/hover/hover.js'; -import { IHoverDelegate } from '../../../../base/browser/ui/hover/hoverDelegate.js'; import { createInstantHoverDelegate, getDefaultHoverDelegate } from '../../../../base/browser/ui/hover/hoverDelegateFactory.js'; import { renderLabelWithIcons } from '../../../../base/browser/ui/iconLabel/iconLabels.js'; import { IAction, Separator, toAction } from '../../../../base/common/actions.js'; -import { Promises } from '../../../../base/common/async.js'; import { Codicon } from '../../../../base/common/codicons.js'; import { Emitter, Event } from '../../../../base/common/event.js'; import { HistoryNavigator2 } from '../../../../base/common/history.js'; import { KeyCode } from '../../../../base/common/keyCodes.js'; import { Disposable, DisposableStore, IDisposable, MutableDisposable, toDisposable } from '../../../../base/common/lifecycle.js'; import { ResourceSet } from '../../../../base/common/map.js'; -import { basename, dirname } from '../../../../base/common/path.js'; import { isMacintosh } from '../../../../base/common/platform.js'; import { URI } from '../../../../base/common/uri.js'; import { IEditorConstructionOptions } from '../../../../editor/browser/config/editorConfiguration.js'; @@ -32,10 +29,8 @@ import { CodeEditorWidget } from '../../../../editor/browser/widget/codeEditor/c import { EditorOptions } from '../../../../editor/common/config/editorOptions.js'; import { IDimension } from '../../../../editor/common/core/dimension.js'; import { IPosition } from '../../../../editor/common/core/position.js'; -import { IRange, Range } from '../../../../editor/common/core/range.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; +import { Range } from '../../../../editor/common/core/range.js'; import { ITextModel } from '../../../../editor/common/model.js'; -import { getIconClasses } from '../../../../editor/common/services/getIconClasses.js'; import { IModelService } from '../../../../editor/common/services/model.js'; import { ITextModelService } from '../../../../editor/common/services/resolverService.js'; import { CopyPasteController } from '../../../../editor/contrib/dropOrPasteInto/browser/copyPasteController.js'; @@ -56,10 +51,8 @@ import { ICommandService } from '../../../../platform/commands/common/commands.j import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { IContextKey, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js'; -import { ITextEditorOptions } from '../../../../platform/editor/common/editor.js'; -import { FileKind, IFileService } from '../../../../platform/files/common/files.js'; +import { IFileService } from '../../../../platform/files/common/files.js'; import { registerAndCreateHistoryNavigationContext } from '../../../../platform/history/browser/contextScopedHistoryWidget.js'; -import { IHoverService } from '../../../../platform/hover/browser/hover.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { ServiceCollection } from '../../../../platform/instantiation/common/serviceCollection.js'; import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; @@ -67,39 +60,43 @@ import { ILabelService } from '../../../../platform/label/common/label.js'; import { WorkbenchList } from '../../../../platform/list/browser/listService.js'; import { ILogService } from '../../../../platform/log/common/log.js'; import { INotificationService } from '../../../../platform/notification/common/notification.js'; -import { IOpenerService, type OpenInternalOptions } from '../../../../platform/opener/common/opener.js'; import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; -import { FolderThemeIcon, IThemeService } from '../../../../platform/theme/common/themeService.js'; -import { IFileLabelOptions, ResourceLabels } from '../../../browser/labels.js'; +import { IThemeService } from '../../../../platform/theme/common/themeService.js'; +import { ResourceLabels } from '../../../browser/labels.js'; import { ACTIVE_GROUP, IEditorService, SIDE_GROUP } from '../../../services/editor/common/editorService.js'; import { AccessibilityVerbositySettingId } from '../../accessibility/browser/accessibilityConfiguration.js'; import { AccessibilityCommandId } from '../../accessibility/common/accessibilityCommands.js'; import { getSimpleCodeEditorWidgetOptions, getSimpleEditorOptions, setupSimpleEditorSelectionStyling } from '../../codeEditor/browser/simpleEditorOptions.js'; -import { revealInSideBarCommand } from '../../files/browser/fileActions.contribution.js'; -import { ChatAgentLocation, IChatAgentService } from '../common/chatAgents.js'; +import { IChatAgentService } from '../common/chatAgents.js'; import { ChatContextKeys } from '../common/chatContextKeys.js'; -import { IChatEditingSession, WorkingSetEntryRemovalReason, WorkingSetEntryState } from '../common/chatEditingService.js'; -import { IChatRequestVariableEntry, isPasteVariableEntry } from '../common/chatModel.js'; -import { IChatFollowup } from '../common/chatService.js'; +import { IChatEditingSession } from '../common/chatEditingService.js'; +import { ChatEntitlement, IChatEntitlementService } from '../common/chatEntitlementService.js'; +import { IChatRequestVariableEntry, isImageVariableEntry, isLinkVariableEntry, isPasteVariableEntry } from '../common/chatModel.js'; +import { IChatFollowup, IChatService } from '../common/chatService.js'; import { IChatVariablesService } from '../common/chatVariables.js'; import { IChatResponseViewModel } from '../common/chatViewModel.js'; import { ChatInputHistoryMaxEntries, IChatHistoryEntry, IChatInputState, IChatWidgetHistoryService } from '../common/chatWidgetHistoryService.js'; +import { ChatAgentLocation, ChatMode } from '../common/constants.js'; import { ILanguageModelChatMetadataAndIdentifier, ILanguageModelsService } from '../common/languageModels.js'; -import { CancelAction, ChatModelPickerActionId, ChatSubmitAction, ChatSubmitSecondaryAgentAction, IChatExecuteActionContext, IToggleAgentModeArgs, ToggleAgentModeActionId } from './actions/chatExecuteActions.js'; +import { CancelAction, ChatSubmitAction, ChatSubmitSecondaryAgentAction, ChatSwitchToNextModelActionId, IChatExecuteActionContext, IToggleChatModeArgs, ToggleAgentModeActionId } from './actions/chatExecuteActions.js'; +import { AttachToolsAction } from './actions/chatToolActions.js'; import { ImplicitContextAttachmentWidget } from './attachments/implicitContextAttachment.js'; -import { InstructionAttachmentsWidget } from './attachments/instructionsAttachment/instructionAttachments.js'; +import { PromptAttachmentsCollectionWidget } from './attachments/promptAttachments/promptAttachmentsCollectionWidget.js'; import { IChatWidget } from './chat.js'; import { ChatAttachmentModel } from './chatAttachmentModel.js'; -import { toChatVariable } from './chatAttachmentModel/chatInstructionAttachmentsModel.js'; -import { hookUpResourceAttachmentDragAndContextMenu, hookUpSymbolAttachmentDragAndContextMenu } from './chatContentParts/chatAttachmentsContentPart.js'; +import { toChatVariable } from './chatAttachmentModel/chatPromptAttachmentsCollection.js'; +import { DefaultChatAttachmentWidget, FileAttachmentWidget, ImageAttachmentWidget, LinkAttachmentWidget, PasteAttachmentWidget } from './chatAttachmentWidgets.js'; import { IDisposableReference } from './chatContentParts/chatCollections.js'; import { CollapsibleListPool, IChatCollapsibleListItem } from './chatContentParts/chatReferencesContentPart.js'; -import { ChatDragAndDrop, EditsDragAndDrop } from './chatDragAndDrop.js'; +import { ChatDragAndDrop } from './chatDragAndDrop.js'; import { ChatEditingRemoveAllFilesAction, ChatEditingShowChangesAction } from './chatEditing/chatEditingActions.js'; import { ChatFollowups } from './chatFollowups.js'; +import { ChatSelectedTools } from './chatSelectedTools.js'; import { IChatViewState } from './chatWidget.js'; import { ChatFileReference } from './contrib/chatDynamicVariables/chatFileReference.js'; import { ChatImplicitContext } from './contrib/chatImplicitContext.js'; +import { ChatRelatedFiles } from './contrib/chatInputRelatedFilesContrib.js'; +import { resizeImage } from './imageUtils.js'; const $ = dom.$; @@ -121,11 +118,12 @@ interface IChatInputPartOptions { }; editorOverflowWidgetsDomNode?: HTMLElement; renderWorkingSet?: boolean; + enableImplicitContext?: boolean; + supportsChangingModes?: boolean; } export interface IWorkingSetEntry { uri: URI; - isMarkedReadonly?: boolean; } export class ChatInputPart extends Disposable implements IHistoryNavigationWidget { @@ -155,6 +153,8 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge return this._attachmentModel; } + readonly selectedToolsModel: ChatSelectedTools; + public getAttachedAndImplicitContext(sessionId: string): IChatRequestVariableEntry[] { const contextArr = [...this.attachmentModel.attachments]; if (this.implicitContext?.enabled && this.implicitContext.value) { @@ -181,7 +181,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge }); } - // factor in nested file references into the implicit context + // factor in nested file links of a prompt into the implicit context const variables = this.variableService.getDynamicVariables(sessionId); for (const variable of variables) { if (!(variable instanceof ChatFileReference)) { @@ -195,10 +195,6 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge return toChatVariable(link, false); }), ); - // then add the root reference itself - contextArr.push( - toChatVariable(variable, true), - ); } contextArr @@ -221,6 +217,11 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge return this._implicitContext; } + private _relatedFiles: ChatRelatedFiles | undefined; + public get relatedFiles(): ChatRelatedFiles | undefined { + return this._relatedFiles; + } + private _hasFileAttachmentContextKey: IContextKey; private readonly _onDidChangeVisibility = this._register(new Emitter()); @@ -235,6 +236,8 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge private followupsContainer!: HTMLElement; private readonly followupsDisposables = this._register(new DisposableStore()); + private attachmentsContainer!: HTMLElement; + private attachedContextContainer!: HTMLElement; private readonly attachedContextDisposables = this._register(new MutableDisposable()); @@ -263,6 +266,8 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge private executeToolbar!: MenuWorkbenchToolBar; private inputActionsToolbar!: MenuWorkbenchToolBar; + private addFilesToolbar: MenuWorkbenchToolBar | undefined; + get inputEditor() { return this._inputEditor; } @@ -276,10 +281,12 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge private inputEditorHasText: IContextKey; private chatCursorAtTop: IContextKey; private inputEditorHasFocus: IContextKey; + private agentMode: IContextKey; /** * Context key is set when prompt instructions are attached. */ private promptInstructionsAttached: IContextKey; + private chatMode: IContextKey; private readonly _waitForPersistedLanguageModel = this._register(new MutableDisposable()); private _onDidChangeCurrentLanguageModel = this._register(new Emitter()); @@ -288,6 +295,20 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge return this._currentLanguageModel?.identifier; } + private _onDidChangeCurrentChatMode = this._register(new Emitter()); + readonly onDidChangeCurrentChatMode = this._onDidChangeCurrentChatMode.event; + + private _currentMode: ChatMode = ChatMode.Chat; + public get currentMode(): ChatMode { + if (this.location === ChatAgentLocation.Panel && !this.chatService.unifiedViewEnabled) { + return ChatMode.Chat; + } + + return this._currentMode === ChatMode.Agent && !this.agentService.hasToolsAgent ? + ChatMode.Edit : + this._currentMode; + } + private cachedDimensions: dom.Dimension | undefined; private cachedExecuteToolbarWidth: number | undefined; private cachedInputToolbarWidth: number | undefined; @@ -323,9 +344,9 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge /** * Child widget of prompt instruction attachments. - * See {@linkcode InstructionAttachmentsWidget}. + * See {@linkcode PromptAttachmentsCollectionWidget}. */ - private instructionAttachmentsPart: InstructionAttachmentsWidget; + private instructionAttachmentsPart: PromptAttachmentsCollectionWidget; constructor( // private readonly editorOptions: ChatEditorOptions, // TODO this should be used @@ -335,7 +356,6 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge getContribsInputState: () => any, @IChatWidgetHistoryService private readonly historyService: IChatWidgetHistoryService, @IModelService private readonly modelService: IModelService, - @ILanguageService private readonly languageService: ILanguageService, @IInstantiationService private readonly instantiationService: IInstantiationService, @IContextKeyService private readonly contextKeyService: IContextKeyService, @IConfigurationService private readonly configurationService: IConfigurationService, @@ -343,31 +363,27 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge @IAccessibilityService private readonly accessibilityService: IAccessibilityService, @ILanguageModelsService private readonly languageModelsService: ILanguageModelsService, @ILogService private readonly logService: ILogService, - @IHoverService private readonly hoverService: IHoverService, @IFileService private readonly fileService: IFileService, - @ICommandService private readonly commandService: ICommandService, @IEditorService private readonly editorService: IEditorService, - @IOpenerService private readonly openerService: IOpenerService, @IThemeService private readonly themeService: IThemeService, @ITextModelService private readonly textModelResolverService: ITextModelService, @IStorageService private readonly storageService: IStorageService, @ILabelService private readonly labelService: ILabelService, @IChatVariablesService private readonly variableService: IChatVariablesService, - @IChatAgentService private readonly chatAgentService: IChatAgentService, + @IChatAgentService private readonly agentService: IChatAgentService, + @IChatService private readonly chatService: IChatService, ) { super(); this._attachmentModel = this._register(this.instantiationService.createInstance(ChatAttachmentModel)); - if (this.location === ChatAgentLocation.EditingSession) { - this.dnd = this._register(this.instantiationService.createInstance(EditsDragAndDrop, this.attachmentModel, styles)); - } else { - this.dnd = this._register(this.instantiationService.createInstance(ChatDragAndDrop, this.attachmentModel, styles)); - } + this.selectedToolsModel = this._register(this.instantiationService.createInstance(ChatSelectedTools)); + this.dnd = this._register(this.instantiationService.createInstance(ChatDragAndDrop, this._attachmentModel, styles)); this.getInputState = (): IChatInputState => { return { ...getContribsInputState(), chatContextAttachments: this._attachmentModel.attachments, + chatMode: this._currentMode, }; }; this.inputEditorMaxHeight = this.options.renderStyle === 'compact' ? INPUT_EDITOR_MAX_HEIGHT / 3 : INPUT_EDITOR_MAX_HEIGHT; @@ -376,6 +392,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge this.chatCursorAtTop = ChatContextKeys.inputCursorAtTop.bindTo(contextKeyService); this.inputEditorHasFocus = ChatContextKeys.inputHasFocus.bindTo(contextKeyService); this.promptInstructionsAttached = ChatContextKeys.instructionsAttached.bindTo(contextKeyService); + this.agentMode = ChatContextKeys.Editing.agentMode.bindTo(contextKeyService); this.history = this.loadHistory(); this._register(this.historyService.onDidClearHistory(() => this.history = new HistoryNavigator2([{ text: '' }], ChatInputHistoryMaxEntries, historyKeyFn))); @@ -392,7 +409,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge this.instructionAttachmentsPart = this._register( instantiationService.createInstance( - InstructionAttachmentsWidget, + PromptAttachmentsCollectionWidget, this.attachmentModel.promptInstructions, this._contextResourceLabels, ), @@ -415,8 +432,8 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge if (persistedSelection) { const model = this.languageModelsService.lookupLanguageModel(persistedSelection); if (model) { - this._currentLanguageModel = { metadata: model, identifier: persistedSelection }; - this._onDidChangeCurrentLanguageModel.fire(this._currentLanguageModel); + this.setCurrentLanguageModel({ metadata: model, identifier: persistedSelection }); + this.checkModelSupported(); } else { this._waitForPersistedLanguageModel.value = this.languageModelsService.onDidChangeLanguageModels(e => { const persistedModel = e.added?.find(m => m.identifier === persistedSelection); @@ -424,34 +441,56 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge this._waitForPersistedLanguageModel.clear(); if (persistedModel.metadata.isUserSelectable) { - this._currentLanguageModel = { metadata: persistedModel.metadata, identifier: persistedSelection }; - this._onDidChangeCurrentLanguageModel.fire(this._currentLanguageModel!); + this.setCurrentLanguageModel({ metadata: persistedModel.metadata, identifier: persistedSelection }); + this.checkModelSupported(); } } }); } } - this._register(this.chatAgentService.onDidChangeToolsAgentModeEnabled(() => { - if (this._currentLanguageModel && !this.modelSupportedForDefaultAgent(this._currentLanguageModel)) { - this.setCurrentLanguageModelToDefault(); - } + this._register(this._onDidChangeCurrentChatMode.event(() => { + this.checkModelSupported(); })); } - private supportsVision(): boolean { - return this._currentLanguageModel?.metadata.capabilities?.vision ?? false; + public switchToNextModel(): void { + const models = this.getModels(); + if (models.length > 0) { + const currentIndex = models.findIndex(model => model.identifier === this._currentLanguageModel?.identifier); + const nextIndex = (currentIndex + 1) % models.length; + this.setCurrentLanguageModel(models[nextIndex]); + } + } + + private checkModelSupported(): void { + if (this._currentLanguageModel && !this.modelSupportedForDefaultAgent(this._currentLanguageModel)) { + this.setCurrentLanguageModelToDefault(); + } + } + + setChatMode(mode: ChatMode): void { + if (!this.options.supportsChangingModes) { + return; + } + + this._currentMode = mode; + this.chatMode.set(mode); + this._onDidChangeCurrentChatMode.fire(); + this.agentMode.set(this._currentMode === ChatMode.Agent); } private modelSupportedForDefaultAgent(model: ILanguageModelChatMetadataAndIdentifier): boolean { // Probably this logic could live in configuration on the agent, or somewhere else, if it gets more complex - if (this.chatAgentService.getDefaultAgent(this.location)?.isToolsAgent) { + if (this.currentMode === ChatMode.Agent || (this.currentMode === ChatMode.Edit && this.chatService.unifiedViewEnabled)) { if (this.configurationService.getValue('chat.agent.allModels')) { return true; } + const supportsToolsAgent = typeof model.metadata.capabilities?.agentMode === 'undefined' || model.metadata.capabilities.agentMode; + // Filter out models that don't support tool calling, and models that don't support enough context to have a good experience with the tools agent - return !!model.metadata.capabilities?.toolCalling && model.metadata.maxInputTokens > 40000; + return supportsToolsAgent && !!model.metadata.capabilities?.toolCalling; } return true; @@ -472,21 +511,25 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge const model = this.languageModelsService.lookupLanguageModel(id); return model?.isUserSelectable && !model.isDefault; }); - this._currentLanguageModel = hasUserSelectableLanguageModels && defaultLanguageModelId ? + const defaultModel = hasUserSelectableLanguageModels && defaultLanguageModelId ? { metadata: this.languageModelsService.lookupLanguageModel(defaultLanguageModelId)!, identifier: defaultLanguageModelId } : undefined; + if (defaultModel) { + this.setCurrentLanguageModel(defaultModel); + } } - private setCurrentLanguageModelByUser(model: ILanguageModelChatMetadataAndIdentifier) { + private setCurrentLanguageModel(model: ILanguageModelChatMetadataAndIdentifier) { this._currentLanguageModel = model; - // The user changed the language model, so we don't wait for the persisted option to be registered - this._waitForPersistedLanguageModel.clear(); if (this.cachedDimensions) { + // For quick chat and editor chat, relayout because the input may need to shrink to accomodate the model name this.layout(this.cachedDimensions.height, this.cachedDimensions.width); } this.storageService.store(this.getSelectedModelStorageKey(), model.identifier, StorageScope.APPLICATION, StorageTarget.USER); + + this._onDidChangeCurrentLanguageModel.fire(model); } private loadHistory(): HistoryNavigator2 { @@ -519,6 +562,14 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge if (state.inputValue) { this.setValue(state.inputValue, false); } + + if (state.inputState?.chatMode) { + this.setChatMode(state.inputState.chatMode); + } else if (this.location === ChatAgentLocation.EditingSession) { + this.setChatMode(ChatMode.Edit); + } + + this.selectedToolsModel.reset(); } logInputHistory(): void { @@ -534,12 +585,13 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge return this.container; } - showPreviousValue(): void { + async showPreviousValue(): Promise { const inputState = this.getInputState(); if (this.history.isAtEnd()) { this.saveCurrentValue(inputState); } else { - if (!this.history.has({ text: this._inputEditor.getValue(), state: inputState })) { + const currentEntry = this.getFilteredEntry(this._inputEditor.getValue(), inputState); + if (!this.history.has(currentEntry)) { this.saveCurrentValue(inputState); this.history.resetCursor(); } @@ -548,12 +600,13 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge this.navigateHistory(true); } - showNextValue(): void { + async showNextValue(): Promise { const inputState = this.getInputState(); if (this.history.isAtEnd()) { return; } else { - if (!this.history.has({ text: this._inputEditor.getValue(), state: inputState })) { + const currentEntry = this.getFilteredEntry(this._inputEditor.getValue(), inputState); + if (!this.history.has(currentEntry)) { this.saveCurrentValue(inputState); this.history.resetCursor(); } @@ -562,11 +615,30 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge this.navigateHistory(false); } - private navigateHistory(previous: boolean): void { + private async navigateHistory(previous: boolean): Promise { const historyEntry = previous ? this.history.previous() : this.history.next(); - const historyAttachments = historyEntry.state?.chatContextAttachments ?? []; + let historyAttachments = historyEntry.state?.chatContextAttachments ?? []; + + // Check for images in history to restore the value. + if (historyAttachments.length > 0) { + historyAttachments = (await Promise.all(historyAttachments.map(async (attachment) => { + if (attachment.isImage && attachment.references?.length && URI.isUri(attachment.references[0].reference)) { + try { + const buffer = await this.fileService.readFile(attachment.references[0].reference); + const newAttachment = { ...attachment }; + newAttachment.value = (isImageVariableEntry(attachment) && attachment.isPasted) ? buffer.value.buffer : await resizeImage(buffer.value.buffer); // if pasted image, we do not need to resize. + return newAttachment; + } catch (err) { + this.logService.error('Failed to restore image from history', err); + return undefined; + } + } + return attachment; + }))).filter(attachment => attachment !== undefined); + } + this._attachmentModel.clearAndSetContext(...historyAttachments); aria.status(historyEntry.text); @@ -606,8 +678,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge } private saveCurrentValue(inputState: IChatInputState): void { - inputState.chatContextAttachments = inputState.chatContextAttachments?.filter(attachment => !attachment.isImage); - const newEntry = { text: this._inputEditor.getValue(), state: inputState }; + const newEntry = this.getFilteredEntry(this._inputEditor.getValue(), inputState); this.history.replaceLast(newEntry); } @@ -627,8 +698,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge if (isUserQuery) { const userQuery = this._inputEditor.getValue(); const inputState = this.getInputState(); - inputState.chatContextAttachments = inputState.chatContextAttachments?.filter(attachment => !attachment.isImage); - const entry: IChatHistoryEntry = { text: userQuery, state: inputState }; + const entry = this.getFilteredEntry(userQuery, inputState); this.history.replaceLast(entry); this.history.add({ text: '' }); } @@ -644,6 +714,26 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge } } + // A funtion that filters out specifically the `value` property of the attachment. + private getFilteredEntry(query: string, inputState: IChatInputState): IChatHistoryEntry { + const attachmentsWithoutImageValues = inputState.chatContextAttachments?.map(attachment => { + if (attachment.isImage && attachment.references?.length && attachment.value) { + const newAttachment = { ...attachment }; + newAttachment.value = undefined; + return newAttachment; + } + return attachment; + }); + + inputState.chatContextAttachments = attachmentsWithoutImageValues; + const newEntry = { + text: query, + state: inputState, + }; + + return newEntry; + } + private _acceptInputForVoiceover(): void { const domNode = this._inputEditor.getDomNode(); if (!domNode) { @@ -674,8 +764,11 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge dom.h('.chat-input-toolbars@inputToolbars'), ]), ]), - dom.h('.chat-attached-context@attachedContextContainer'), - dom.h('.chat-related-files@relatedFilesContainer'), + dom.h('.chat-attachments-container@attachmentsContainer', [ + dom.h('.chat-attachment-toolbar@attachmentToolbar'), + dom.h('.chat-attached-context@attachedContextContainer'), + dom.h('.chat-related-files@relatedFilesContainer'), + ]), dom.h('.interactive-input-followups@followupsContainer'), ]) ]); @@ -685,8 +778,11 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge dom.h('.chat-editing-session@chatEditingSessionWidgetContainer'), dom.h('.interactive-input-and-side-toolbar@inputAndSideToolbar', [ dom.h('.chat-input-container@inputContainer', [ - dom.h('.chat-attached-context@attachedContextContainer'), - dom.h('.chat-related-files@relatedFilesContainer'), + dom.h('.chat-attachments-container@attachmentsContainer', [ + dom.h('.chat-attachment-toolbar@attachmentToolbar'), + dom.h('.chat-related-files@relatedFilesContainer'), + dom.h('.chat-attached-context@attachedContextContainer'), + ]), dom.h('.chat-editor-container@editorContainer'), dom.h('.chat-input-toolbars@inputToolbars'), ]), @@ -700,16 +796,26 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge const inputAndSideToolbar = elements.inputAndSideToolbar; // The chat input and toolbar to the right const inputContainer = elements.inputContainer; // The chat editor, attachments, and toolbars const editorContainer = elements.editorContainer; + this.attachmentsContainer = elements.attachmentsContainer; this.attachedContextContainer = elements.attachedContextContainer; this.relatedFilesContainer = elements.relatedFilesContainer; const toolbarsContainer = elements.inputToolbars; + const attachmentToolbarContainer = elements.attachmentToolbar; this.chatEditingSessionWidgetContainer = elements.chatEditingSessionWidgetContainer; - this.renderAttachedContext(widget); - this._implicitContext = this._register(new ChatImplicitContext()); - this._register(this._implicitContext.onDidChangeValue(() => this._handleAttachedContextChange())); + if (this.options.enableImplicitContext) { + this._implicitContext = this._register(new ChatImplicitContext()); + this._register(this._implicitContext.onDidChangeValue(() => this._handleAttachedContextChange())); + } + this.renderAttachedContext(); this._register(this._attachmentModel.onDidChangeContext(() => this._handleAttachedContextChange())); - this.renderChatEditingSessionState(null, widget); + this.renderChatEditingSessionState(null); + + if (this.options.renderWorkingSet) { + this._relatedFiles = this._register(new ChatRelatedFiles()); + this._register(this._relatedFiles.onDidChange(() => this.renderChatRelatedFiles())); + } + this.renderChatRelatedFiles(); this.dnd.addOverlay(container, container); @@ -734,7 +840,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge options.wrappingStrategy = 'advanced'; options.bracketPairColorization = { enabled: false }; options.suggest = { - showIcons: false, + showIcons: true, showSnippets: false, showWords: true, showStatusBar: false, @@ -747,7 +853,10 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge const editorOptions = getSimpleCodeEditorWidgetOptions(); editorOptions.contributions?.push(...EditorExtensionsRegistry.getSomeEditorContributions([ContentHoverController.ID, GlyphHoverController.ID, CopyPasteController.ID, LinkDetector.ID])); this._inputEditor = this._register(scopedInstantiationService.createInstance(CodeEditorWidget, this._inputEditorElement, options, editorOptions)); + SuggestController.get(this._inputEditor)?.forceRenderingAbove(); + options.overflowWidgetsDomNode?.classList.add('hideSuggestTextIcons'); + this._inputEditorElement.classList.add('hideSuggestTextIcons'); this._register(this._inputEditor.onDidChangeModelContent(() => { const currentHeight = Math.min(this._inputEditor.getContentHeight(), this.inputEditorMaxHeight); @@ -785,6 +894,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge const hoverDelegate = this._register(createInstantHoverDelegate()); this._register(dom.addStandardDisposableListener(toolbarsContainer, dom.EventType.CLICK, e => this.inputEditor.focus())); + this._register(dom.addStandardDisposableListener(this.attachmentsContainer, dom.EventType.CLICK, e => this.inputEditor.focus())); this.inputActionsToolbar = this._register(this.instantiationService.createInstance(MenuWorkbenchToolBar, toolbarsContainer, MenuId.ChatInput, { telemetrySource: this.options.menus.telemetrySource, menuOptions: { shouldForwardArgs: true }, @@ -812,7 +922,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge } } - if (action.id === ChatModelPickerActionId && action instanceof MenuItemAction) { + if (action.id === ChatSwitchToNextModelActionId && action instanceof MenuItemAction) { if (!this._currentLanguageModel) { this.setCurrentLanguageModelToDefault(); } @@ -821,15 +931,21 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge const itemDelegate: ModelPickerDelegate = { onDidChangeModel: this._onDidChangeCurrentLanguageModel.event, setModel: (model: ILanguageModelChatMetadataAndIdentifier) => { - this.setCurrentLanguageModelByUser(model); - this.renderAttachedContext(widget); + // The user changed the language model, so we don't wait for the persisted option to be registered + this._waitForPersistedLanguageModel.clear(); + this.setCurrentLanguageModel(model); + this.renderAttachedContext(); }, getModels: () => this.getModels() }; return this.instantiationService.createInstance(ModelPickerActionViewItem, action, this._currentLanguageModel, itemDelegate); } } else if (action.id === ToggleAgentModeActionId && action instanceof MenuItemAction) { - return this.instantiationService.createInstance(ToggleAgentActionViewItem, action); + const delegate: IModePickerDelegate = { + getMode: () => this.currentMode, + onDidChangeMode: this._onDidChangeCurrentChatMode.event + }; + return this.instantiationService.createInstance(ToggleChatModeActionViewItem, action, delegate); } return undefined; @@ -899,29 +1015,35 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge onDidChangeCursorPosition(); this._register(this.themeService.onDidFileIconThemeChange(() => { - this.renderAttachedContext(widget); + this.renderAttachedContext(); })); - } - private renderAddFilesHint(container: HTMLElement, hoverDelegate: IHoverDelegate, store: DisposableStore, chatWidget: IChatWidget) { - const button = store.add(new Button(container, { - supportIcons: true, - secondary: true, - hoverDelegate + this.addFilesToolbar = this._register(this.instantiationService.createInstance(MenuWorkbenchToolBar, attachmentToolbarContainer, MenuId.ChatInputAttachmentToolbar, { + telemetrySource: this.options.menus.telemetrySource, + label: true, + menuOptions: { shouldForwardArgs: true, renderShortTitle: true }, + hiddenItemStrategy: HiddenItemStrategy.Ignore, + hoverDelegate, + actionViewItemProvider: (action, options) => { + if (action.id === 'workbench.action.chat.editing.attachContext' || action.id === 'workbench.action.chat.attachContext') { + const viewItem = this.instantiationService.createInstance(AddFilesButton, undefined, action, options); + return viewItem; + } + if (action.id === AttachToolsAction.id) { + return this.selectedToolsModel.toolsActionItemViewItemProvider(action, options); + } + return undefined; + } })); - button.element.classList.add('chat-attached-context-attachment', 'chat-add-files'); - button.label = localize('chatAddFiles', '{0} Add Files...', '$(attach)'); - const commandId = 'workbench.action.chat.editing.attachFiles'; - const kb = this.keybindingService.lookupKeybinding(commandId)?.getLabel(); - const title = localize('attachFiles.label', 'Attach files to your request{0}', kb ? ` (${kb})` : ''); - button.setTitle(title); - store.add(button.onDidClick(() => { - this.commandService.executeCommand(commandId, { widget: chatWidget, placeholder: localize('chatAttachFiles', 'Search for files and context to add to your request') }); + this.addFilesToolbar.context = { widget, placeholder: localize('chatAttachFiles', 'Search for files and context to add to your request') }; + this._register(this.addFilesToolbar.onDidChangeMenuItems(() => { + if (this.cachedDimensions) { + this.layout(this.cachedDimensions.height, this.cachedDimensions.width); + } })); - dom.append(container, button.element); } - private async renderAttachedContext(widget?: IChatWidget) { + private renderAttachedContext() { const container = this.attachedContextContainer; const oldHeight = container.offsetHeight; const store = new DisposableStore(); @@ -929,11 +1051,11 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge dom.clearNode(container); const hoverDelegate = store.add(createInstantHoverDelegate()); - if (widget && this.options.renderWorkingSet) { - this.renderAddFilesHint(container, hoverDelegate, store, widget); - } + const attachments = [...this.attachmentModel.attachments.entries()]; - dom.setVisibility(Boolean(widget && this.options.renderWorkingSet) || Boolean(attachments.length) || Boolean(this.implicitContext?.value) || !this.instructionAttachmentsPart.empty, this.attachedContextContainer); + const hasAttachments = Boolean(attachments.length) || Boolean(this.implicitContext?.value) || !this.instructionAttachmentsPart.empty; + dom.setVisibility(Boolean(hasAttachments || (this.addFilesToolbar && !this.addFilesToolbar.isEmpty())), this.attachmentsContainer); + dom.setVisibility(hasAttachments, this.attachedContextContainer); if (!attachments.length) { this._indexOfLastAttachedContextDeletedWithKeyboard = -1; } @@ -944,159 +1066,29 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge } this.promptInstructionsAttached.set(!this.instructionAttachmentsPart.empty); - container.appendChild(this.instructionAttachmentsPart.domNode); + this.instructionAttachmentsPart.render(container); - const attachmentInitPromises: Promise[] = []; for (const [index, attachment] of attachments) { - const widget = dom.append(container, $('.chat-attached-context-attachment.show-file-icons')); - const label = this._contextResourceLabels.create(widget, { supportIcons: true, hoverDelegate, hoverTargetOverride: widget }); + const resource = URI.isUri(attachment.value) ? attachment.value : attachment.value && typeof attachment.value === 'object' && 'uri' in attachment.value && URI.isUri(attachment.value.uri) ? attachment.value.uri : undefined; + const range = attachment.value && typeof attachment.value === 'object' && 'range' in attachment.value && Range.isIRange(attachment.value.range) ? attachment.value.range : undefined; + const shouldFocusClearButton = index === Math.min(this._indexOfLastAttachedContextDeletedWithKeyboard, this.attachmentModel.size - 1); - let ariaLabel: string | undefined; - - let resource = URI.isUri(attachment.value) ? attachment.value : attachment.value && typeof attachment.value === 'object' && 'uri' in attachment.value && URI.isUri(attachment.value.uri) ? attachment.value.uri : undefined; - let range = attachment.value && typeof attachment.value === 'object' && 'range' in attachment.value && Range.isIRange(attachment.value.range) ? attachment.value.range : undefined; + let attachmentWidget; if (resource && (attachment.isFile || attachment.isDirectory)) { - const fileBasename = basename(resource.path); - const fileDirname = dirname(resource.path); - const friendlyName = `${fileBasename} ${fileDirname}`; - ariaLabel = range ? localize('chat.fileAttachmentWithRange', "Attached file, {0}, line {1} to line {2}", friendlyName, range.startLineNumber, range.endLineNumber) : localize('chat.fileAttachment', "Attached file, {0}", friendlyName); - - if (attachment.isOmitted) { - this.customAttachment(widget, friendlyName, hoverDelegate, ariaLabel, store); - } else { - const fileOptions: IFileLabelOptions = { hidePath: true }; - label.setFile(resource, attachment.isFile ? { - ...fileOptions, - fileKind: FileKind.FILE, - range, - } : { - ...fileOptions, - fileKind: FileKind.FOLDER, - icon: !this.themeService.getFileIconTheme().hasFolderIcons ? FolderThemeIcon : undefined - }); - } - - this.attachButtonAndDisposables(widget, index, attachment, hoverDelegate); - this.instantiationService.invokeFunction(accessor => { - if (resource) { - store.add(hookUpResourceAttachmentDragAndContextMenu(accessor, widget, resource)); - } - }); + attachmentWidget = this.instantiationService.createInstance(FileAttachmentWidget, resource, range, attachment, this._currentLanguageModel, shouldFocusClearButton, container, this._contextResourceLabels, hoverDelegate); } else if (attachment.isImage) { - ariaLabel = localize('chat.imageAttachment', "Attached image, {0}", attachment.name); - const supportsVision = this.supportsVision(); - const hoverElement = this.customAttachment(widget, attachment.name, hoverDelegate, ariaLabel, store, attachment.isImage, supportsVision); - - if (attachment.references) { - widget.style.cursor = 'pointer'; - const clickHandler = () => { - if (attachment.references && URI.isUri(attachment.references[0].reference)) { - this.openResource(attachment.references[0].reference, false, undefined); - } - }; - store.add(addDisposableListener(widget, 'click', clickHandler)); - } - - if (supportsVision) { - attachmentInitPromises.push(Promises.withAsyncBody(async (resolve) => { - let buffer: Uint8Array; - try { - if (attachment.value instanceof URI) { - const readFile = await this.fileService.readFile(attachment.value); - if (store.isDisposed) { - return; - } - buffer = readFile.value.buffer; - } else { - buffer = attachment.value as Uint8Array; - } - this.createImageElements(buffer, widget, hoverElement); - } catch (error) { - console.error('Error processing attachment:', error); - } - store.add(this.hoverService.setupManagedHover(hoverDelegate, widget, hoverElement, { trapFocus: false })); - resolve(); - })); - } - - this.attachButtonAndDisposables(widget, index, attachment, hoverDelegate); - widget.style.position = 'relative'; + attachmentWidget = this.instantiationService.createInstance(ImageAttachmentWidget, resource, attachment, this._currentLanguageModel, shouldFocusClearButton, container, this._contextResourceLabels, hoverDelegate); } else if (isPasteVariableEntry(attachment)) { - ariaLabel = localize('chat.attachment', "Attached context, {0}", attachment.name); - - const classNames = ['file-icon', `${attachment.language}-lang-file-icon`]; - if (attachment.copiedFrom) { - resource = attachment.copiedFrom.uri; - range = attachment.copiedFrom.range; - const filename = basename(resource.path); - label.setLabel(filename, undefined, { extraClasses: classNames }); - } else { - label.setLabel(attachment.fileName, undefined, { extraClasses: classNames }); - } - widget.appendChild(dom.$('span.attachment-additional-info', {}, `Pasted ${attachment.pastedLines}`)); - - widget.style.position = 'relative'; - - const hoverContent: IManagedHoverTooltipMarkdownString = { - markdown: { - value: `${attachment.copiedFrom ? this.labelService.getUriLabel(attachment.copiedFrom.uri, { relative: true }) : attachment.fileName}\n\n---\n\n\`\`\`${attachment.language}\n\n${attachment.code}\n\`\`\``, - }, - markdownNotSupportedFallback: attachment.code, - }; - store.add(this.hoverService.setupManagedHover(hoverDelegate, widget, hoverContent, { trapFocus: true })); - this.attachButtonAndDisposables(widget, index, attachment, hoverDelegate); - - const copiedFromResource = attachment.copiedFrom?.uri; - if (copiedFromResource) { - store.add(this.instantiationService.invokeFunction(accessor => hookUpResourceAttachmentDragAndContextMenu(accessor, widget, copiedFromResource))); - } - + attachmentWidget = this.instantiationService.createInstance(PasteAttachmentWidget, attachment, this._currentLanguageModel, shouldFocusClearButton, container, this._contextResourceLabels, hoverDelegate); + } else if (isLinkVariableEntry(attachment)) { + attachmentWidget = this.instantiationService.createInstance(LinkAttachmentWidget, attachment, this._currentLanguageModel, shouldFocusClearButton, container, this._contextResourceLabels, hoverDelegate); } else { - const attachmentLabel = attachment.fullName ?? attachment.name; - const withIcon = attachment.icon?.id ? `$(${attachment.icon.id}) ${attachmentLabel}` : attachmentLabel; - label.setLabel(withIcon, undefined); - - ariaLabel = localize('chat.attachment', "Attached context, {0}", attachment.name); - - this.attachButtonAndDisposables(widget, index, attachment, hoverDelegate); - } - - if (attachment.kind === 'symbol') { - const scopedContextKeyService = store.add(this.contextKeyService.createScoped(widget)); - store.add(this.instantiationService.invokeFunction(accessor => hookUpSymbolAttachmentDragAndContextMenu(accessor, widget, scopedContextKeyService, { ...attachment, kind: attachment.symbolKind }, MenuId.ChatInputSymbolAttachmentContext))); - } - - await Promise.all(attachmentInitPromises); - if (store.isDisposed) { - return; - } - - if (resource) { - widget.style.cursor = 'pointer'; - store.add(dom.addDisposableListener(widget, dom.EventType.CLICK, (e: MouseEvent) => { - dom.EventHelper.stop(e, true); - if (attachment.isDirectory) { - this.openResource(resource, true); - } else { - this.openResource(resource, false, range); - } - })); - - store.add(dom.addDisposableListener(widget, dom.EventType.KEY_DOWN, (e: KeyboardEvent) => { - const event = new StandardKeyboardEvent(e); - if (event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) { - dom.EventHelper.stop(e, true); - if (attachment.isDirectory) { - this.openResource(resource, true); - } else { - this.openResource(resource, false, range); - } - } - })); + attachmentWidget = this.instantiationService.createInstance(DefaultChatAttachmentWidget, resource, range, attachment, this._currentLanguageModel, shouldFocusClearButton, container, this._contextResourceLabels, hoverDelegate); } - - widget.tabIndex = 0; - widget.ariaLabel = ariaLabel; + store.add(attachmentWidget); + store.add(attachmentWidget.onDidDelete((e) => { + this.handleAttachmentDeletion(e, index, attachment); + })); } if (oldHeight !== container.offsetHeight) { @@ -1104,110 +1096,27 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge } } - private customAttachment(widget: HTMLElement, friendlyName: string, hoverDelegate: IHoverDelegate, ariaLabel: string, store: DisposableStore, isImage?: boolean, supportsVision?: boolean): HTMLElement { - const pillIcon = dom.$('div.chat-attached-context-pill', {}, dom.$(supportsVision ? 'span.codicon.codicon-file-media' : 'span.codicon.codicon-warning')); - const textLabel = dom.$('span.chat-attached-context-custom-text', {}, friendlyName); - widget.appendChild(pillIcon); - widget.appendChild(textLabel); - - const hoverElement = dom.$('div.chat-attached-context-hover'); - hoverElement.setAttribute('aria-label', ariaLabel); + private handleAttachmentDeletion(e: globalThis.Event, index: number, attachment: IChatRequestVariableEntry) { + this._attachmentModel.delete(attachment.id); - if (!supportsVision) { - widget.classList.add('warning'); - hoverElement.textContent = localize('chat.fileAttachmentHover', "{0} does not support this {1} type.", this.currentLanguageModel ? this.languageModelsService.lookupLanguageModel(this.currentLanguageModel)?.name : this.currentLanguageModel, isImage ? 'image' : 'file'); - store.add(this.hoverService.setupManagedHover(hoverDelegate, widget, hoverElement, { trapFocus: true })); - } - - return hoverElement; - } - - private openResource(resource: URI, isDirectory: true): void; - private openResource(resource: URI, isDirectory: false, range: IRange | undefined): void; - private openResource(resource: URI, isDirectory?: boolean, range?: IRange): void { - if (isDirectory) { - // Reveal Directory in explorer - this.commandService.executeCommand(revealInSideBarCommand.id, resource); - return; - } - - // Open file in editor - const openTextEditorOptions: ITextEditorOptions | undefined = range ? { selection: range } : undefined; - const options: OpenInternalOptions = { - fromUserGesture: true, - editorOptions: openTextEditorOptions, - }; - this.openerService.open(resource, options); - } - - private attachButtonAndDisposables(widget: HTMLElement, index: number, attachment: IChatRequestVariableEntry, hoverDelegate: IHoverDelegate) { - const store = this.attachedContextDisposables.value; - if (!store) { - return; - } - - const clearButton = new Button(widget, { - supportIcons: true, - hoverDelegate, - title: localize('chat.attachment.clearButton', "Remove from context") - }); - - // If this item is rendering in place of the last attached context item, focus the clear button so the user can continue deleting attached context items with the keyboard - if (index === Math.min(this._indexOfLastAttachedContextDeletedWithKeyboard, this.attachmentModel.size - 1)) { - clearButton.focus(); - } - - store.add(clearButton); - clearButton.icon = Codicon.close; - store.add(Event.once(clearButton.onDidClick)((e) => { - this._attachmentModel.delete(attachment.id); - - // Set focus to the next attached context item if deletion was triggered by a keystroke (vs a mouse click) - if (dom.isKeyboardEvent(e)) { - const event = new StandardKeyboardEvent(e); - if (event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) { - this._indexOfLastAttachedContextDeletedWithKeyboard = index; - } - } - - if (this._attachmentModel.size === 0) { - this.focus(); + // Set focus to the next attached context item if deletion was triggered by a keystroke (vs a mouse click) + if (dom.isKeyboardEvent(e)) { + const event = new StandardKeyboardEvent(e); + if (event.equals(KeyCode.Enter) || event.equals(KeyCode.Space)) { + this._indexOfLastAttachedContextDeletedWithKeyboard = index; } - - this._onDidChangeContext.fire({ removed: [attachment] }); - })); - } - - // Helper function to create and replace image - private createImageElements(buffer: ArrayBuffer | Uint8Array, widget: HTMLElement, hoverElement: HTMLElement) { - const blob = new Blob([buffer], { type: 'image/png' }); - const url = URL.createObjectURL(blob); - const pillImg = dom.$('img.chat-attached-context-pill-image', { src: url, alt: '' }); - const pill = dom.$('div.chat-attached-context-pill', {}, pillImg); - - const existingPill = widget.querySelector('.chat-attached-context-pill'); - if (existingPill) { - existingPill.replaceWith(pill); } - const hoverImage = dom.$('img.chat-attached-context-image', { src: url, alt: '' }); - - // Update hover image - hoverElement.appendChild(hoverImage); + if (this._attachmentModel.size === 0) { + this.focus(); + } - hoverImage.onload = () => { - URL.revokeObjectURL(url); - }; + this._onDidChangeContext.fire({ removed: [attachment] }); } - async renderChatEditingSessionState(chatEditingSession: IChatEditingSession | null, chatWidget?: IChatWidget) { + async renderChatEditingSessionState(chatEditingSession: IChatEditingSession | null) { dom.setVisibility(Boolean(chatEditingSession), this.chatEditingSessionWidgetContainer); - await this.renderAttachedContext(chatWidget); - if (chatEditingSession) { - this.renderChatRelatedFiles(chatEditingSession, this.relatedFilesContainer); - } - const seenEntries = new ResourceSet(); const entries: IChatCollapsibleListItem[] = chatEditingSession?.entries.get().map((entry) => { seenEntries.add(entry.modifiedURI); @@ -1227,16 +1136,14 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge // Summary of number of files changed const innerContainer = this.chatEditingSessionWidgetContainer.querySelector('.chat-editing-session-container.show-file-icons') as HTMLElement ?? dom.append(this.chatEditingSessionWidgetContainer, $('.chat-editing-session-container.show-file-icons')); - for (const [file, metadata] of chatEditingSession.workingSet.entries()) { - if (!seenEntries.has(file) && metadata.state !== WorkingSetEntryState.Suggested) { + for (const entry of chatEditingSession.entries.get()) { + if (!seenEntries.has(entry.modifiedURI)) { entries.unshift({ - reference: file, - state: metadata.state, - description: metadata.description, + reference: entry.modifiedURI, + state: entry.state.get(), kind: 'reference', - isMarkedReadonly: metadata.isMarkedReadonly, }); - seenEntries.add(file); + seenEntries.add(entry.modifiedURI); } } @@ -1254,19 +1161,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge const overviewTitle = overviewRegion.querySelector('.working-set-title') as HTMLElement ?? dom.append(overviewRegion, $('.working-set-title')); const overviewFileCount = overviewTitle.querySelector('span.working-set-count') ?? dom.append(overviewTitle, $('span.working-set-count')); - let suggestedFilesInWorkingSetCount = 0; - overviewFileCount.textContent = ''; - if (entries.length === 1) { - overviewFileCount.textContent = localize('chatEditingSession.oneFile.1', '1 file changed'); - suggestedFilesInWorkingSetCount = entries[0].kind === 'reference' && entries[0].state === WorkingSetEntryState.Suggested ? 1 : 0; - } else { - suggestedFilesInWorkingSetCount = entries.filter(e => e.kind === 'reference' && e.state === WorkingSetEntryState.Suggested).length; - } - - if (entries.length > 1) { - const fileCount = entries.length - suggestedFilesInWorkingSetCount; - overviewFileCount.textContent = (fileCount === 1 ? localize('chatEditingSession.oneFile.1', '1 file changed') : localize('chatEditingSession.manyFiles.1', '{0} files changed', fileCount)); - } + overviewFileCount.textContent = entries.length === 1 ? localize('chatEditingSession.oneFile.1', '1 file changed') : localize('chatEditingSession.manyFiles.1', '{0} files changed', entries.length); overviewTitle.ariaLabel = overviewFileCount.textContent; overviewTitle.tabIndex = 0; @@ -1338,26 +1233,30 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge this._onDidChangeHeight.fire(); } - async renderChatRelatedFiles(chatEditingSession: IChatEditingSession, anchor: HTMLElement) { + async renderChatRelatedFiles() { + const anchor = this.relatedFilesContainer; dom.clearNode(anchor); + const shouldRender = this.configurationService.getValue('chat.renderRelatedFiles'); + dom.setVisibility(Boolean(this.relatedFiles?.value.length && shouldRender), anchor); + if (!shouldRender || !this.relatedFiles?.value.length) { + return; + } + const hoverDelegate = getDefaultHoverDelegate('element'); - for (const [uri, metadata] of chatEditingSession.workingSet) { - if (metadata.state !== WorkingSetEntryState.Suggested) { - continue; - } + for (const { uri, description } of this.relatedFiles.value) { const uriLabel = this._chatEditsActionsDisposables.add(new Button(anchor, { supportIcons: true, secondary: true, hoverDelegate })); uriLabel.label = this.labelService.getUriBasenameLabel(uri); - uriLabel.element.classList.add('monaco-icon-label', ...getIconClasses(this.modelService, this.languageService, uri, FileKind.FILE)); - uriLabel.element.title = localize('suggeste.title', "{0} - {1}", this.labelService.getUriLabel(uri, { relative: true }), metadata.description ?? ''); + uriLabel.element.classList.add('monaco-icon-label'); + uriLabel.element.title = localize('suggeste.title', "{0} - {1}", this.labelService.getUriLabel(uri, { relative: true }), description ?? ''); this._chatEditsActionsDisposables.add(uriLabel.onDidClick(() => { group.remove(); // REMOVE asap this._attachmentModel.addFile(uri); - chatEditingSession.remove(WorkingSetEntryRemovalReason.User, uri); + this.relatedFiles?.remove(uri); })); const addButton = this._chatEditsActionsDisposables.add(new Button(anchor, { @@ -1371,17 +1270,17 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge this._chatEditsActionsDisposables.add(addButton.onDidClick(() => { group.remove(); // REMOVE asap this._attachmentModel.addFile(uri); - chatEditingSession.remove(WorkingSetEntryRemovalReason.User, uri); + this.relatedFiles?.remove(uri); })); const sep = document.createElement('div'); sep.classList.add('separator'); const group = document.createElement('span'); - group.classList.add('monaco-button-dropdown', 'sidebyside-button', 'show-file-icons'); - group.appendChild(uriLabel.element); - group.appendChild(sep); + group.classList.add('monaco-button-dropdown', 'sidebyside-button'); group.appendChild(addButton.element); + group.appendChild(sep); + group.appendChild(uriLabel.element); dom.append(anchor, group); this._chatEditsActionsDisposables.add(toDisposable(() => { @@ -1454,7 +1353,7 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge inputPartEditorHeight: Math.min(this._inputEditor.getContentHeight(), this.inputEditorMaxHeight), inputPartHorizontalPadding: this.options.renderStyle === 'compact' ? 16 : 32, inputPartVerticalPadding: this.options.renderStyle === 'compact' ? 12 : 28, - attachmentsHeight: this.attachedContextContainer.offsetHeight + this.relatedFilesContainer.offsetHeight, + attachmentsHeight: this.attachmentsContainer.offsetHeight + (this.attachmentsContainer.checkVisibility() ? 6 : 0), editorBorder: 2, inputPartHorizontalPaddingInside: 12, toolbarsWidth: this.options.renderStyle === 'compact' ? executeToolbarWidth + executeToolbarPadding + inputToolbarWidth + inputToolbarPadding : 0, @@ -1550,6 +1449,7 @@ class ModelPickerActionViewItem extends DropdownMenuActionViewItemWithKeybinding @IContextMenuService contextMenuService: IContextMenuService, @IKeybindingService keybindingService: IKeybindingService, @IContextKeyService contextKeyService: IContextKeyService, + @IChatEntitlementService chatEntitlementService: IChatEntitlementService, @ICommandService commandService: ICommandService, ) { const modelActionsProvider: IActionProvider = { @@ -1572,16 +1472,21 @@ class ModelPickerActionViewItem extends DropdownMenuActionViewItemWithKeybinding const models: ILanguageModelChatMetadataAndIdentifier[] = this.delegate.getModels(); const actions = models.map(entry => setLanguageModelAction(entry)); - if (contextKeyService.getContextKeyValue(ChatContextKeys.Setup.limited.key) === true) { + if (chatEntitlementService.entitlement === ChatEntitlement.Limited) { actions.push(new Separator()); - actions.push(toAction({ id: 'moreModels', label: localize('chat.moreModels', "Add More Models..."), run: () => commandService.executeCommand('workbench.action.chat.upgradePlan') })); + actions.push(toAction({ id: 'moreModels', label: localize('chat.moreModels', "Add More Models..."), run: () => commandService.executeCommand('workbench.action.chat.upgradePlan', 'chat-models') })); } return actions; } }; - super(action, modelActionsProvider, contextMenuService, undefined, keybindingService, contextKeyService); + const actionWithLabel: IAction = { + ...action, + tooltip: localize('chat.modelPicker.label', "Pick Model"), + run: () => { } + }; + super(actionWithLabel, modelActionsProvider, contextMenuService, undefined, keybindingService, contextKeyService); this._register(delegate.onDidChangeModel(modelId => { this.currentLanguageModel = modelId; this.renderLabel(this.element!); @@ -1596,57 +1501,92 @@ class ModelPickerActionViewItem extends DropdownMenuActionViewItemWithKeybinding override render(container: HTMLElement): void { super.render(container); - container.classList.add('chat-modelPicker-item'); + container.classList.add('chat-modelPicker-item', 'chat-dropdown-item'); } } const chatInputEditorContainerSelector = '.interactive-input-editor'; setupSimpleEditorSelectionStyling(chatInputEditorContainerSelector); -class ToggleAgentActionViewItem extends DropdownMenuActionViewItemWithKeybinding { - private readonly agentStateActions: IAction[]; +interface IModePickerDelegate { + onDidChangeMode: Event; + getMode(): ChatMode; +} +class ToggleChatModeActionViewItem extends DropdownMenuActionViewItemWithKeybinding { constructor( action: MenuItemAction, + private readonly delegate: IModePickerDelegate, @IContextMenuService contextMenuService: IContextMenuService, @IKeybindingService keybindingService: IKeybindingService, @IContextKeyService contextKeyService: IContextKeyService, + @IChatService chatService: IChatService, ) { - const agentStateActions = [ - { - ...action, - id: 'agentMode', - label: localize('chat.agentMode', "Agent"), - class: undefined, - enabled: true, - run: () => action.run({ agentMode: true } satisfies IToggleAgentModeArgs) - }, - { - ...action, - id: 'normalMode', - label: localize('chat.normalMode', "Edit"), - class: undefined, - enabled: true, - checked: !action.checked, - run: () => action.run({ agentMode: false } satisfies IToggleAgentModeArgs) - }, - ]; + const makeAction = (mode: ChatMode): IAction => ({ + ...action, + id: mode, + label: this.modeToString(mode), + class: undefined, + enabled: true, + checked: delegate.getMode() === mode, + run: async () => { + const result = await action.run({ mode } satisfies IToggleChatModeArgs); + this.renderLabel(this.element!); + return result; + } + }); + + const actionProvider: IActionProvider = { + getActions: () => { + const agentStateActions = [ + makeAction(ChatMode.Agent), + makeAction(ChatMode.Edit), + ]; + if (chatService.unifiedViewEnabled) { + agentStateActions.unshift(makeAction(ChatMode.Chat)); + } + + return agentStateActions; + } + }; - super(action, agentStateActions, contextMenuService, undefined, keybindingService, contextKeyService); - this.agentStateActions = agentStateActions; + super(action, actionProvider, contextMenuService, undefined, keybindingService, contextKeyService); + this._register(delegate.onDidChangeMode(() => this.renderLabel(this.element!))); + } + + private modeToString(mode: ChatMode) { + switch (mode) { + case ChatMode.Agent: + return localize('chat.agentMode', "Agent"); + case ChatMode.Edit: + return localize('chat.normalMode', "Edit"); + case ChatMode.Chat: + return localize('chat.chatMode', "Chat"); + } } protected override renderLabel(element: HTMLElement): IDisposable | null { // Can't call super.renderLabel because it has a hack of forcing the 'codicon' class this.setAriaLabelAttributes(element); - const state = this.agentStateActions.find(action => action.checked)?.label ?? ''; + const state = this.modeToString(this.delegate.getMode()); dom.reset(element, dom.$('span.chat-model-label', undefined, state), ...renderLabelWithIcons(`$(chevron-down)`)); return null; } override render(container: HTMLElement): void { super.render(container); - container.classList.add('chat-modelPicker-item'); + container.classList.add('chat-dropdown-item'); + } +} + +class AddFilesButton extends ActionViewItem { + constructor(context: unknown, action: IAction, options: IActionViewItemOptions) { + super(context, action, options); + } + + override render(container: HTMLElement): void { + super.render(container); + container.classList.add('chat-attached-context-attachment', 'chat-add-files'); } } diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index 8f5d0743d042..1e04eadc400d 100644 --- a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts @@ -17,8 +17,9 @@ import { Codicon } from '../../../../base/common/codicons.js'; import { Emitter, Event } from '../../../../base/common/event.js'; import { FuzzyScore } from '../../../../base/common/filters.js'; import { MarkdownString } from '../../../../base/common/htmlContent.js'; +import { Iterable } from '../../../../base/common/iterator.js'; import { KeyCode } from '../../../../base/common/keyCodes.js'; -import { Disposable, DisposableStore, IDisposable, dispose, toDisposable } from '../../../../base/common/lifecycle.js'; +import { Disposable, DisposableStore, IDisposable, dispose, thenIfNotDisposed, toDisposable } from '../../../../base/common/lifecycle.js'; import { ResourceMap } from '../../../../base/common/map.js'; import { FileAccess } from '../../../../base/common/network.js'; import { clamp } from '../../../../base/common/numbers.js'; @@ -42,14 +43,16 @@ import { ColorScheme } from '../../../../platform/theme/common/theme.js'; import { IThemeService } from '../../../../platform/theme/common/themeService.js'; import { IWorkbenchIssueService } from '../../issue/common/issue.js'; import { annotateSpecialMarkdownContent } from '../common/annotations.js'; -import { ChatAgentLocation, IChatAgentMetadata } from '../common/chatAgents.js'; +import { checkModeOption } from '../common/chat.js'; +import { IChatAgentMetadata } from '../common/chatAgents.js'; import { ChatContextKeys } from '../common/chatContextKeys.js'; import { IChatRequestVariableEntry, IChatTextEditGroup } from '../common/chatModel.js'; import { chatSubcommandLeader } from '../common/chatParserTypes.js'; -import { ChatAgentVoteDirection, ChatAgentVoteDownReason, IChatConfirmation, IChatContentReference, IChatFollowup, IChatMarkdownContent, IChatTask, IChatToolInvocation, IChatToolInvocationSerialized, IChatTreeData } from '../common/chatService.js'; +import { ChatAgentVoteDirection, ChatAgentVoteDownReason, ChatErrorLevel, IChatConfirmation, IChatContentReference, IChatFollowup, IChatMarkdownContent, IChatService, IChatTask, IChatToolInvocation, IChatToolInvocationSerialized, IChatTreeData, IChatUndoStop } from '../common/chatService.js'; import { IChatCodeCitations, IChatReferences, IChatRendererContent, IChatRequestViewModel, IChatResponseViewModel, IChatWorkingProgress, isRequestVM, isResponseVM } from '../common/chatViewModel.js'; import { getNWords } from '../common/chatWordCounter.js'; import { CodeBlockModelCollection } from '../common/codeBlockModelCollection.js'; +import { ChatMode } from '../common/constants.js'; import { MarkUnhelpfulActionId } from './actions/chatTitleActions.js'; import { ChatTreeItem, IChatCodeBlockInfo, IChatFileTreeInfo, IChatListItemRendererOptions, IChatWidgetService } from './chat.js'; import { ChatAgentHover, getChatAgentHoverOptions } from './chatAgentHover.js'; @@ -59,7 +62,7 @@ import { ChatCodeCitationContentPart } from './chatContentParts/chatCodeCitation import { ChatCommandButtonContentPart } from './chatContentParts/chatCommandContentPart.js'; import { ChatConfirmationContentPart } from './chatContentParts/chatConfirmationContentPart.js'; import { IChatContentPart, IChatContentPartRenderContext } from './chatContentParts/chatContentParts.js'; -import { ChatMarkdownContentPart, EditorPool } from './chatContentParts/chatMarkdownContentPart.js'; +import { ChatMarkdownContentPart, EditorPool, IChatMarkdownContentPartOptions } from './chatContentParts/chatMarkdownContentPart.js'; import { ChatProgressContentPart, ChatWorkingProgressContentPart } from './chatContentParts/chatProgressContentPart.js'; import { ChatQuotaExceededPart } from './chatContentParts/chatQuotaExceededPart.js'; import { ChatCollapsibleListContentPart, ChatUsedReferencesListContentPart, CollapsibleListPool } from './chatContentParts/chatReferencesContentPart.js'; @@ -104,6 +107,7 @@ const forceVerboseLayoutTracing = false export interface IChatRendererDelegate { container: HTMLElement; getListLength(): number; + currentChatMode(): ChatMode; readonly onDidScroll?: Event; } @@ -119,8 +123,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer(); private readonly focusedFileTreesByResponseId = new Map(); - private readonly renderer: MarkdownRenderer; - private readonly markdownDecorationsRenderer: ChatMarkdownDecorationsRenderer; + private readonly renderer: ChatMarkdownRenderer; protected readonly _onDidClickFollowup = this._register(new Emitter()); readonly onDidClickFollowup: Event = this._onDidClickFollowup.event; @@ -131,8 +134,6 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer()); readonly onDidChangeItemHeight: Event = this._onDidChangeItemHeight.event; - private readonly _editorPool: EditorPool; - private readonly _diffEditorPool: DiffEditorPool; private readonly _treePool: TreePool; private readonly _contentReferencesListPool: CollapsibleListPool; @@ -140,6 +141,12 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer()); + /** + * Tool invocations get their own so that the ChatViewModel doesn't overwrite it. + * TODO@roblourens shouldn't use the CodeBlockModelCollection at all + */ + private readonly _toolInvocationCodeBlockCollection: CodeBlockModelCollection; + constructor( editorOptions: ChatEditorOptions, private readonly rendererOptions: IChatListItemRendererOptions, @@ -154,17 +161,15 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer { - return this._editorPool.inUse(); + return Iterable.concat(this._editorPool.inUse(), this._toolEditorPool.inUse()); } private traceLayout(method: string, message: string) { @@ -233,10 +238,13 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer this._onDidClickRerunWithAgentOrCommandDetection.fire({ sessionId: element.sessionId, requestId: element.id })); templateData.value.appendChild(cmdPart.domNode); parts.push(cmdPart); inlineSlashCommandRendered = true; } - templateData.value.appendChild(newPart.domNode); + if (newPart.domNode) { + templateData.value.appendChild(newPart.domNode); + } parts.push(newPart); } }); @@ -596,7 +606,8 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer this.updateItemHeight(templateData))); } else { - const renderedError = this.instantiationService.createInstance(ChatWarningContentPart, element.errorDetails.responseIsFiltered ? 'info' : 'error', new MarkdownString(element.errorDetails.message), this.renderer); + const level = element.errorDetails.level ?? (element.errorDetails.responseIsFiltered ? ChatErrorLevel.Info : ChatErrorLevel.Error); + const renderedError = this.instantiationService.createInstance(ChatWarningContentPart, level, new MarkdownString(element.errorDetails.message), this.renderer); templateData.elementDisposables.add(renderedError); templateData.value.appendChild(renderedError.domNode); } @@ -708,77 +719,59 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer(['undoStop']); - const partsToRender: IChatRendererContent[] = []; - if (element.contentReferences.length) { - partsToRender.push({ kind: 'references', references: element.contentReferences }); - } - - let moreContentAvailable = false; - for (let i = 0; i < renderableResponse.length; i++) { - const part = renderableResponse[i]; - if (part.kind === 'markdownContent') { - const wordCountResult = getNWords(part.content.value, numNeededWords); - this.traceLayout('getNextProgressiveRenderContent', ` Chunk ${i}: Want to render ${numNeededWords} words and found ${wordCountResult.returnedWordCount} words. Total words in chunk: ${wordCountResult.totalWordCount}`); - numNeededWords -= wordCountResult.returnedWordCount; - - if (wordCountResult.isFullString) { - partsToRender.push(part); - - // Consumed full markdown chunk- need to ensure that all following non-markdown parts are rendered - for (const nextPart of renderableResponse.slice(i + 1)) { - if (nextPart.kind !== 'markdownContent' && !typesToSkip.has(nextPart.kind)) { - i++; - partsToRender.push(nextPart); - } else { - break; - } - } - } else { - // Only taking part of this markdown part - moreContentAvailable = true; - partsToRender.push({ ...part, content: new MarkdownString(wordCountResult.value, part.content) }); - } + return { element: result, dispose: () => content.dispose() }; + } - if (numNeededWords <= 0) { - // Collected all words and following non-markdown parts if needed, done - if (renderableResponse.slice(i + 1).some(part => part.kind === 'markdownContent')) { - moreContentAvailable = true; - } - break; - } - } else if (!typesToSkip.has(part.kind)) { - partsToRender.push(part); + private renderMarkdown(markdown: IMarkdownString, element: ChatTreeItem, templateData: IChatListItemTemplate, fillInIncompleteTokens = false): IMarkdownRenderResult { + const disposables = new DisposableStore(); + + // TODO if the slash commands stay completely dynamic, this isn't quite right + const slashCommands = this.delegate.getSlashCommands(); + const usedSlashCommand = slashCommands.find(s => markdown.value.startsWith(`/${s.command} `)); + const toRender = usedSlashCommand ? markdown.value.slice(usedSlashCommand.command.length + 2) : markdown.value; + markdown = new MarkdownString(toRender, { + isTrusted: { + // Disable all other config options except isTrusted + enabledCommands: typeof markdown.isTrusted === 'object' ? markdown.isTrusted?.enabledCommands : [] ?? [] } + }); + + const result = this.renderer.render(markdown, element, templateData.contextKeyService, fillInIncompleteTokens); + + if (isResponseVM(element)) { + this.codeBlocksByResponseId.set(element.id, result.codeblocks); + disposables.add(toDisposable(() => this.codeBlocksByResponseId.delete(element.id))); } const lastWordCount = element.contentUpdateTimings?.lastWordCount ?? 0; @@ -799,10 +792,14 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer part.kind === 'toolInvocation' && !part.isComplete))) { @@ -812,9 +809,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer): IWordCountResult | undefined { const rate = this.getProgressiveRenderRate(element); const numWordsToRender = renderData.lastRenderTime === 0 ? 1 : @@ -859,7 +854,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer content.kind === other.kind); + } + + private renderUndoStop(content: IChatUndoStop) { + return this.renderNoContent(other => other.kind === content.kind && other.id === content.id); + } + + private renderNoContent(equals: (otherContent: IChatRendererContent) => boolean): IChatContentPart { + return { + dispose: () => { }, + domNode: undefined, + hasSameContent: equals, + }; } private renderTreeData(content: IChatTreeData, templateData: IChatListItemTemplate, context: IChatContentPartRenderContext): IChatContentPart { @@ -908,7 +917,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer { this.updateItemHeight(templateData); })); @@ -921,29 +930,48 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer { - this.updateItemHeight(templateData); - })); - return part; + private getCodeBlockStartIndex(context: IChatContentPartRenderContext): number { + return context.preceedingContentParts.reduce((acc, part) => acc + (part.codeblocks?.length ?? 0), 0); } - private renderProgressTask(task: IChatTask, templateData: IChatListItemTemplate, context: IChatContentPartRenderContext): IChatContentPart | undefined { - if (!isResponseVM(context.element)) { + private handleRenderedCodeblocks(element: ChatTreeItem, part: IChatContentPart, codeBlockStartIndex: number): void { + if (!part.addDisposable || part.codeblocksPartId === undefined) { return; } - - const taskPart = this.instantiationService.createInstance(ChatTaskContentPart, task, this._contentReferencesListPool, this.renderer, context); - taskPart.addDisposable(taskPart.onDidChangeHeight(() => { - this.updateItemHeight(templateData); - })); - return taskPart; + const fileTreeCount = element.response.value.filter((v) => !('value' in v))?.length ?? 0; + let fileTreeCountHint = ''; + switch (fileTreeCount) { + case 0: + break; + case 1: + fileTreeCountHint = localize('singleFileTreeHint', "1 file tree"); + break; + default: + fileTreeCountHint = localize('multiFileTreeHint', "{0} file trees", fileTreeCount); + break; + } + const codeBlockCount = marked.lexer(element.response.asString()).filter(token => token.type === 'code')?.length ?? 0; + switch (codeBlockCount) { + case 0: + label = accessibleViewHint ? localize('noCodeBlocksHint', "{0} {1} {2}", fileTreeCountHint, element.response.asString(), accessibleViewHint) : localize('noCodeBlocks', "{0} {1}", fileTreeCountHint, element.response.asString()); + break; + case 1: + label = accessibleViewHint ? localize('singleCodeBlockHint', "{0} 1 code block: {1} {2}", fileTreeCountHint, element.response.asString(), accessibleViewHint) : localize('singleCodeBlock', "{0} 1 code block: {1}", fileTreeCountHint, element.response.asString()); + break; + default: + label = accessibleViewHint ? localize('multiCodeBlockHint', "{0} {1} code blocks: {2}", fileTreeCountHint, codeBlockCount, element.response.asString(), accessibleViewHint) : localize('multiCodeBlock', "{0} {1} code blocks", fileTreeCountHint, codeBlockCount, element.response.asString()); + break; + } + return commandFollowUpInfo ? commandFollowUpInfo + ', ' + label : label; } +} - private renderWorkingProgress(workingProgress: IChatWorkingProgress, context: IChatContentPartRenderContext): IChatContentPart | undefined { - return this.instantiationService.createInstance(ChatWorkingProgressContentPart, workingProgress, this.renderer, context); - } +interface IDisposableReference extends IDisposable { + object: T; + isStale: () => boolean; +} +class TreePool extends Disposable { + private _pool: ResourcePool>; private renderConfirmation(context: IChatContentPartRenderContext, confirmation: IChatConfirmation, templateData: IChatListItemTemplate): IChatContentPart { const part = this.instantiationService.createInstance(ChatConfirmationContentPart, confirmation, context); @@ -968,45 +996,17 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer acc + (part instanceof ChatMarkdownContentPart ? part.codeblocks.length : 0), 0); - const markdownPart = templateData.instantiationService.createInstance(ChatMarkdownContentPart, markdown, context, this._editorPool, fillInIncompleteTokens, codeBlockStartIndex, this.renderer, this._currentLayoutWidth, this.codeBlockModelCollection, this.rendererOptions); - const markdownPartId = markdownPart.id; + const codeBlockStartIndex = this.getCodeBlockStartIndex(context); + const options: IChatMarkdownContentPartOptions = { + renderCodeBlockPills: checkModeOption(this.delegate.currentChatMode(), this.rendererOptions.renderCodeBlockPills), + }; + const markdownPart = templateData.instantiationService.createInstance(ChatMarkdownContentPart, markdown, context, this._editorPool, fillInIncompleteTokens, codeBlockStartIndex, this.renderer, this._currentLayoutWidth, this.codeBlockModelCollection, options); markdownPart.addDisposable(markdownPart.onDidChangeHeight(() => { markdownPart.layout(this._currentLayoutWidth); this.updateItemHeight(templateData); })); - const codeBlocksByResponseId = this.codeBlocksByResponseId.get(element.id) ?? []; - this.codeBlocksByResponseId.set(element.id, codeBlocksByResponseId); - markdownPart.addDisposable(toDisposable(() => { - const codeBlocksByResponseId = this.codeBlocksByResponseId.get(element.id); - if (codeBlocksByResponseId) { - // Only delete if this is my code block - markdownPart.codeblocks.forEach((info, i) => { - const codeblock = codeBlocksByResponseId[codeBlockStartIndex + i]; - if (codeblock?.ownerMarkdownPartId === markdownPartId) { - delete codeBlocksByResponseId[codeBlockStartIndex + i]; - } - }); - } - })); - - markdownPart.codeblocks.forEach((info, i) => { - codeBlocksByResponseId[codeBlockStartIndex + i] = info; - info.uriPromise.then(uri => { - if (!uri) { - return; - } - - this.codeBlocksByEditorUri.set(uri, info); - markdownPart.addDisposable(toDisposable(() => { - const codeblock = this.codeBlocksByEditorUri.get(uri); - if (codeblock?.ownerMarkdownPartId === markdownPartId) { - this.codeBlocksByEditorUri.delete(uri); - } - })); - }); - }); + this.handleRenderedCodeblocks(element, markdownPart, codeBlockStartIndex); return markdownPart; } @@ -1014,6 +1014,12 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer, index: number, templateData: IChatListItemTemplate): void { this.traceLayout('disposeElement', `Disposing element, index=${index}`); templateData.elementDisposables.clear(); + + // Don't retain the toolbar context which includes chat viewmodels + if (templateData.titleToolbar) { + templateData.titleToolbar.context = undefined; + } + templateData.footerToolbar.context = undefined; } disposeTemplate(templateData: IChatListItemTemplate): void { @@ -1051,65 +1057,8 @@ export class ChatListDelegate implements IListVirtualDelegate { } } -const voteDownDetailLabels: Record = { - [ChatAgentVoteDownReason.IncorrectCode]: localize('incorrectCode', "Suggested incorrect code"), - [ChatAgentVoteDownReason.DidNotFollowInstructions]: localize('didNotFollowInstructions', "Didn't follow instructions"), - [ChatAgentVoteDownReason.MissingContext]: localize('missingContext', "Missing context"), - [ChatAgentVoteDownReason.OffensiveOrUnsafe]: localize('offensiveOrUnsafe', "Offensive or unsafe"), - [ChatAgentVoteDownReason.PoorlyWrittenOrFormatted]: localize('poorlyWrittenOrFormatted', "Poorly written or formatted"), - [ChatAgentVoteDownReason.RefusedAValidRequest]: localize('refusedAValidRequest', "Refused a valid request"), - [ChatAgentVoteDownReason.IncompleteCode]: localize('incompleteCode', "Incomplete code"), - [ChatAgentVoteDownReason.WillReportIssue]: localize('reportIssue', "Report an issue"), - [ChatAgentVoteDownReason.Other]: localize('other', "Other"), -}; - -export class ChatVoteDownButton extends DropdownMenuActionViewItem { - constructor( - action: IAction, - options: IDropdownMenuActionViewItemOptions | undefined, - @ICommandService private readonly commandService: ICommandService, - @IWorkbenchIssueService private readonly issueService: IWorkbenchIssueService, - @ILogService private readonly logService: ILogService, - @IContextMenuService contextMenuService: IContextMenuService, - ) { - super(action, - { getActions: () => this.getActions(), }, - contextMenuService, - { - ...options, - classNames: ThemeIcon.asClassNameArray(Codicon.thumbsdown), - }); - } - - getActions(): readonly IAction[] { - return [ - this.getVoteDownDetailAction(ChatAgentVoteDownReason.IncorrectCode), - this.getVoteDownDetailAction(ChatAgentVoteDownReason.DidNotFollowInstructions), - this.getVoteDownDetailAction(ChatAgentVoteDownReason.IncompleteCode), - this.getVoteDownDetailAction(ChatAgentVoteDownReason.MissingContext), - this.getVoteDownDetailAction(ChatAgentVoteDownReason.PoorlyWrittenOrFormatted), - this.getVoteDownDetailAction(ChatAgentVoteDownReason.RefusedAValidRequest), - this.getVoteDownDetailAction(ChatAgentVoteDownReason.OffensiveOrUnsafe), - this.getVoteDownDetailAction(ChatAgentVoteDownReason.Other), - { - id: 'reportIssue', - label: voteDownDetailLabels[ChatAgentVoteDownReason.WillReportIssue], - tooltip: '', - enabled: true, - class: undefined, - run: async (context: IChatResponseViewModel) => { - if (!isResponseVM(context)) { - this.logService.error('ChatVoteDownButton#run: invalid context'); - return; - } - - await this.commandService.executeCommand(MarkUnhelpfulActionId, context, ChatAgentVoteDownReason.WillReportIssue); - await this.issueService.openReporter({ extensionId: context.agent?.extensionId.value }); - } - } - ]; - } +class ChatVoteButton extends MenuEntryActionViewItem { override render(container: HTMLElement): void { super.render(container); diff --git a/src/vs/workbench/contrib/chat/browser/chatMarkdownDecorationsRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatMarkdownDecorationsRenderer.ts index 0c3186b3b9fd..3b763727a527 100644 --- a/src/vs/workbench/contrib/chat/browser/chatMarkdownDecorationsRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatMarkdownDecorationsRenderer.ts @@ -193,7 +193,8 @@ export class ChatMarkdownDecorationsRenderer { { location: widget.location, agentId: agent.id, - userSelectedModelId: widget.input.currentLanguageModel + userSelectedModelId: widget.input.currentLanguageModel, + mode: widget.input.currentMode }); })); } else { @@ -229,7 +230,8 @@ export class ChatMarkdownDecorationsRenderer { location: widget.location, agentId: agent.id, slashCommand: args.command, - userSelectedModelId: widget.input.currentLanguageModel + userSelectedModelId: widget.input.currentLanguageModel, + mode: widget.input.currentMode }); })); diff --git a/src/vs/workbench/contrib/chat/browser/chatMarkdownRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatMarkdownRenderer.ts index 6e78f992eeb8..298a6f512894 100644 --- a/src/vs/workbench/contrib/chat/browser/chatMarkdownRenderer.ts +++ b/src/vs/workbench/contrib/chat/browser/chatMarkdownRenderer.ts @@ -3,125 +3,410 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { MarkdownRenderOptions, MarkedOptions } from '../../../../base/browser/markdownRenderer.js'; -import { getDefaultHoverDelegate } from '../../../../base/browser/ui/hover/hoverDelegateFactory.js'; -import { IMarkdownString } from '../../../../base/common/htmlContent.js'; -import { DisposableStore } from '../../../../base/common/lifecycle.js'; -import { URI } from '../../../../base/common/uri.js'; -import { IMarkdownRendererOptions, IMarkdownRenderResult, MarkdownRenderer } from '../../../../editor/browser/widget/markdownRenderer/browser/markdownRenderer.js'; -import { ILanguageService } from '../../../../editor/common/languages/language.js'; -import { ICommandService } from '../../../../platform/commands/common/commands.js'; -import { IFileService } from '../../../../platform/files/common/files.js'; -import { IHoverService } from '../../../../platform/hover/browser/hover.js'; -import { IOpenerService } from '../../../../platform/opener/common/opener.js'; -import { REVEAL_IN_EXPLORER_COMMAND_ID } from '../../files/browser/fileConstants.js'; -import { ITrustedDomainService } from '../../url/browser/trustedDomainService.js'; - -const allowedHtmlTags = [ - 'b', - 'blockquote', - 'br', - 'code', - 'em', - 'h1', - 'h2', - 'h3', - 'h4', - 'h5', - 'h6', - 'hr', - 'i', - 'li', - 'ol', - 'p', - 'pre', - 'strong', - 'sub', - 'sup', - 'table', - 'tbody', - 'td', - 'th', - 'thead', - 'tr', - 'ul', - 'a', - 'img', - - // TODO@roblourens when we sanitize attributes in markdown source, we can ban these elements at that step. microsoft/vscode-copilot#5091 - // Not in the official list, but used for codicons and other vscode markdown extensions - 'span', - 'div', -]; - -/** - * This wraps the MarkdownRenderer and applies sanitizer options needed for Chat. - */ -export class ChatMarkdownRenderer extends MarkdownRenderer { +import * as dom from 'vs/base/browser/dom'; +import { Emitter, Event } from 'vs/base/common/event'; +import { Disposable, DisposableStore, IDisposable, toDisposable } from 'vs/base/common/lifecycle'; +import { CodeEditorWidget } from 'vs/editor/browser/widget/codeEditorWidget'; +import { ILanguageService } from 'vs/editor/common/languages/language'; +import { EndOfLinePreference, ITextModel } from 'vs/editor/common/model'; +import { IModelService } from 'vs/editor/common/services/model'; +import { IMarkdownRenderResult, MarkdownRenderer } from 'vs/editor/contrib/markdownRenderer/browser/markdownRenderer'; +import { IAccessibilityService } from 'vs/platform/accessibility/common/accessibility'; +import { MenuWorkbenchToolBar } from 'vs/platform/actions/browser/toolbar'; +import { MenuId } from 'vs/platform/actions/common/actions'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { ServiceCollection } from 'vs/platform/instantiation/common/serviceCollection'; +import { AccessibilityVerbositySettingId } from 'vs/workbench/contrib/accessibility/browser/accessibilityConfiguration'; +import { ChatTreeItem, IChatCodeBlockInfo } from 'vs/workbench/contrib/chat/browser/chat'; +import { ChatEditorOptions } from 'vs/workbench/contrib/chat/browser/chatOptions'; +import { ResourcePool } from 'vs/workbench/contrib/chat/browser/resourcePool'; +import { getSimpleEditorOptions } from 'vs/workbench/contrib/codeEditor/browser/simpleEditorOptions'; +import { localize } from 'vs/nls'; +import { EditorExtensionsRegistry } from 'vs/editor/browser/editorExtensions'; +import { IEditorOptions, EDITOR_FONT_DEFAULTS } from 'vs/editor/common/config/editorOptions'; +import { PLAINTEXT_LANGUAGE_ID } from 'vs/editor/common/languages/modesRegistry'; +import { BracketMatchingController } from 'vs/editor/contrib/bracketMatching/browser/bracketMatching'; +import { ContextMenuController } from 'vs/editor/contrib/contextmenu/browser/contextmenu'; +import { ViewportSemanticTokensContribution } from 'vs/editor/contrib/semanticTokens/browser/viewportSemanticTokens'; +import { SmartSelectController } from 'vs/editor/contrib/smartSelect/browser/smartSelect'; +import { WordHighlighterContribution } from 'vs/editor/contrib/wordHighlighter/browser/wordHighlighter'; +import { IChatCodeBlockActionContext } from 'vs/workbench/contrib/chat/browser/actions/chatCodeblockActions'; +import { isResponseVM } from 'vs/workbench/contrib/chat/common/chatViewModel'; +import { MenuPreventer } from 'vs/workbench/contrib/codeEditor/browser/menuPreventer'; +import { SelectionClipboardContributionID } from 'vs/workbench/contrib/codeEditor/browser/selectionClipboard'; +import { Range } from 'vs/editor/common/core/range'; +import { IMarkdownString } from 'vs/base/common/htmlContent'; +import { ResourceMap } from 'vs/base/common/map'; +import { URI } from 'vs/base/common/uri'; + +const $ = dom.$; + +export interface IItemHeightChangeParams { + element: ChatTreeItem; +} + +export class ChatMarkdownRenderer { + + private readonly _renderer: MarkdownRenderer; + private readonly _editorPool: EditorPool; + private readonly _store = new DisposableStore(); + private _currentLayoutWidth: number = 0; + + private readonly _codeBlocksByEditorUri = new ResourceMap(); + + protected readonly _onDidChangeItemHeight = this._store.add(new Emitter()); + readonly onDidChangeItemHeight: Event = this._onDidChangeItemHeight.event; + constructor( - options: IMarkdownRendererOptions | undefined, - @ILanguageService languageService: ILanguageService, - @IOpenerService openerService: IOpenerService, - @ITrustedDomainService private readonly trustedDomainService: ITrustedDomainService, - @IHoverService private readonly hoverService: IHoverService, - @IFileService private readonly fileService: IFileService, - @ICommandService private readonly commandService: ICommandService, + private readonly editorOptions: ChatEditorOptions, + @IInstantiationService _instaService: IInstantiationService ) { - super(options ?? {}, languageService, openerService); + this._renderer = _instaService.createInstance(MarkdownRenderer, {}); + this._editorPool = this._store.add(_instaService.createInstance(EditorPool, this.editorOptions)); + } - override render(markdown: IMarkdownString | undefined, options?: MarkdownRenderOptions, markedOptions?: MarkedOptions): IMarkdownRenderResult { - options = { - ...options, - remoteImageIsAllowed: (uri) => this.trustedDomainService.isValid(uri), - sanitizerOptions: { - replaceWithPlaintext: true, - allowedTags: allowedHtmlTags, - } - }; + getCodeBlockInfoForEditor(uri: URI): IChatCodeBlockInfo | undefined { + return this._codeBlocksByEditorUri.get(uri); + } + + layout(width: number): void { + this._currentLayoutWidth = width - 40; // TODO Padding + this._editorPool.inUse.forEach(editor => { + editor.layout(this._currentLayoutWidth); + }); + } - const mdWithBody: IMarkdownString | undefined = (markdown && markdown.supportHtml) ? - { - ...markdown, + public renderSimple(markdown: IMarkdownString): IMarkdownRenderResult { + return this._renderer.render(markdown); + } - // dompurify uses DOMParser, which strips leading comments. Wrapping it all in 'body' prevents this. - // The \n\n prevents marked.js from parsing the body contents as just text in an 'html' token, instead of actual markdown. - value: `\n\n${markdown.value}`, - } - : markdown; - const result = super.render(mdWithBody, options, markedOptions); - return this.attachCustomHover(result); - } - - private attachCustomHover(result: IMarkdownRenderResult): IMarkdownRenderResult { - const store = new DisposableStore(); - result.element.querySelectorAll('a').forEach((element) => { - if (element.title) { - const title = element.title; - element.title = ''; - store.add(this.hoverService.setupManagedHover(getDefaultHoverDelegate('element'), element, title)); + public render(markdown: IMarkdownString, element: ChatTreeItem, parentContextKeyService: IContextKeyService, fillInIncompleteTokens = false) { + let codeBlockIndex = 0; + const disposables = new DisposableStore(); + + // We release editors in order so that it's more likely that the same editor will be assigned if this element is re-rendered right away, like it often is during progressive rendering + const orderedDisposablesList: IDisposable[] = []; + + const codeblocks: IChatCodeBlockInfo[] = []; + + const result = this._renderer.render(markdown, { + fillInIncompleteTokens, + codeBlockRendererSync: (languageId, text) => { + const data = { languageId, text, codeBlockIndex: codeBlockIndex++, element, parentContextKeyService }; + const ref = this.renderCodeBlock(data, disposables); + + // Attach this after updating text/layout of the editor, so it should only be fired when the size updates later (horizontal scrollbar, wrapping) + // not during a renderElement OR a progressive render (when we will be firing this event anyway at the end of the render) + disposables.add(ref.object.onDidChangeContentHeight(() => { + ref.object.layout(this._currentLayoutWidth); + this._onDidChangeItemHeight.fire({ element }); + })); + + if (isResponseVM(element)) { + const info: IChatCodeBlockInfo = { + codeBlockIndex: data.codeBlockIndex, + element, + focus() { + ref.object.focus(); + } + }; + codeblocks.push(info); + this._codeBlocksByEditorUri.set(ref.object.textModel.uri, info); + disposables.add(toDisposable(() => this._codeBlocksByEditorUri.delete(ref.object.textModel.uri))); + } + orderedDisposablesList.push(ref); + return ref.object.element; } }); return { element: result.element, - dispose: () => { + codeblocks, + dispose() { result.dispose(); - store.dispose(); + disposables.dispose(); + } + }; + } + + private renderCodeBlock(data: IChatResultCodeBlockData, disposables: DisposableStore): IDisposableReference { + const ref = this._editorPool.get(); + const editorInfo = ref.object; + editorInfo.render(data, this._currentLayoutWidth); + + return ref; + } + + dispose(): void { + this._store.dispose(); + } +} + +export interface IDisposableReference extends IDisposable { + object: T; + isStale: () => boolean; +} + +export interface IChatResultCodeBlockData { + text: string; + languageId: string; + codeBlockIndex: number; + element: ChatTreeItem; + parentContextKeyService: IContextKeyService; +} + +export interface IChatResultCodeBlockPart { + readonly onDidChangeContentHeight: Event; + readonly element: HTMLElement; + readonly textModel: ITextModel; + layout(width: number): void; + render(data: IChatResultCodeBlockData, width: number): void; + focus(): void; + dispose(): void; +} + +const defaultCodeblockPadding = 10; + +class CodeBlockPart extends Disposable implements IChatResultCodeBlockPart { + private readonly _onDidChangeContentHeight = this._register(new Emitter()); + public readonly onDidChangeContentHeight = this._onDidChangeContentHeight.event; + + private readonly editor: CodeEditorWidget; + private readonly toolbar: MenuWorkbenchToolBar; + private readonly contextKeyService: IContextKeyService; + + public readonly textModel: ITextModel; + public readonly element: HTMLElement; + + private currentScrollWidth = 0; + + constructor( + private readonly options: ChatEditorOptions, + @IInstantiationService instantiationService: IInstantiationService, + @IContextKeyService contextKeyService: IContextKeyService, + @ILanguageService private readonly languageService: ILanguageService, + @IModelService private readonly modelService: IModelService, + @IConfigurationService private readonly configurationService: IConfigurationService, + @IAccessibilityService private readonly accessibilityService: IAccessibilityService + ) { + super(); + this.element = $('.interactive-result-editor-wrapper'); + this.contextKeyService = this._register(contextKeyService.createScoped(this.element)); + const scopedInstantiationService = instantiationService.createChild(new ServiceCollection([IContextKeyService, this.contextKeyService])); + this.toolbar = this._register(scopedInstantiationService.createInstance(MenuWorkbenchToolBar, this.element, MenuId.ChatCodeBlock, { + menuOptions: { + shouldForwardArgs: true + } + })); + + this._configureForScreenReader(); + this._register(this.accessibilityService.onDidChangeScreenReaderOptimized(() => this._configureForScreenReader())); + this._register(this.configurationService.onDidChangeConfiguration((e) => { + if (e.affectedKeys.has(AccessibilityVerbositySettingId.Chat)) { + this._configureForScreenReader(); + } + })); + const editorElement = dom.append(this.element, $('.interactive-result-editor')); + this.editor = this._register(scopedInstantiationService.createInstance(CodeEditorWidget, editorElement, { + ...getSimpleEditorOptions(this.configurationService), + readOnly: true, + lineNumbers: 'off', + selectOnLineNumbers: true, + scrollBeyondLastLine: false, + lineDecorationsWidth: 8, + dragAndDrop: false, + padding: { top: defaultCodeblockPadding, bottom: defaultCodeblockPadding }, + mouseWheelZoom: false, + scrollbar: { + alwaysConsumeMouseWheel: false + }, + ariaLabel: localize('chat.codeBlockHelp', 'Code block'), + ...this.getEditorOptionsFromConfig() + }, { + isSimpleWidget: true, + contributions: EditorExtensionsRegistry.getSomeEditorContributions([ + MenuPreventer.ID, + SelectionClipboardContributionID, + ContextMenuController.ID, + + WordHighlighterContribution.ID, + ViewportSemanticTokensContribution.ID, + BracketMatchingController.ID, + SmartSelectController.ID, + ]) + })); + + this._register(this.options.onDidChange(() => { + this.editor.updateOptions(this.getEditorOptionsFromConfig()); + })); + + this._register(this.editor.onDidScrollChange(e => { + this.currentScrollWidth = e.scrollWidth; + })); + this._register(this.editor.onDidContentSizeChange(e => { + if (e.contentHeightChanged) { + this._onDidChangeContentHeight.fire(e.contentHeight); } + })); + this._register(this.editor.onDidBlurEditorWidget(() => { + this.element.classList.remove('focused'); + WordHighlighterContribution.get(this.editor)?.stopHighlighting(); + })); + this._register(this.editor.onDidFocusEditorWidget(() => { + this.element.classList.add('focused'); + WordHighlighterContribution.get(this.editor)?.restoreViewState(true); + })); + + this.textModel = this._register(this.modelService.createModel('', null, undefined)); + this.editor.setModel(this.textModel); + } + + focus(): void { + this.editor.focus(); + } + + private updatePaddingForLayout() { + // scrollWidth = "the width of the content that needs to be scrolled" + // contentWidth = "the width of the area where content is displayed" + const horizontalScrollbarVisible = this.currentScrollWidth > this.editor.getLayoutInfo().contentWidth; + const scrollbarHeight = this.editor.getLayoutInfo().horizontalScrollbarHeight; + const bottomPadding = horizontalScrollbarVisible ? + Math.max(defaultCodeblockPadding - scrollbarHeight, 2) : + defaultCodeblockPadding; + this.editor.updateOptions({ padding: { top: defaultCodeblockPadding, bottom: bottomPadding } }); + } + + private _configureForScreenReader(): void { + const toolbarElt = this.toolbar.getElement(); + if (this.accessibilityService.isScreenReaderOptimized()) { + toolbarElt.style.display = 'block'; + toolbarElt.ariaLabel = this.configurationService.getValue(AccessibilityVerbositySettingId.Chat) ? localize('chat.codeBlock.toolbarVerbose', 'Toolbar for code block which can be reached via tab') : localize('chat.codeBlock.toolbar', 'Code block toolbar'); + } else { + toolbarElt.style.display = ''; + } + + } + + private getEditorOptionsFromConfig(): IEditorOptions { + return { + wordWrap: this.options.configuration.resultEditor.wordWrap, + fontLigatures: this.options.configuration.resultEditor.fontLigatures, + bracketPairColorization: this.options.configuration.resultEditor.bracketPairColorization, + fontFamily: this.options.configuration.resultEditor.fontFamily === 'default' ? + EDITOR_FONT_DEFAULTS.fontFamily : + this.options.configuration.resultEditor.fontFamily, + fontSize: this.options.configuration.resultEditor.fontSize, + fontWeight: this.options.configuration.resultEditor.fontWeight, + lineHeight: this.options.configuration.resultEditor.lineHeight, }; } - protected override async openMarkdownLink(link: string, markdown: IMarkdownString) { - try { - const uri = URI.parse(link); - if ((await this.fileService.stat(uri)).isDirectory) { - return this.commandService.executeCommand(REVEAL_IN_EXPLORER_COMMAND_ID, uri); + layout(width: number): void { + const realContentHeight = this.editor.getContentHeight(); + const editorBorder = 2; + this.editor.layout({ width: width - editorBorder, height: realContentHeight }); + this.updatePaddingForLayout(); + } + + render(data: IChatResultCodeBlockData, width: number): void { + this.contextKeyService.updateParent(data.parentContextKeyService); + + if (this.options.configuration.resultEditor.wordWrap === 'on') { + // Intialize the editor with the new proper width so that getContentHeight + // will be computed correctly in the next call to layout() + this.layout(width); + } + + const text = this.fixCodeText(data.text, data.languageId); + this.setText(text); + + const vscodeLanguageId = this.languageService.getLanguageIdByLanguageName(data.languageId) ?? undefined; + this.setLanguage(vscodeLanguageId); + + this.layout(width); + this.editor.updateOptions({ ariaLabel: localize('chat.codeBlockLabel', "Code block {0}", data.codeBlockIndex + 1) }); + this.toolbar.context = { + code: data.text, + codeBlockIndex: data.codeBlockIndex, + element: data.element, + languageId: vscodeLanguageId + }; + + if (isResponseVM(data.element) && data.element.errorDetails?.responseIsFiltered) { + dom.hide(this.toolbar.getElement()); + } else { + dom.show(this.toolbar.getElement()); + } + } + + private fixCodeText(text: string, languageId: string): string { + if (languageId === 'php') { + if (!text.trim().startsWith('<')) { + return ``; } - } catch { - // noop } - return super.openMarkdownLink(link, markdown); + return text; + } + + private setText(newText: string): void { + const currentText = this.textModel.getValue(EndOfLinePreference.LF); + if (newText === currentText) { + return; + } + + if (newText.startsWith(currentText)) { + const text = newText.slice(currentText.length); + const lastLine = this.textModel.getLineCount(); + const lastCol = this.textModel.getLineMaxColumn(lastLine); + this.textModel.applyEdits([{ range: new Range(lastLine, lastCol, lastLine, lastCol), text }]); + } else { + // console.log(`Failed to optimize setText`); + this.textModel.setValue(newText); + } + } + + private setLanguage(vscodeLanguageId: string | undefined): void { + this.textModel.setLanguage(vscodeLanguageId ?? PLAINTEXT_LANGUAGE_ID); + } +} + + + +class EditorPool extends Disposable { + private _pool: ResourcePool; + + public get inUse(): ReadonlySet { + return this._pool.inUse; + } + + constructor( + private readonly options: ChatEditorOptions, + @IInstantiationService private readonly instantiationService: IInstantiationService, + ) { + super(); + this._pool = this._register(new ResourcePool(() => this.editorFactory())); + + // TODO listen to changes on options + } + + private editorFactory(): IChatResultCodeBlockPart { + return this.instantiationService.createInstance(CodeBlockPart, this.options); + } + + get(): IDisposableReference { + const object = this._pool.get(); + let stale = false; + return { + object, + isStale: () => stale, + dispose: () => { + stale = true; + this._pool.release(object); + } + }; } } diff --git a/src/vs/workbench/contrib/chat/browser/chatParticipant.contribution.ts b/src/vs/workbench/contrib/chat/browser/chatParticipant.contribution.ts index 3256369a754a..f3cda6408937 100644 --- a/src/vs/workbench/contrib/chat/browser/chatParticipant.contribution.ts +++ b/src/vs/workbench/contrib/chat/browser/chatParticipant.contribution.ts @@ -21,14 +21,15 @@ import { Registry } from '../../../../platform/registry/common/platform.js'; import { ViewPaneContainer } from '../../../browser/parts/views/viewPaneContainer.js'; import { IWorkbenchContribution } from '../../../common/contributions.js'; import { IViewContainersRegistry, IViewDescriptor, IViewsRegistry, ViewContainer, ViewContainerLocation, Extensions as ViewExtensions } from '../../../common/views.js'; -import { IExtensionFeatureTableRenderer, IRenderedData, ITableData, IRowData, IExtensionFeaturesRegistry, Extensions } from '../../../services/extensionManagement/common/extensionFeatures.js'; +import { Extensions, IExtensionFeaturesRegistry, IExtensionFeatureTableRenderer, IRenderedData, IRowData, ITableData } from '../../../services/extensionManagement/common/extensionFeatures.js'; import { isProposedApiEnabled } from '../../../services/extensions/common/extensions.js'; import * as extensionsRegistry from '../../../services/extensions/common/extensionsRegistry.js'; import { showExtensionsWithIdsCommandId } from '../../extensions/browser/extensionsActions.js'; import { IExtension, IExtensionsWorkbenchService } from '../../extensions/common/extensions.js'; -import { ChatAgentLocation, IChatAgentData, IChatAgentService } from '../common/chatAgents.js'; +import { IChatAgentData, IChatAgentService } from '../common/chatAgents.js'; import { ChatContextKeys } from '../common/chatContextKeys.js'; import { IRawChatParticipantContribution } from '../common/chatParticipantContribTypes.js'; +import { ChatAgentLocation, ChatConfiguration } from '../common/constants.js'; import { ChatViewId } from './chat.js'; import { CHAT_EDITING_SIDEBAR_PANEL_ID, CHAT_SIDEBAR_PANEL_ID, ChatViewPane } from './chatViewPane.js'; @@ -36,7 +37,7 @@ import { CHAT_EDITING_SIDEBAR_PANEL_ID, CHAT_SIDEBAR_PANEL_ID, ChatViewPane } fr const chatViewContainer: ViewContainer = Registry.as(ViewExtensions.ViewContainersRegistry).registerViewContainer({ id: CHAT_SIDEBAR_PANEL_ID, - title: localize2('chat.viewContainer.label', "Chat"), + title: { value: 'Copilot', original: 'Copilot' }, icon: Codicon.commentDiscussion, ctorDescriptor: new SyncDescriptor(ViewPaneContainer, [CHAT_SIDEBAR_PANEL_ID, { mergeViewWithContainerWhenSingleView: true }]), storageId: CHAT_SIDEBAR_PANEL_ID, @@ -49,7 +50,7 @@ const chatViewDescriptor: IViewDescriptor[] = [{ containerIcon: chatViewContainer.icon, containerTitle: chatViewContainer.title.value, singleViewPaneContainerTitle: chatViewContainer.title.value, - name: localize2('chat.viewContainer.label', "Chat"), + name: { value: 'Copilot', original: 'Copilot' }, canToggleVisibility: false, canMoveView: true, openCommandActionDescriptor: { @@ -107,10 +108,13 @@ const editsViewDescriptor: IViewDescriptor[] = [{ order: 2 }, ctorDescriptor: new SyncDescriptor(ChatViewPane, [{ location: ChatAgentLocation.EditingSession }]), - when: ContextKeyExpr.or( - ChatContextKeys.Setup.hidden.negate(), - ChatContextKeys.Setup.installed, - ChatContextKeys.editingParticipantRegistered + when: ContextKeyExpr.and( + ContextKeyExpr.has(`config.${ChatConfiguration.UnifiedChatView}`).negate(), + ContextKeyExpr.or( + ChatContextKeys.Setup.hidden.negate(), + ChatContextKeys.Setup.installed, + ChatContextKeys.editingParticipantRegistered + ) ) }]; Registry.as(ViewExtensions.ViewsRegistry).registerViews(editsViewDescriptor, editsViewContainer); diff --git a/src/vs/workbench/contrib/chat/browser/chatPasteProviders.ts b/src/vs/workbench/contrib/chat/browser/chatPasteProviders.ts index fc71eeb1bdc8..72911840c5f3 100644 --- a/src/vs/workbench/contrib/chat/browser/chatPasteProviders.ts +++ b/src/vs/workbench/contrib/chat/browser/chatPasteProviders.ts @@ -2,25 +2,28 @@ * Copyright (c) Microsoft Corporation. All rights reserved. * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ - +import { VSBuffer } from '../../../../base/common/buffer.js'; import { CancellationToken } from '../../../../base/common/cancellation.js'; +import { Codicon } from '../../../../base/common/codicons.js'; import { createStringDataTransferItem, IDataTransferItem, IReadonlyVSDataTransfer, VSDataTransfer } from '../../../../base/common/dataTransfer.js'; import { HierarchicalKind } from '../../../../base/common/hierarchicalKind.js'; +import { Disposable } from '../../../../base/common/lifecycle.js'; +import { Mimes } from '../../../../base/common/mime.js'; +import { basename, joinPath } from '../../../../base/common/resources.js'; +import { URI, UriComponents } from '../../../../base/common/uri.js'; import { IRange } from '../../../../editor/common/core/range.js'; import { DocumentPasteContext, DocumentPasteEdit, DocumentPasteEditProvider, DocumentPasteEditsSession } from '../../../../editor/common/languages.js'; import { ITextModel } from '../../../../editor/common/model.js'; import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.js'; -import { Disposable } from '../../../../base/common/lifecycle.js'; -import { ChatInputPart } from './chatInputPart.js'; -import { IChatWidgetService } from './chat.js'; -import { Codicon } from '../../../../base/common/codicons.js'; +import { IModelService } from '../../../../editor/common/services/model.js'; import { localize } from '../../../../nls.js'; -import { IChatRequestPasteVariableEntry, IChatRequestVariableEntry } from '../common/chatModel.js'; +import { IEnvironmentService } from '../../../../platform/environment/common/environment.js'; +import { IFileService } from '../../../../platform/files/common/files.js'; +import { ILogService } from '../../../../platform/log/common/log.js'; import { IExtensionService, isProposedApiEnabled } from '../../../services/extensions/common/extensions.js'; -import { Mimes } from '../../../../base/common/mime.js'; -import { IModelService } from '../../../../editor/common/services/model.js'; -import { URI, UriComponents } from '../../../../base/common/uri.js'; -import { basename } from '../../../../base/common/resources.js'; +import { IChatRequestPasteVariableEntry, IChatRequestVariableEntry } from '../common/chatModel.js'; +import { IChatWidgetService } from './chat.js'; +import { ChatInputPart } from './chatInputPart.js'; import { resizeImage } from './imageUtils.js'; const COPY_MIME_TYPES = 'application/vnd.code.additional-editor-data'; @@ -31,6 +34,7 @@ interface SerializedCopyData { } export class PasteImageProvider implements DocumentPasteEditProvider { + private readonly imagesFolder: URI; public readonly kind = new HierarchicalKind('chat.attach.image'); public readonly providedPasteEditKinds = [this.kind]; @@ -41,7 +45,13 @@ export class PasteImageProvider implements DocumentPasteEditProvider { constructor( private readonly chatWidgetService: IChatWidgetService, private readonly extensionService: IExtensionService, - ) { } + @IFileService private readonly fileService: IFileService, + @IEnvironmentService private readonly environmentService: IEnvironmentService, + @ILogService private readonly logService: ILogService, + ) { + this.imagesFolder = joinPath(this.environmentService.workspaceStorageHome, 'vscode-chat-images'); + this.cleanupOldImages(); + } async provideDocumentPasteEdits(model: ITextModel, ranges: readonly IRange[], dataTransfer: IReadonlyVSDataTransfer, context: DocumentPasteContext, token: CancellationToken): Promise { if (!this.extensionService.extensions.some(ext => isProposedApiEnabled(ext, 'chatReferenceBinaryData'))) { @@ -90,12 +100,17 @@ export class PasteImageProvider implements DocumentPasteEditProvider { tempDisplayName = `${displayName} ${appendValue}`; } + const fileReference = await this.createFileForMedia(currClipboard, mimeType); + if (token.isCancellationRequested || !fileReference) { + return; + } + const scaledImageData = await resizeImage(currClipboard); if (token.isCancellationRequested || !scaledImageData) { return; } - const scaledImageContext = await getImageAttachContext(scaledImageData, mimeType, token, tempDisplayName); + const scaledImageContext = await getImageAttachContext(scaledImageData, mimeType, token, tempDisplayName, fileReference); if (token.isCancellationRequested || !scaledImageContext) { return; } @@ -111,21 +126,75 @@ export class PasteImageProvider implements DocumentPasteEditProvider { const edit = createCustomPasteEdit(model, scaledImageContext, mimeType, this.kind, localize('pastedImageAttachment', 'Pasted Image Attachment'), this.chatWidgetService); return createEditSession(edit); } + + private async createFileForMedia( + dataTransfer: Uint8Array, + mimeType: string, + ): Promise { + const exists = await this.fileService.exists(this.imagesFolder); + if (!exists) { + await this.fileService.createFolder(this.imagesFolder); + } + + const ext = mimeType.split('/')[1] || 'png'; + const filename = `image-${Date.now()}.${ext}`; + const fileUri = joinPath(this.imagesFolder, filename); + + const buffer = VSBuffer.wrap(dataTransfer); + await this.fileService.writeFile(fileUri, buffer); + + return fileUri; + } + + private async cleanupOldImages(): Promise { + const exists = await this.fileService.exists(this.imagesFolder); + if (!exists) { + return; + } + + const duration = 7 * 24 * 60 * 60 * 1000; // 7 days + const files = await this.fileService.resolve(this.imagesFolder); + if (!files.children) { + return; + } + + await Promise.all(files.children.map(async (file) => { + try { + const timestamp = this.getTimestampFromFilename(file.name); + if (timestamp && (Date.now() - timestamp > duration)) { + await this.fileService.del(file.resource); + } + } catch (err) { + this.logService.error('Failed to clean up old images', err); + } + })); + } + + private getTimestampFromFilename(filename: string): number | undefined { + const match = filename.match(/image-(\d+)\./); + if (match) { + return parseInt(match[1], 10); + } + return undefined; + } } -async function getImageAttachContext(data: Uint8Array, mimeType: string, token: CancellationToken, displayName: string): Promise { +async function getImageAttachContext(data: Uint8Array, mimeType: string, token: CancellationToken, displayName: string, resource: URI): Promise { const imageHash = await imageToHash(data); if (token.isCancellationRequested) { return undefined; } return { + kind: 'image', value: data, id: imageHash, name: displayName, isImage: true, icon: Codicon.fileMedia, - mimeType + mimeType, + isPasted: true, + references: [{ reference: resource, kind: 'reference' }] }; } @@ -308,10 +377,13 @@ export class ChatPasteProvidersFeature extends Disposable { @ILanguageFeaturesService languageFeaturesService: ILanguageFeaturesService, @IChatWidgetService chatWidgetService: IChatWidgetService, @IExtensionService extensionService: IExtensionService, - @IModelService modelService: IModelService + @IFileService fileService: IFileService, + @IModelService modelService: IModelService, + @IEnvironmentService environmentService: IEnvironmentService, + @ILogService logService: ILogService, ) { super(); - this._register(languageFeaturesService.documentPasteEditProvider.register({ scheme: ChatInputPart.INPUT_SCHEME, pattern: '*', hasAccessToAllModels: true }, new PasteImageProvider(chatWidgetService, extensionService))); + this._register(languageFeaturesService.documentPasteEditProvider.register({ scheme: ChatInputPart.INPUT_SCHEME, pattern: '*', hasAccessToAllModels: true }, new PasteImageProvider(chatWidgetService, extensionService, fileService, environmentService, logService))); this._register(languageFeaturesService.documentPasteEditProvider.register({ scheme: ChatInputPart.INPUT_SCHEME, pattern: '*', hasAccessToAllModels: true }, new PasteTextProvider(chatWidgetService, modelService))); this._register(languageFeaturesService.documentPasteEditProvider.register('*', new CopyTextProvider())); } diff --git a/src/vs/workbench/contrib/chat/browser/chatQuick.ts b/src/vs/workbench/contrib/chat/browser/chatQuick.ts index 412295c39eb4..137fc14757b1 100644 --- a/src/vs/workbench/contrib/chat/browser/chatQuick.ts +++ b/src/vs/workbench/contrib/chat/browser/chatQuick.ts @@ -19,12 +19,12 @@ import { IQuickInputService, IQuickWidget } from '../../../../platform/quickinpu import { editorBackground, inputBackground, quickInputBackground, quickInputForeground } from '../../../../platform/theme/common/colorRegistry.js'; import { IQuickChatOpenOptions, IQuickChatService, showChatView } from './chat.js'; import { ChatWidget } from './chatWidget.js'; -import { ChatAgentLocation } from '../common/chatAgents.js'; -import { ChatModel } from '../common/chatModel.js'; +import { ChatModel, isCellTextEditOperation } from '../common/chatModel.js'; import { IParsedChatRequest } from '../common/chatParserTypes.js'; import { IChatProgress, IChatService } from '../common/chatService.js'; import { IViewsService } from '../../../services/views/common/viewsService.js'; import { EDITOR_DRAG_AND_DROP_BACKGROUND } from '../../../common/theme.js'; +import { ChatAgentLocation } from '../common/constants.js'; export class QuickChatService extends Disposable implements IQuickChatService { readonly _serviceBrand: undefined; @@ -221,7 +221,7 @@ class QuickChat extends Disposable { ChatWidget, ChatAgentLocation.Panel, { isQuickChat: true }, - { autoScroll: true, renderInputOnTop: true, renderStyle: 'compact', menus: { inputSideToolbar: MenuId.ChatInputSide } }, + { autoScroll: true, renderInputOnTop: true, renderStyle: 'compact', menus: { inputSideToolbar: MenuId.ChatInputSide }, enableImplicitContext: true }, { listForeground: quickInputForeground, listBackground: quickInputBackground, @@ -300,11 +300,19 @@ class QuickChat extends Disposable { } } else if (item.kind === 'notebookEditGroup') { for (const group of item.edits) { - message.push({ - kind: 'notebookEdit', - edits: group, - uri: item.uri - }); + if (isCellTextEditOperation(group)) { + message.push({ + kind: 'textEdit', + edits: [group.edit], + uri: group.uri + }); + } else { + message.push({ + kind: 'notebookEdit', + edits: [group], + uri: item.uri + }); + } } } else { message.push(item); diff --git a/src/vs/workbench/contrib/chat/browser/chatQuotasService.ts b/src/vs/workbench/contrib/chat/browser/chatQuotasService.ts deleted file mode 100644 index 65d287a23f30..000000000000 --- a/src/vs/workbench/contrib/chat/browser/chatQuotasService.ts +++ /dev/null @@ -1,224 +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 { Codicon } from '../../../../base/common/codicons.js'; -import { safeIntl } from '../../../../base/common/date.js'; -import { language } from '../../../../base/common/platform.js'; -import { Emitter, Event } from '../../../../base/common/event.js'; -import { MarkdownString } from '../../../../base/common/htmlContent.js'; -import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.js'; -import { localize } from '../../../../nls.js'; -import { Action2, registerAction2 } from '../../../../platform/actions/common/actions.js'; -import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; -import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js'; -import { createDecorator, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; -import { IWorkbenchContribution } from '../../../common/contributions.js'; -import { IStatusbarService, StatusbarAlignment } from '../../../services/statusbar/browser/statusbar.js'; -import { ChatContextKeys } from '../common/chatContextKeys.js'; -import { ICommandService } from '../../../../platform/commands/common/commands.js'; - -export const IChatQuotasService = createDecorator('chatQuotasService'); - -export interface IChatQuotasService { - _serviceBrand: undefined; - - readonly onDidChangeQuotas: Event; - readonly quotas: IChatQuotas; - - acceptQuotas(quotas: IChatQuotas): void; - clearQuotas(): void; -} - -export interface IChatQuotas { - chatQuotaExceeded: boolean; - completionsQuotaExceeded: boolean; - quotaResetDate: Date | undefined; -} - -export const OPEN_CHAT_QUOTA_EXCEEDED_DIALOG = 'workbench.action.chat.openQuotaExceededDialog'; - -export class ChatQuotasService extends Disposable implements IChatQuotasService { - - declare _serviceBrand: undefined; - - private readonly _onDidChangeQuotas = this._register(new Emitter()); - readonly onDidChangeQuotas: Event = this._onDidChangeQuotas.event; - - private _quotas: IChatQuotas = { chatQuotaExceeded: false, completionsQuotaExceeded: false, quotaResetDate: undefined }; - get quotas(): IChatQuotas { return this._quotas; } - - private readonly chatQuotaExceededContextKey = ChatContextKeys.chatQuotaExceeded.bindTo(this.contextKeyService); - private readonly completionsQuotaExceededContextKey = ChatContextKeys.completionsQuotaExceeded.bindTo(this.contextKeyService); - - private ExtensionQuotaContextKeys = { - chatQuotaExceeded: 'github.copilot.chat.quotaExceeded', - completionsQuotaExceeded: 'github.copilot.completions.quotaExceeded', - }; - - constructor( - @IContextKeyService private readonly contextKeyService: IContextKeyService, - ) { - super(); - - this.registerListeners(); - this.registerActions(); - } - - private registerListeners(): void { - const chatQuotaExceededSet = new Set([this.ExtensionQuotaContextKeys.chatQuotaExceeded]); - const completionsQuotaExceededSet = new Set([this.ExtensionQuotaContextKeys.completionsQuotaExceeded]); - - this._register(this.contextKeyService.onDidChangeContext(e => { - let changed = false; - if (e.affectsSome(chatQuotaExceededSet)) { - const newChatQuotaExceeded = this.contextKeyService.getContextKeyValue(this.ExtensionQuotaContextKeys.chatQuotaExceeded); - if (typeof newChatQuotaExceeded === 'boolean' && newChatQuotaExceeded !== this._quotas.chatQuotaExceeded) { - this._quotas.chatQuotaExceeded = newChatQuotaExceeded; - changed = true; - } - } - - if (e.affectsSome(completionsQuotaExceededSet)) { - const newCompletionsQuotaExceeded = this.contextKeyService.getContextKeyValue(this.ExtensionQuotaContextKeys.completionsQuotaExceeded); - if (typeof newCompletionsQuotaExceeded === 'boolean' && newCompletionsQuotaExceeded !== this._quotas.completionsQuotaExceeded) { - this._quotas.completionsQuotaExceeded = newCompletionsQuotaExceeded; - changed = true; - } - } - - if (changed) { - this.updateContextKeys(); - this._onDidChangeQuotas.fire(); - } - })); - } - - private registerActions(): void { - const that = this; - - class ShowLimitReachedDialogAction extends Action2 { - - constructor() { - super({ - id: OPEN_CHAT_QUOTA_EXCEEDED_DIALOG, - title: localize('upgradeChat', "Upgrade to Copilot Pro"), - }); - } - - override async run(accessor: ServicesAccessor) { - const commandService = accessor.get(ICommandService); - const dialogService = accessor.get(IDialogService); - - const dateFormatter = safeIntl.DateTimeFormat(language, { year: 'numeric', month: 'long', day: 'numeric' }); - - let message: string; - const { chatQuotaExceeded, completionsQuotaExceeded } = that.quotas; - if (chatQuotaExceeded && !completionsQuotaExceeded) { - message = localize('chatQuotaExceeded', "You've run out of free chat messages. You still have free code completions available in the Copilot Free plan. These limits will reset on {0}.", dateFormatter.format(that.quotas.quotaResetDate)); - } else if (completionsQuotaExceeded && !chatQuotaExceeded) { - message = localize('completionsQuotaExceeded', "You've run out of free code completions. You still have free chat messages available in the Copilot Free plan. These limits will reset on {0}.", dateFormatter.format(that.quotas.quotaResetDate)); - } else { - message = localize('chatAndCompletionsQuotaExceeded', "You've reached the limit of the Copilot Free plan. These limits will reset on {0}.", dateFormatter.format(that.quotas.quotaResetDate)); - } - - const upgradeToPro = localize('upgradeToPro', "Upgrade to Copilot Pro (your first 30 days are free) for:\n- Unlimited code completions\n- Unlimited chat messages\n- Access to additional models"); - - await dialogService.prompt({ - type: 'none', - message: localize('copilotFree', "Copilot Limit Reached"), - cancelButton: { - label: localize('dismiss', "Dismiss"), - run: () => { /* noop */ } - }, - buttons: [ - { - label: localize('managePlan', "Upgrade to Copilot Pro"), - run: () => commandService.executeCommand('workbench.action.chat.upgradePlan') - }, - ], - custom: { - icon: Codicon.copilotWarningLarge, - markdownDetails: [ - { markdown: new MarkdownString(message, true) }, - { markdown: new MarkdownString(upgradeToPro, true) } - ] - } - }); - } - } - - registerAction2(ShowLimitReachedDialogAction); - } - - acceptQuotas(quotas: IChatQuotas): void { - this._quotas = quotas; - this.updateContextKeys(); - - this._onDidChangeQuotas.fire(); - } - - clearQuotas(): void { - if (this.quotas.chatQuotaExceeded || this.quotas.completionsQuotaExceeded) { - this.acceptQuotas({ chatQuotaExceeded: false, completionsQuotaExceeded: false, quotaResetDate: undefined }); - } - } - - private updateContextKeys(): void { - this.chatQuotaExceededContextKey.set(this._quotas.chatQuotaExceeded); - this.completionsQuotaExceededContextKey.set(this._quotas.completionsQuotaExceeded); - } -} - -export class ChatQuotasStatusBarEntry extends Disposable implements IWorkbenchContribution { - - static readonly ID = 'chat.quotasStatusBarEntry'; - - private readonly entry = this._register(new DisposableStore()); - - constructor( - @IStatusbarService private readonly statusbarService: IStatusbarService, - @IChatQuotasService private readonly chatQuotasService: IChatQuotasService - ) { - super(); - - this._register(Event.runAndSubscribe(this.chatQuotasService.onDidChangeQuotas, () => this.updateStatusbarEntry())); - } - - private updateStatusbarEntry(): void { - this.entry.clear(); - - const { chatQuotaExceeded, completionsQuotaExceeded } = this.chatQuotasService.quotas; - if (chatQuotaExceeded || completionsQuotaExceeded) { - let text: string; - if (chatQuotaExceeded && !completionsQuotaExceeded) { - text = localize('chatQuotaExceededStatus', "Chat limit reached"); - } else if (completionsQuotaExceeded && !chatQuotaExceeded) { - text = localize('completionsQuotaExceededStatus', "Completions limit reached"); - } else { - text = localize('chatAndCompletionsQuotaExceededStatus', "Copilot limit reached"); - } - - this.entry.add(this.statusbarService.addEntry({ - name: localize('indicator', "Copilot Limit Indicator"), - text: `$(copilot-warning) ${text}`, - ariaLabel: text, - command: OPEN_CHAT_QUOTA_EXCEEDED_DIALOG, - showInAllWindows: true, - kind: 'prominent', - tooltip: quotaToButtonMessage({ chatQuotaExceeded, completionsQuotaExceeded }) - }, ChatQuotasStatusBarEntry.ID, StatusbarAlignment.RIGHT, 1)); - } - } -} - -export function quotaToButtonMessage({ chatQuotaExceeded, completionsQuotaExceeded }: { chatQuotaExceeded: boolean; completionsQuotaExceeded: boolean }): string { - if (chatQuotaExceeded && !completionsQuotaExceeded) { - return localize('chatQuotaExceededButton', "Monthly chat messages limit reached. Click for details."); - } else if (completionsQuotaExceeded && !chatQuotaExceeded) { - return localize('completionsQuotaExceededButton', "Monthly code completions limit reached. Click for details."); - } else { - return localize('chatAndCompletionsQuotaExceededButton', "Copilot Free plan limit reached. Click for details."); - } -} diff --git a/src/vs/workbench/contrib/chat/browser/chatSelectedTools.ts b/src/vs/workbench/contrib/chat/browser/chatSelectedTools.ts new file mode 100644 index 000000000000..e3e71ba0b69b --- /dev/null +++ b/src/vs/workbench/contrib/chat/browser/chatSelectedTools.ts @@ -0,0 +1,93 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + + +import { reset } from '../../../../base/browser/dom.js'; +import { IActionViewItemProvider } from '../../../../base/browser/ui/actionbar/actionbar.js'; +import { renderLabelWithIcons } from '../../../../base/browser/ui/iconLabel/iconLabels.js'; +import { Disposable } from '../../../../base/common/lifecycle.js'; +import { autorun, derived, IObservable, observableFromEvent, observableValue } from '../../../../base/common/observable.js'; +import { assertType } from '../../../../base/common/types.js'; +import { localize } from '../../../../nls.js'; +import { MenuEntryActionViewItem } from '../../../../platform/actions/browser/menuEntryActionViewItem.js'; +import { MenuItemAction } from '../../../../platform/actions/common/actions.js'; +import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; +import { ILanguageModelToolsService, IToolData } from '../common/languageModelToolsService.js'; + +export class ChatSelectedTools extends Disposable { + + private readonly _selectedTools = observableValue(this, undefined); + + readonly tools: IObservable; + + readonly toolsActionItemViewItemProvider: IActionViewItemProvider; + + constructor( + @ILanguageModelToolsService toolsService: ILanguageModelToolsService, + @IInstantiationService instaService: IInstantiationService + ) { + super(); + + const allTools = observableFromEvent( + toolsService.onDidChangeTools, + () => Array.from(toolsService.getTools()).filter(t => t.canBeReferencedInPrompt) + ); + + this.tools = derived(r => { + const custom = this._selectedTools.read(r); + return custom ?? allTools.read(r); + }); + + const toolsCount = derived(r => { + const count = allTools.read(r).length; + const enabled = this.tools.read(r).length; + return { count, enabled }; + }); + + this.toolsActionItemViewItemProvider = (action, options) => { + if (!(action instanceof MenuItemAction)) { + return undefined; + } + + return instaService.createInstance(class extends MenuEntryActionViewItem { + + override render(container: HTMLElement): void { + this.options.icon = false; + this.options.label = true; + container.classList.add('chat-mcp'); + super.render(container); + } + + protected override updateLabel(): void { + this._store.add(autorun(r => { + assertType(this.label); + + const { enabled, count } = toolsCount.read(r); + + if (count === 0) { + super.updateLabel(); + return; + } + + const message = enabled !== count + ? localize('tool.1', "{0} {1} of {2}", '$(tools)', enabled, count) + : localize('tool.0', "{0} {1}", '$(tools)', count); + reset(this.label, ...renderLabelWithIcons(message)); + })); + } + + }, action, { ...options, keybindingNotRenderedWithLabel: true }); + + }; + } + + update(tools: IToolData[]): void { + this._selectedTools.set(tools, undefined); + } + + reset(): void { + this._selectedTools.set(undefined, undefined); + } +} diff --git a/src/vs/workbench/contrib/chat/browser/chatSetup.ts b/src/vs/workbench/contrib/chat/browser/chatSetup.ts index 12003d4dc8a7..c7701df9dc05 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSetup.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSetup.ts @@ -7,68 +7,68 @@ import './media/chatViewSetup.css'; import { $, getActiveElement, setVisibility } from '../../../../base/browser/dom.js'; import { ButtonWithDropdown } from '../../../../base/browser/ui/button/button.js'; import { renderIcon } from '../../../../base/browser/ui/iconLabel/iconLabels.js'; +import { mainWindow } from '../../../../base/browser/window.js'; import { toAction, WorkbenchActionExecutedClassification, WorkbenchActionExecutedEvent } from '../../../../base/common/actions.js'; -import { Barrier, timeout } from '../../../../base/common/async.js'; -import { CancellationToken, CancellationTokenSource } from '../../../../base/common/cancellation.js'; +import { timeout } from '../../../../base/common/async.js'; import { Codicon } from '../../../../base/common/codicons.js'; +import { toErrorMessage } from '../../../../base/common/errorMessage.js'; import { isCancellationError } from '../../../../base/common/errors.js'; import { Emitter, Event } from '../../../../base/common/event.js'; import { MarkdownString } from '../../../../base/common/htmlContent.js'; import { Lazy } from '../../../../base/common/lazy.js'; -import { Disposable, MutableDisposable } from '../../../../base/common/lifecycle.js'; -import { IRequestContext } from '../../../../base/parts/request/common/request.js'; +import { combinedDisposable, Disposable, DisposableStore, IDisposable, MutableDisposable } from '../../../../base/common/lifecycle.js'; +import Severity from '../../../../base/common/severity.js'; +import { StopWatch } from '../../../../base/common/stopwatch.js'; +import { equalsIgnoreCase } from '../../../../base/common/strings.js'; +import { isObject } from '../../../../base/common/types.js'; +import { URI } from '../../../../base/common/uri.js'; import { ServicesAccessor } from '../../../../editor/browser/editorExtensions.js'; import { MarkdownRenderer } from '../../../../editor/browser/widget/markdownRenderer/browser/markdownRenderer.js'; import { localize, localize2 } from '../../../../nls.js'; import { Action2, MenuId, registerAction2 } from '../../../../platform/actions/common/actions.js'; import { ICommandService } from '../../../../platform/commands/common/commands.js'; import { ConfigurationTarget, IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; -import { ContextKeyExpr, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; +import { Extensions as ConfigurationExtensions, IConfigurationRegistry } from '../../../../platform/configuration/common/configurationRegistry.js'; +import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js'; import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js'; import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js'; -import { ExtensionIdentifier } from '../../../../platform/extensions/common/extensions.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { ILogService } from '../../../../platform/log/common/log.js'; +import { IOpenerService } from '../../../../platform/opener/common/opener.js'; import product from '../../../../platform/product/common/product.js'; import { IProductService } from '../../../../platform/product/common/productService.js'; import { IProgressService, ProgressLocation } from '../../../../platform/progress/common/progress.js'; +import { IQuickInputService } from '../../../../platform/quickinput/common/quickInput.js'; import { Registry } from '../../../../platform/registry/common/platform.js'; -import { asText, IRequestService } from '../../../../platform/request/common/request.js'; -import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; import { ITelemetryService, TelemetryLevel } from '../../../../platform/telemetry/common/telemetry.js'; import { defaultButtonStyles } from '../../../../platform/theme/browser/defaultStyles.js'; -import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; +import { IWorkspaceTrustRequestService } from '../../../../platform/workspace/common/workspaceTrust.js'; import { IWorkbenchContribution } from '../../../common/contributions.js'; import { IViewDescriptorService, ViewContainerLocation } from '../../../common/views.js'; import { IActivityService, ProgressBadge } from '../../../services/activity/common/activity.js'; -import { AuthenticationSession, IAuthenticationExtensionsService, IAuthenticationService } from '../../../services/authentication/common/authentication.js'; -import { IWorkbenchExtensionEnablementService } from '../../../services/extensionManagement/common/extensionManagement.js'; +import { AuthenticationSession, IAuthenticationService } from '../../../services/authentication/common/authentication.js'; +import { ExtensionUrlHandlerOverrideRegistry } from '../../../services/extensions/browser/extensionUrlHandler.js'; +import { nullExtensionDescription } from '../../../services/extensions/common/extensions.js'; +import { IHostService } from '../../../services/host/browser/host.js'; import { IWorkbenchLayoutService, Parts } from '../../../services/layout/browser/layoutService.js'; +import { ILifecycleService } from '../../../services/lifecycle/common/lifecycle.js'; +import { IStatusbarService } from '../../../services/statusbar/browser/statusbar.js'; import { IViewsService } from '../../../services/views/common/viewsService.js'; -import { IExtension, IExtensionsWorkbenchService } from '../../extensions/common/extensions.js'; -import { IChatAgentService } from '../common/chatAgents.js'; +import { IExtensionsWorkbenchService } from '../../extensions/common/extensions.js'; +import { IChatAgentImplementation, IChatAgentRequest, IChatAgentResult, IChatAgentService, IChatWelcomeMessageContent } from '../common/chatAgents.js'; import { ChatContextKeys } from '../common/chatContextKeys.js'; -import { CHAT_CATEGORY } from './actions/chatActions.js'; +import { ChatEntitlement, ChatEntitlementContext, ChatEntitlementRequests, ChatEntitlementService, IChatEntitlementService } from '../common/chatEntitlementService.js'; +import { IChatProgress, IChatProgressMessage, IChatService, IChatWarningMessage } from '../common/chatService.js'; +import { CHAT_CATEGORY, CHAT_SETUP_ACTION_ID } from './actions/chatActions.js'; import { ChatViewId, EditsViewId, ensureSideBarChatViewSize, preferCopilotEditsView, showCopilotView } from './chat.js'; import { CHAT_EDITING_SIDEBAR_PANEL_ID, CHAT_SIDEBAR_PANEL_ID } from './chatViewPane.js'; import { ChatViewsWelcomeExtensions, IChatViewsWelcomeContributionRegistry } from './viewsWelcome/chatViewsWelcome.js'; -import { IChatQuotasService } from './chatQuotasService.js'; -import { mainWindow } from '../../../../base/browser/window.js'; -import { IOpenerService } from '../../../../platform/opener/common/opener.js'; -import { URI } from '../../../../base/common/uri.js'; -import { IHostService } from '../../../services/host/browser/host.js'; -import Severity from '../../../../base/common/severity.js'; -import { IWorkbenchEnvironmentService } from '../../../services/environment/common/environmentService.js'; -import { isWeb } from '../../../../base/common/platform.js'; -import { ExtensionUrlHandlerOverrideRegistry } from '../../../services/extensions/browser/extensionUrlHandler.js'; -import { IWorkspaceTrustRequestService } from '../../../../platform/workspace/common/workspaceTrust.js'; -import { toErrorMessage } from '../../../../base/common/errorMessage.js'; -import { StopWatch } from '../../../../base/common/stopwatch.js'; -import { IConfigurationRegistry, Extensions as ConfigurationExtensions, IConfigurationNode } from '../../../../platform/configuration/common/configurationRegistry.js'; -import { IQuickInputService } from '../../../../platform/quickinput/common/quickInput.js'; -import { ILifecycleService } from '../../../services/lifecycle/common/lifecycle.js'; -import { equalsIgnoreCase } from '../../../../base/common/strings.js'; -import { IWorkbenchAssignmentService } from '../../../services/assignment/common/assignmentService.js'; +import { ChatAgentLocation } from '../common/constants.js'; +import { ILanguageModelsService } from '../common/languageModels.js'; +import { Dialog } from '../../../../base/browser/ui/dialog/dialog.js'; +import { ILayoutService } from '../../../../platform/layout/browser/layoutService.js'; +import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; +import { createWorkbenchDialogOptions } from '../../../../platform/dialogs/browser/dialog.js'; const defaultChat = { extensionId: product.defaultChatAgent?.extensionId ?? '', @@ -79,37 +79,294 @@ const defaultChat = { skusDocumentationUrl: product.defaultChatAgent?.skusDocumentationUrl ?? '', publicCodeMatchesUrl: product.defaultChatAgent?.publicCodeMatchesUrl ?? '', upgradePlanUrl: product.defaultChatAgent?.upgradePlanUrl ?? '', - providerId: product.defaultChatAgent?.providerId ?? '', providerName: product.defaultChatAgent?.providerName ?? '', enterpriseProviderId: product.defaultChatAgent?.enterpriseProviderId ?? '', enterpriseProviderName: product.defaultChatAgent?.enterpriseProviderName ?? '', - providerSetting: product.defaultChatAgent?.providerSetting ?? '', providerUriSetting: product.defaultChatAgent?.providerUriSetting ?? '', providerScopes: product.defaultChatAgent?.providerScopes ?? [[]], - entitlementUrl: product.defaultChatAgent?.entitlementUrl ?? '', - entitlementSignupLimitedUrl: product.defaultChatAgent?.entitlementSignupLimitedUrl ?? '', manageSettingsUrl: product.defaultChatAgent?.manageSettingsUrl ?? '', + completionsAdvancedSetting: product.defaultChatAgent?.completionsAdvancedSetting ?? '', + walkthroughCommand: product.defaultChatAgent?.walkthroughCommand ?? '', + completionsRefreshTokenCommand: product.defaultChatAgent?.completionsRefreshTokenCommand ?? '', + chatRefreshTokenCommand: product.defaultChatAgent?.chatRefreshTokenCommand ?? '', }; -enum ChatEntitlement { - /** Signed out */ - Unknown = 1, - /** Signed in but not yet resolved */ - Unresolved, - /** Signed in and entitled to Limited */ - Available, - /** Signed in but not entitled to Limited */ - Unavailable, - /** Signed-up to Limited */ - Limited, - /** Signed-up to Pro */ - Pro +//#region Contribution + +class SetupChatAgentImplementation extends Disposable implements IChatAgentImplementation { + + static register(instantiationService: IInstantiationService, location: ChatAgentLocation, isToolsAgent: boolean, context: ChatEntitlementContext, controller: Lazy): IDisposable { + return instantiationService.invokeFunction(accessor => { + const chatAgentService = accessor.get(IChatAgentService); + + // TODO@bpasero: expand this to more cases (installed, not signed in / not signed up) + const setupChatAgentContext = ContextKeyExpr.and( + ChatContextKeys.Setup.hidden.negate(), + ChatContextKeys.Setup.installed.negate(), + ChatContextKeys.Setup.fromDialog + ); + + const id = location === ChatAgentLocation.Panel ? 'setup.chat' : isToolsAgent ? 'setup.agent' : 'setup.edits'; + + const welcomeMessageContent: IChatWelcomeMessageContent = location === ChatAgentLocation.Panel ? + { + title: localize('chatTitle', "Ask Copilot"), + message: new MarkdownString(localize('chatMessage', "Copilot is powered by AI, so mistakes are possible. Review output carefully before use.")), + icon: Codicon.copilotLarge + } : isToolsAgent ? + { + title: localize('editsTitle', "Edit with Copilot"), + message: new MarkdownString(localize('agentMessage', "Ask Copilot to edit your files in agent mode. Copilot will automatically use multiple requests to pick files to edit, run terminal commands, and iterate on errors.")), + icon: Codicon.copilotLarge + } : + { + title: localize('editsTitle', "Edit with Copilot"), + message: new MarkdownString(localize('editsMessage', "Start your editing session by defining a set of files that you want to work with. Then ask Copilot for the changes you want to make.")), + icon: Codicon.copilotLarge + }; + + const disposable = new DisposableStore(); + + disposable.add(chatAgentService.registerAgent(id, { + id, + name: `${defaultChat.providerName} Copilot`, + isDefault: true, + isToolsAgent, + when: setupChatAgentContext?.serialize(), + slashCommands: [], + disambiguation: [], + locations: [location], + metadata: { welcomeMessageContent }, + description: location === ChatAgentLocation.Panel ? localize('chatDescription', "Ask Copilot") : isToolsAgent ? localize('agentDescription', "Edit files in your workspace in agent mode (Experimental)") : localize('editsDescription', "Edit files in your workspace"), + extensionId: nullExtensionDescription.identifier, + extensionDisplayName: nullExtensionDescription.name, + extensionPublisherId: nullExtensionDescription.publisher + })); + + disposable.add(chatAgentService.registerAgentImplementation(id, disposable.add(instantiationService.createInstance(SetupChatAgentImplementation, context, controller)))); + + return disposable; + }); + } + + constructor( + private readonly context: ChatEntitlementContext, + private readonly controller: Lazy, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @ILogService private readonly logService: ILogService, + @IConfigurationService private readonly configurationService: IConfigurationService, + @ITelemetryService private readonly telemetryService: ITelemetryService + ) { + super(); + } + + async invoke(request: IChatAgentRequest, progress: (part: IChatProgress) => void): Promise { + return this.instantiationService.invokeFunction(async accessor => { + const chatService = accessor.get(IChatService); // use accessor for lazy loading + const languageModelsService = accessor.get(ILanguageModelsService); // of chat related services + + return this.doInvoke(request, progress, chatService, languageModelsService); + }); + } + + private async doInvoke(request: IChatAgentRequest, progress: (part: IChatProgress) => void, chatService: IChatService, languageModelsService: ILanguageModelsService): Promise { + this.telemetryService.publicLog2('workbenchActionExecuted', { id: CHAT_SETUP_ACTION_ID, from: 'chat' }); + + const dialog = this.instantiationService.createInstance(ChatSetupDialog, this.context); + const result = await dialog.show(); + + // Proceed with setting up Copilot + if (result) { + const setupListener = this.controller.value.onDidChange(() => { + switch (this.controller.value.step) { + case ChatSetupStep.SigningIn: + progress({ + kind: 'progressMessage', + content: new MarkdownString(localize('setupChatSignIn2', "Signing in to {0}.", ChatEntitlementRequests.providerId(this.configurationService) === defaultChat.enterpriseProviderId ? defaultChat.enterpriseProviderName : defaultChat.providerName)), + } satisfies IChatProgressMessage); + break; + case ChatSetupStep.Installing: + progress({ + kind: 'progressMessage', + content: new MarkdownString(localize('installingCopilot', "Getting Copilot ready.")), + } satisfies IChatProgressMessage); + break; + } + }); + + const whenDefaultModel = Event.toPromise(Event.filter(languageModelsService.onDidChangeLanguageModels, e => e.added?.some(added => added.metadata.isDefault) ?? false)); + + let success = false; + try { + switch (result) { + case ChatSetupDialogResult.SetupWithEnterpriseProvider: + success = await this.controller.value.setupWithProvider(true); + break; + case ChatSetupDialogResult.SetupWithoutEnterpriseProvider: + success = await this.controller.value.setupWithProvider(false); + break; + case ChatSetupDialogResult.DefaultSetup: + success = await this.controller.value.setup(); + break; + } + } catch (error) { + this.logService.error(localize('setupError', "Error during setup: {0}", toErrorMessage(error))); + } finally { + setupListener.dispose(); + } + + if (success) { + progress({ + kind: 'progressMessage', + content: new MarkdownString(localize('copilotReady', "Copilot is ready to use.")), + } satisfies IChatProgressMessage); + + // Await a default model to be present before attempting + // to re-submit the request. Otherwise, the request will fail. + const hasDefaultModel = await Promise.race([ + timeout(5000), + whenDefaultModel + ]); + + if (hasDefaultModel) { + chatService.cancelCurrentRequestForSession(request.sessionId); + chatService.sendRequest(request.sessionId, request.message); + } + } + else { + progress({ + kind: 'warning', + content: new MarkdownString(localize('copilotSetupError', "Copilot setup failed. [Try again]({0} \"Retry\").", `command:${CHAT_SETUP_ACTION_ID}`), { isTrusted: true }), + } satisfies IChatWarningMessage); + } + } + + // User has cancelled the setup + else { + progress({ + kind: 'warning', + content: new MarkdownString(localize('settingUpCopilotWarning', "You need to [set up Copilot]({0} \"Set up Copilot\") to use Chat.", `command:${CHAT_SETUP_ACTION_ID}`), { isTrusted: true }), + } satisfies IChatWarningMessage); + } + + return {}; + } } -//#region Contribution +enum ChatSetupDialogResult { + Canceled = 0, + DefaultSetup = 1, + SetupWithoutEnterpriseProvider = 2, + SetupWithEnterpriseProvider = 3 +} + +class ChatSetupDialog { + + constructor( + private readonly context: ChatEntitlementContext, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @ITelemetryService private readonly telemetryService: ITelemetryService, + @IContextMenuService private readonly contextMenuService: IContextMenuService, + @ILayoutService private readonly layoutService: IWorkbenchLayoutService, + @IKeybindingService private readonly keybindingService: IKeybindingService, + ) { } + + async show(): Promise { + const buttons = [ + this.getPrimaryButton(), + localize('cancelButton', "Cancel") + ]; + const disposables = new DisposableStore(); + + let result: ChatSetupDialogResult | undefined = undefined; + + const dialog = disposables.add(new Dialog( + this.layoutService.activeContainer, + localize('copilotFree', "Welcome to Copilot"), + buttons, + createWorkbenchDialogOptions({ + type: 'none', + cancelId: buttons.length - 1, + renderBody: body => body.appendChild(this.create(disposables)), + icon: Codicon.copilotLarge, + primaryButtonDropdown: { + contextMenuProvider: this.contextMenuService, + addPrimaryActionToDropdown: false, + actions: [ + toAction({ id: 'setupWithProvider', label: localize('setupWithProvider', "Sign in with a {0} Account", defaultChat.providerName), run: () => result = ChatSetupDialogResult.SetupWithoutEnterpriseProvider }), + toAction({ id: 'setupWithEnterpriseProvider', label: localize('setupWithEnterpriseProvider', "Sign in with a {0} Account", defaultChat.enterpriseProviderName), run: () => result = ChatSetupDialogResult.SetupWithEnterpriseProvider }), + ] + } + }, this.keybindingService, this.layoutService) + )); + + const { button } = await dialog.show(); + disposables.dispose(); + + return button === 0 ? result ?? ChatSetupDialogResult.DefaultSetup : ChatSetupDialogResult.Canceled; + } -const TRIGGER_SETUP_COMMAND_ID = 'workbench.action.chat.triggerSetup'; -const TRIGGER_SETUP_COMMAND_LABEL = localize2('triggerChatSetup', "Use AI Features with Copilot for Free..."); + private getPrimaryButton(): string { + switch (this.context.state.entitlement) { + case ChatEntitlement.Unknown: + return this.context.state.registered ? localize('signUp', "Sign in to Use Copilot") : localize('signUpFree', "Sign in to Use Copilot for Free"); + case ChatEntitlement.Unresolved: + return this.context.state.registered ? localize('startUp', "Use Copilot") : localize('startUpLimited', "Use Copilot for Free"); + case ChatEntitlement.Available: + case ChatEntitlement.Limited: + return localize('startUpLimited', "Use Copilot for Free"); + case ChatEntitlement.Pro: + case ChatEntitlement.Unavailable: + return localize('startUp', "Use Copilot"); + } + } + + private create(disposables: DisposableStore): HTMLElement { + const element = $('.chat-setup-view'); + + const markdown = this.instantiationService.createInstance(MarkdownRenderer, {}); + + // Header + const header = localize({ key: 'header', comment: ['{Locked="[Copilot]({0})"}'] }, "[Copilot]({0}) is your AI pair programmer.", defaultChat.documentationUrl); + element.appendChild($('p', undefined, disposables.add(markdown.render(new MarkdownString(header, { isTrusted: true }))).element)); + element.appendChild( + $('div.chat-features-container', undefined, + $('div', undefined, + $('div.chat-feature-container', undefined, + renderIcon(Codicon.code), + $('span', undefined, localize('featureChat', "Code faster with Completions")) + ), + $('div.chat-feature-container', undefined, + renderIcon(Codicon.editSession), + $('span', undefined, localize('featureEdits', "Build features with Copilot Edits")) + ), + $('div.chat-feature-container', undefined, + renderIcon(Codicon.commentDiscussion), + $('span', undefined, localize('featureExplore', "Explore your codebase with Chat")) + ) + ) + ) + ); + + // Limited SKU + if (this.context.state.entitlement !== ChatEntitlement.Pro && this.context.state.entitlement !== ChatEntitlement.Unavailable) { + const free = localize({ key: 'free', comment: ['{Locked="[]({0})"}'] }, "$(sparkle-filled) We now offer [Copilot for free]({0}).", defaultChat.skusDocumentationUrl); + element.appendChild($('p', undefined, disposables.add(markdown.render(new MarkdownString(free, { isTrusted: true, supportThemeIcons: true }))).element)); + } + + // Terms + const terms = localize({ key: 'terms', comment: ['{Locked="["}', '{Locked="]({0})"}', '{Locked="]({1})"}'] }, "By continuing, you agree to the [Terms]({0}) and [Privacy Policy]({1}).", defaultChat.termsStatementUrl, defaultChat.privacyStatementUrl); + element.appendChild($('p.legal', undefined, disposables.add(markdown.render(new MarkdownString(terms, { isTrusted: true }))).element)); + + // SKU Settings + if (this.telemetryService.telemetryLevel !== TelemetryLevel.NONE) { + const settings = localize({ key: 'settings', comment: ['{Locked="["}', '{Locked="]({0})"}', '{Locked="]({1})"}'] }, "Copilot Free and Pro may show [public code]({0}) suggestions and we may use your data for product improvement. You can change these [settings]({1}) at any time.", defaultChat.publicCodeMatchesUrl, defaultChat.manageSettingsUrl); + element.appendChild($('p.legal', undefined, disposables.add(markdown.render(new MarkdownString(settings, { isTrusted: true }))).element)); + } + + return element; + } +} export class ChatSetupContribution extends Disposable implements IWorkbenchContribution { @@ -118,31 +375,50 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr constructor( @IProductService private readonly productService: IProductService, @IInstantiationService private readonly instantiationService: IInstantiationService, - @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService, @ICommandService private readonly commandService: ICommandService, @ITelemetryService private readonly telemetryService: ITelemetryService, - @IWorkbenchAssignmentService private readonly experimentService: IWorkbenchAssignmentService, + @IChatEntitlementService chatEntitlementService: ChatEntitlementService, + @IConfigurationService private readonly configurationService: IConfigurationService, ) { super(); - if ( - !this.productService.defaultChatAgent || // needs product config - (isWeb && !this.environmentService.remoteAuthority) // only enabled locally or a remote backend - ) { - return; + const context = chatEntitlementService.context?.value; + const requests = chatEntitlementService.requests?.value; + if (!context || !requests) { + return; // disabled } - const context = this._register(this.instantiationService.createInstance(ChatSetupContext)); - const requests = this._register(this.instantiationService.createInstance(ChatSetupRequests, context)); const controller = new Lazy(() => this._register(this.instantiationService.createInstance(ChatSetupController, context, requests))); - this.registerChatWelcome(controller, context); - this.registerActions(context, requests); + this.registerSetupAgents(context, controller); + this.registerChatWelcome(context, controller); + this.registerActions(context, requests, controller); this.registerUrlLinkHandler(); - this.registerSetting(context); } - private registerChatWelcome(controller: Lazy, context: ChatSetupContext): void { + private registerSetupAgents(context: ChatEntitlementContext, controller: Lazy): void { + const registration = this._register(new MutableDisposable()); + + const updateRegistration = () => { + const disabled = context.state.installed || context.state.hidden || !this.configurationService.getValue('chat.experimental.setupFromDialog'); + if (!disabled && !registration.value) { + registration.value = combinedDisposable( + SetupChatAgentImplementation.register(this.instantiationService, ChatAgentLocation.Panel, false, context, controller), + SetupChatAgentImplementation.register(this.instantiationService, ChatAgentLocation.EditingSession, false, context, controller), + SetupChatAgentImplementation.register(this.instantiationService, ChatAgentLocation.EditingSession, true, context, controller) + ); + } else if (disabled && registration.value) { + registration.clear(); + } + }; + + this._register(Event.runAndSubscribe(Event.any( + context.onDidChange, + Event.filter(this.configurationService.onDidChangeConfiguration, e => e.affectsConfiguration('chat.experimental.setupFromDialog')) + ), () => updateRegistration())); + } + + private registerChatWelcome(context: ChatEntitlementContext, controller: Lazy): void { Registry.as(ChatViewsWelcomeExtensions.ChatViewsWelcomeRegistry).register({ title: localize('welcomeChat', "Welcome to Copilot"), when: ChatContextKeys.SetupViewCondition, @@ -151,18 +427,22 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr }); } - private registerActions(context: ChatSetupContext, requests: ChatSetupRequests): void { + private registerActions(context: ChatEntitlementContext, requests: ChatEntitlementRequests, controller: Lazy): void { + const chatSetupTriggerContext = ContextKeyExpr.and( + ChatContextKeys.Setup.fromDialog.negate(), // reduce noise when using the skeleton-view approach + ContextKeyExpr.or( + ChatContextKeys.Setup.installed.negate(), + ChatContextKeys.Entitlement.canSignUp + )); + + const CHAT_SETUP_ACTION_LABEL = localize2('triggerChatSetup', "Use AI Features with Copilot for Free..."); - const chatSetupTriggerContext = ContextKeyExpr.or( - ChatContextKeys.Setup.installed.negate(), - ChatContextKeys.Setup.canSignUp - ); class ChatSetupTriggerAction extends Action2 { constructor() { super({ - id: TRIGGER_SETUP_COMMAND_ID, - title: TRIGGER_SETUP_COMMAND_LABEL, + id: CHAT_SETUP_ACTION_ID, + title: CHAT_SETUP_ACTION_LABEL, category: CHAT_CATEGORY, f1: true, precondition: chatSetupTriggerContext, @@ -180,13 +460,61 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr const viewDescriptorService = accessor.get(IViewDescriptorService); const configurationService = accessor.get(IConfigurationService); const layoutService = accessor.get(IWorkbenchLayoutService); + const statusbarService = accessor.get(IStatusbarService); + const instantiationService = accessor.get(IInstantiationService); + const logService = accessor.get(ILogService); + const dialogService = accessor.get(IDialogService); + const commandService = accessor.get(ICommandService); await context.update({ hidden: false }); - showCopilotView(viewsService, layoutService); - ensureSideBarChatViewSize(viewDescriptorService, layoutService, viewsService); + const setupFromDialog = configurationService.getValue('chat.experimental.setupFromDialog'); + if (!setupFromDialog) { + showCopilotView(viewsService, layoutService); + ensureSideBarChatViewSize(viewDescriptorService, layoutService, viewsService); + } + statusbarService.updateEntryVisibility('chat.statusBarEntry', true); configurationService.updateValue('chat.commandCenter.enabled', true); + + if (setupFromDialog) { + const dialog = instantiationService.createInstance(ChatSetupDialog, context); + const result = await dialog.show(); + if (result) { + let success = false; + try { + switch (result) { + case ChatSetupDialogResult.SetupWithEnterpriseProvider: + success = await controller.value.setupWithProvider(true); + break; + case ChatSetupDialogResult.SetupWithoutEnterpriseProvider: + success = await controller.value.setupWithProvider(false); + break; + case ChatSetupDialogResult.DefaultSetup: + success = await controller.value.setup(); + break; + } + + if (success) { + showCopilotView(viewsService, layoutService); + } + } catch (error) { + logService.error(localize('setupError', "Error during setup: {0}", toErrorMessage(error))); + } + + if (!success) { + const { confirmed } = await dialogService.confirm({ + type: Severity.Error, + message: localize('setupErrorDialog', "Copilot setup failed. Would you like to try again?"), + primaryButton: localize('retry', "Retry"), + }); + + if (confirmed) { + commandService.executeCommand(CHAT_SETUP_ACTION_ID); + } + } + } + } } } @@ -201,7 +529,7 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr title: ChatSetupHideAction.TITLE, f1: true, category: CHAT_CATEGORY, - precondition: ChatContextKeys.Setup.installed.negate(), + precondition: ContextKeyExpr.and(ChatContextKeys.Setup.installed.negate(), ChatContextKeys.Setup.hidden.negate()), menu: { id: MenuId.ChatTitleBarMenu, group: 'z_hide', @@ -216,10 +544,11 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr const layoutService = accessor.get(IWorkbenchLayoutService); const configurationService = accessor.get(IConfigurationService); const dialogService = accessor.get(IDialogService); + const statusbarService = accessor.get(IStatusbarService); const { confirmed } = await dialogService.confirm({ message: localize('hideChatSetupConfirm', "Are you sure you want to hide Copilot?"), - detail: localize('hideChatSetupDetail', "You can restore Copilot by running the '{0}' command.", TRIGGER_SETUP_COMMAND_LABEL.value), + detail: localize('hideChatSetupDetail', "You can restore Copilot by running the '{0}' command.", CHAT_SETUP_ACTION_LABEL.value), primaryButton: localize('hideChatSetupButton', "Hide Copilot") }); @@ -227,8 +556,18 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr return; } - await hideSetupView(viewsDescriptorService, layoutService); + const location = viewsDescriptorService.getViewLocationById(ChatViewId); + + await context.update({ hidden: true }); + + if (location === ViewContainerLocation.AuxiliaryBar) { + const activeContainers = viewsDescriptorService.getViewContainersByLocation(location).filter(container => viewsDescriptorService.getViewContainerModel(container).activeViewDescriptors.length > 0); + if (activeContainers.length === 0) { + layoutService.setPartHidden(true, Parts.AUXILIARYBAR_PART); // hide if there are no views in the secondary sidebar + } + } + statusbarService.updateEntryVisibility('chat.statusBarEntry', false); configurationService.updateValue('chat.commandCenter.enabled', false); } } @@ -242,8 +581,8 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr category: localize2('chat.category', 'Chat'), f1: true, precondition: ContextKeyExpr.or( - ChatContextKeys.Setup.canSignUp, - ChatContextKeys.Setup.limited, + ChatContextKeys.Entitlement.canSignUp, + ChatContextKeys.Entitlement.limited, ), menu: { id: MenuId.ChatTitleBarMenu, @@ -257,506 +596,55 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr }); } - override async run(accessor: ServicesAccessor): Promise { + override async run(accessor: ServicesAccessor, from?: string): Promise { const openerService = accessor.get(IOpenerService); const telemetryService = accessor.get(ITelemetryService); const hostService = accessor.get(IHostService); const commandService = accessor.get(ICommandService); - telemetryService.publicLog2('workbenchActionExecuted', { id: this.desc.id, from: 'chat' }); + telemetryService.publicLog2('workbenchActionExecuted', { id: this.desc.id, from: from ?? 'chat' }); openerService.open(URI.parse(defaultChat.upgradePlanUrl)); - const entitlement = context.state.entitlement; - if (entitlement !== ChatEntitlement.Pro) { - // If the user is not yet Pro, we listen to window focus to refresh the token - // when the user has come back to the window assuming the user signed up. - windowFocusListener.value = hostService.onDidChangeFocus(focus => this.onWindowFocus(focus, commandService)); - } - } - - private async onWindowFocus(focus: boolean, commandService: ICommandService): Promise { - if (focus) { - windowFocusListener.clear(); - - const entitlement = await requests.forceResolveEntitlement(undefined); - if (entitlement === ChatEntitlement.Pro) { - refreshTokens(commandService); - } - } - } - } - - async function hideSetupView(viewsDescriptorService: IViewDescriptorService, layoutService: IWorkbenchLayoutService): Promise { - const location = viewsDescriptorService.getViewLocationById(ChatViewId); - - await context.update({ hidden: true }); - - if (location === ViewContainerLocation.AuxiliaryBar) { - const activeContainers = viewsDescriptorService.getViewContainersByLocation(location).filter(container => viewsDescriptorService.getViewContainerModel(container).activeViewDescriptors.length > 0); - if (activeContainers.length === 0) { - layoutService.setPartHidden(true, Parts.AUXILIARYBAR_PART); // hide if there are no views in the secondary sidebar - } - } - } - - registerAction2(ChatSetupTriggerAction); - registerAction2(ChatSetupHideAction); - registerAction2(UpgradePlanAction); - } - - private registerUrlLinkHandler(): void { - this._register(ExtensionUrlHandlerOverrideRegistry.registerHandler({ - canHandleURL: url => { - return url.scheme === this.productService.urlProtocol && equalsIgnoreCase(url.authority, defaultChat.chatExtensionId); - }, - handleURL: async url => { - const params = new URLSearchParams(url.query); - this.telemetryService.publicLog2('workbenchActionExecuted', { id: TRIGGER_SETUP_COMMAND_ID, from: 'url', detail: params.get('referrer') ?? undefined }); - - await this.commandService.executeCommand(TRIGGER_SETUP_COMMAND_ID); - - return true; - } - })); - } - - private registerSetting(context: ChatSetupContext): void { - const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); - - let lastNode: IConfigurationNode | undefined; - const registerSetting = () => { - const treatmentId = context.state.entitlement === ChatEntitlement.Limited ? - 'chatAgentMaxRequestsFree' : - 'chatAgentMaxRequestsPro'; - this.experimentService.getTreatment(treatmentId).then(value => { - const defaultValue = value ?? (context.state.entitlement === ChatEntitlement.Limited ? 5 : 15); - const node: IConfigurationNode = { - id: 'chatSidebar', - title: localize('interactiveSessionConfigurationTitle', "Chat"), - type: 'object', - properties: { - 'chat.agent.maxRequests': { - type: 'number', - markdownDescription: localize('chat.agent.maxRequests', "The maximum number of requests to allow Copilot Edits to use per-turn in agent mode. When the limit is reached, Copilot will ask the user to confirm that it should keep working. \n\n> **Note**: For users on the Copilot Free plan, note that each agent mode request currently uses one chat request."), - default: defaultValue, - tags: ['experimental'] - }, - } - }; - configurationRegistry.updateConfigurations({ remove: lastNode ? [lastNode] : [], add: [node] }); - lastNode = node; - }); - }; - this._register(Event.runAndSubscribe(Event.debounce(context.onDidChange, () => { }, 1000), () => registerSetting())); - } -} - -//#endregion - -//#region Chat Setup Request Service - -type EntitlementClassification = { - tid: { classification: 'EndUserPseudonymizedInformation'; purpose: 'BusinessInsight'; comment: 'The anonymized analytics id returned by the service'; endpoint: 'GoogleAnalyticsId' }; - entitlement: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Flag indicating the chat entitlement state' }; - quotaChat: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The number of chat completions available to the user' }; - quotaCompletions: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The number of chat completions available to the user' }; - quotaResetDate: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The date the quota will reset' }; - owner: 'bpasero'; - comment: 'Reporting chat setup entitlements'; -}; - -type EntitlementEvent = { - entitlement: ChatEntitlement; - tid: string; - quotaChat: number | undefined; - quotaCompletions: number | undefined; - quotaResetDate: string | undefined; -}; - -interface IEntitlementsResponse { - readonly access_type_sku: string; - readonly assigned_date: string; - readonly can_signup_for_limited: boolean; - readonly chat_enabled: boolean; - readonly analytics_tracking_id: string; - readonly limited_user_quotas?: { - readonly chat: number; - readonly completions: number; - }; - readonly limited_user_reset_date: string; -} - -interface IQuotas { - readonly chat?: number; - readonly completions?: number; - readonly resetDate?: string; -} - -interface IChatEntitlements { - readonly entitlement: ChatEntitlement; - readonly quotas?: IQuotas; -} - -class ChatSetupRequests extends Disposable { - - static providerId(configurationService: IConfigurationService): string { - if (configurationService.getValue(defaultChat.providerSetting) === defaultChat.enterpriseProviderId) { - return defaultChat.enterpriseProviderId; - } - - return defaultChat.providerId; - } - - private state: IChatEntitlements = { entitlement: this.context.state.entitlement }; - - private pendingResolveCts = new CancellationTokenSource(); - private didResolveEntitlements = false; - - constructor( - private readonly context: ChatSetupContext, - @ITelemetryService private readonly telemetryService: ITelemetryService, - @IAuthenticationService private readonly authenticationService: IAuthenticationService, - @ILogService private readonly logService: ILogService, - @IRequestService private readonly requestService: IRequestService, - @IChatQuotasService private readonly chatQuotasService: IChatQuotasService, - @IDialogService private readonly dialogService: IDialogService, - @IOpenerService private readonly openerService: IOpenerService, - @IConfigurationService private readonly configurationService: IConfigurationService - ) { - super(); - - this.registerListeners(); - - this.resolve(); - } - - private registerListeners(): void { - this._register(this.authenticationService.onDidChangeDeclaredProviders(() => this.resolve())); - - this._register(this.authenticationService.onDidChangeSessions(e => { - if (e.providerId === ChatSetupRequests.providerId(this.configurationService)) { - this.resolve(); - } - })); - - this._register(this.authenticationService.onDidRegisterAuthenticationProvider(e => { - if (e.id === ChatSetupRequests.providerId(this.configurationService)) { - this.resolve(); - } - })); - - this._register(this.authenticationService.onDidUnregisterAuthenticationProvider(e => { - if (e.id === ChatSetupRequests.providerId(this.configurationService)) { - this.resolve(); - } - })); - - this._register(this.context.onDidChange(() => { - if (!this.context.state.installed || this.context.state.entitlement === ChatEntitlement.Unknown) { - // When the extension is not installed or the user is not entitled - // make sure to clear quotas so that any indicators are also gone - this.state = { entitlement: this.state.entitlement, quotas: undefined }; - this.chatQuotasService.clearQuotas(); - } - })); - } - - private async resolve(): Promise { - this.pendingResolveCts.dispose(true); - const cts = this.pendingResolveCts = new CancellationTokenSource(); - - const session = await this.findMatchingProviderSession(cts.token); - if (cts.token.isCancellationRequested) { - return; - } - - // Immediately signal whether we have a session or not - let state: IChatEntitlements | undefined = undefined; - if (session) { - // Do not overwrite any state we have already - if (this.state.entitlement === ChatEntitlement.Unknown) { - state = { entitlement: ChatEntitlement.Unresolved }; - } - } else { - this.didResolveEntitlements = false; // reset so that we resolve entitlements fresh when signed in again - state = { entitlement: ChatEntitlement.Unknown }; - } - if (state) { - this.update(state); - } - - if (session && !this.didResolveEntitlements) { - // Afterwards resolve entitlement with a network request - // but only unless it was not already resolved before. - await this.resolveEntitlement(session, cts.token); - } - } - - private async findMatchingProviderSession(token: CancellationToken): Promise { - const sessions = await this.doGetSessions(ChatSetupRequests.providerId(this.configurationService)); - if (token.isCancellationRequested) { - return undefined; - } - - for (const session of sessions) { - for (const scopes of defaultChat.providerScopes) { - if (this.scopesMatch(session.scopes, scopes)) { - return session; - } - } - } - - return undefined; - } - - private async doGetSessions(providerId: string): Promise { - try { - return await this.authenticationService.getSessions(providerId); - } catch (error) { - // ignore - errors can throw if a provider is not registered - } - - return []; - } - - private scopesMatch(scopes: ReadonlyArray, expectedScopes: string[]): boolean { - return scopes.length === expectedScopes.length && expectedScopes.every(scope => scopes.includes(scope)); - } - - private async resolveEntitlement(session: AuthenticationSession, token: CancellationToken): Promise { - const entitlements = await this.doResolveEntitlement(session, token); - if (typeof entitlements?.entitlement === 'number' && !token.isCancellationRequested) { - this.didResolveEntitlements = true; - this.update(entitlements); - } - - return entitlements?.entitlement; - } - - private async doResolveEntitlement(session: AuthenticationSession, token: CancellationToken): Promise { - if (ChatSetupRequests.providerId(this.configurationService) === defaultChat.enterpriseProviderId) { - this.logService.trace('[chat setup] entitlement: enterprise provider, assuming Pro'); - return { entitlement: ChatEntitlement.Pro }; - } - - if (token.isCancellationRequested) { - return undefined; - } - - const response = await this.request(defaultChat.entitlementUrl, 'GET', undefined, session, token); - if (token.isCancellationRequested) { - return undefined; - } - - if (!response) { - this.logService.trace('[chat setup] entitlement: no response'); - return { entitlement: ChatEntitlement.Unresolved }; - } - - if (response.res.statusCode && response.res.statusCode !== 200) { - this.logService.trace(`[chat setup] entitlement: unexpected status code ${response.res.statusCode}`); - return { entitlement: ChatEntitlement.Unresolved }; - } - - let responseText: string | null = null; - try { - responseText = await asText(response); - } catch (error) { - // ignore - handled below - } - if (token.isCancellationRequested) { - return undefined; - } - - if (!responseText) { - this.logService.trace('[chat setup] entitlement: response has no content'); - return { entitlement: ChatEntitlement.Unresolved }; - } - - let entitlementsResponse: IEntitlementsResponse; - try { - entitlementsResponse = JSON.parse(responseText); - this.logService.trace(`[chat setup] entitlement: parsed result is ${JSON.stringify(entitlementsResponse)}`); - } catch (err) { - this.logService.trace(`[chat setup] entitlement: error parsing response (${err})`); - return { entitlement: ChatEntitlement.Unresolved }; - } - - let entitlement: ChatEntitlement; - if (entitlementsResponse.access_type_sku === 'free_limited_copilot') { - entitlement = ChatEntitlement.Limited; - } else if (entitlementsResponse.can_signup_for_limited) { - entitlement = ChatEntitlement.Available; - } else if (entitlementsResponse.chat_enabled) { - entitlement = ChatEntitlement.Pro; - } else { - entitlement = ChatEntitlement.Unavailable; - } - - const entitlements: IChatEntitlements = { - entitlement, - quotas: { - chat: entitlementsResponse.limited_user_quotas?.chat, - completions: entitlementsResponse.limited_user_quotas?.completions, - resetDate: entitlementsResponse.limited_user_reset_date - } - }; - - this.logService.trace(`[chat setup] entitlement: resolved to ${entitlements.entitlement}, quotas: ${JSON.stringify(entitlements.quotas)}`); - this.telemetryService.publicLog2('chatInstallEntitlement', { - entitlement: entitlements.entitlement, - tid: entitlementsResponse.analytics_tracking_id, - quotaChat: entitlementsResponse.limited_user_quotas?.chat, - quotaCompletions: entitlementsResponse.limited_user_quotas?.completions, - quotaResetDate: entitlementsResponse.limited_user_reset_date - }); - - return entitlements; - } - - private async request(url: string, type: 'GET', body: undefined, session: AuthenticationSession, token: CancellationToken): Promise; - private async request(url: string, type: 'POST', body: object, session: AuthenticationSession, token: CancellationToken): Promise; - private async request(url: string, type: 'GET' | 'POST', body: object | undefined, session: AuthenticationSession, token: CancellationToken): Promise { - try { - return await this.requestService.request({ - type, - url, - data: type === 'POST' ? JSON.stringify(body) : undefined, - disableCache: true, - headers: { - 'Authorization': `Bearer ${session.accessToken}` - } - }, token); - } catch (error) { - this.logService.error(`[chat setup] request: error ${error}`); - - return undefined; - } - } - - private update(state: IChatEntitlements): void { - this.state = state; - - this.context.update({ entitlement: this.state.entitlement }); - - if (state.quotas) { - this.chatQuotasService.acceptQuotas({ - chatQuotaExceeded: typeof state.quotas.chat === 'number' ? state.quotas.chat <= 0 : false, - completionsQuotaExceeded: typeof state.quotas.completions === 'number' ? state.quotas.completions <= 0 : false, - quotaResetDate: state.quotas.resetDate ? new Date(state.quotas.resetDate) : undefined - }); - } - } - - async forceResolveEntitlement(session: AuthenticationSession | undefined): Promise { - if (!session) { - session = await this.findMatchingProviderSession(CancellationToken.None); - } - - if (!session) { - return undefined; - } - - return this.resolveEntitlement(session, CancellationToken.None); - } - - async signUpLimited(session: AuthenticationSession): Promise { - const body = { - restricted_telemetry: this.telemetryService.telemetryLevel === TelemetryLevel.NONE ? 'disabled' : 'enabled', - public_code_suggestions: 'enabled' - }; - - const response = await this.request(defaultChat.entitlementSignupLimitedUrl, 'POST', body, session, CancellationToken.None); - if (!response) { - const retry = await this.onUnknownSignUpError(localize('signUpNoResponseError', "No response received."), '[chat setup] sign-up: no response'); - return retry ? this.signUpLimited(session) : { errorCode: 1 }; - } - - if (response.res.statusCode && response.res.statusCode !== 200) { - if (response.res.statusCode === 422) { - try { - const responseText = await asText(response); - if (responseText) { - const responseError: { message: string } = JSON.parse(responseText); - if (typeof responseError.message === 'string' && responseError.message) { - this.onUnprocessableSignUpError(`[chat setup] sign-up: unprocessable entity (${responseError.message})`, responseError.message); - return { errorCode: response.res.statusCode }; - } - } - } catch (error) { - // ignore - handled below - } - } - const retry = await this.onUnknownSignUpError(localize('signUpUnexpectedStatusError', "Unexpected status code {0}.", response.res.statusCode), `[chat setup] sign-up: unexpected status code ${response.res.statusCode}`); - return retry ? this.signUpLimited(session) : { errorCode: response.res.statusCode }; - } - - let responseText: string | null = null; - try { - responseText = await asText(response); - } catch (error) { - // ignore - handled below - } - - if (!responseText) { - const retry = await this.onUnknownSignUpError(localize('signUpNoResponseContentsError', "Response has no contents."), '[chat setup] sign-up: response has no content'); - return retry ? this.signUpLimited(session) : { errorCode: 2 }; - } - - let parsedResult: { subscribed: boolean } | undefined = undefined; - try { - parsedResult = JSON.parse(responseText); - this.logService.trace(`[chat setup] sign-up: response is ${responseText}`); - } catch (err) { - const retry = await this.onUnknownSignUpError(localize('signUpInvalidResponseError', "Invalid response contents."), `[chat setup] sign-up: error parsing response (${err})`); - return retry ? this.signUpLimited(session) : { errorCode: 3 }; - } - - // We have made it this far, so the user either did sign-up or was signed-up already. - // That is, because the endpoint throws in all other case according to Patrick. - this.update({ entitlement: ChatEntitlement.Limited }); - - return Boolean(parsedResult?.subscribed); - } - - private async onUnknownSignUpError(detail: string, logMessage: string): Promise { - this.logService.error(logMessage); - - const { confirmed } = await this.dialogService.confirm({ - type: Severity.Error, - message: localize('unknownSignUpError', "An error occurred while signing up for Copilot Free. Would you like to try again?"), - detail, - primaryButton: localize('retry', "Retry") - }); - - return confirmed; - } + const entitlement = context.state.entitlement; + if (entitlement !== ChatEntitlement.Pro) { + // If the user is not yet Pro, we listen to window focus to refresh the token + // when the user has come back to the window assuming the user signed up. + windowFocusListener.value = hostService.onDidChangeFocus(focus => this.onWindowFocus(focus, commandService)); + } + } - private onUnprocessableSignUpError(logMessage: string, logDetails: string): void { - this.logService.error(logMessage); + private async onWindowFocus(focus: boolean, commandService: ICommandService): Promise { + if (focus) { + windowFocusListener.clear(); - this.dialogService.prompt({ - type: Severity.Error, - message: localize('unprocessableSignUpError', "An error occurred while signing up for Copilot Free."), - detail: logDetails, - buttons: [ - { - label: localize('ok', "OK"), - run: () => { /* noop */ } - }, - { - label: localize('learnMore', "Learn More"), - run: () => this.openerService.open(URI.parse(defaultChat.upgradePlanUrl)) + const entitlements = await requests.forceResolveEntitlement(undefined); + if (entitlements?.entitlement === ChatEntitlement.Pro) { + refreshTokens(commandService); + } } - ] - }); + } + } + + registerAction2(ChatSetupTriggerAction); + registerAction2(ChatSetupHideAction); + registerAction2(UpgradePlanAction); } - override dispose(): void { - this.pendingResolveCts.dispose(true); + private registerUrlLinkHandler(): void { + this._register(ExtensionUrlHandlerOverrideRegistry.registerHandler({ + canHandleURL: url => { + return url.scheme === this.productService.urlProtocol && equalsIgnoreCase(url.authority, defaultChat.chatExtensionId); + }, + handleURL: async url => { + const params = new URLSearchParams(url.query); + this.telemetryService.publicLog2('workbenchActionExecuted', { id: CHAT_SETUP_ACTION_ID, from: 'url', detail: params.get('referrer') ?? undefined }); + + await this.commandService.executeCommand(CHAT_SETUP_ACTION_ID); - super.dispose(); + return true; + } + })); } } @@ -770,11 +658,13 @@ type InstallChatClassification = { installResult: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the extension was installed successfully, cancelled or failed to install.' }; installDuration: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The duration it took to install the extension.' }; signUpErrorCode: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The error code in case of an error signing up.' }; + setupFromDialog: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Whether the setup was triggered from the dialog or not.' }; }; type InstallChatEvent = { installResult: 'installed' | 'cancelled' | 'failedInstall' | 'failedNotSignedIn' | 'failedSignUp' | 'failedNotTrusted' | 'failedNoSession'; installDuration: number; signUpErrorCode: number | undefined; + setupFromDialog: boolean; }; enum ChatSetupStep { @@ -794,11 +684,10 @@ class ChatSetupController extends Disposable { private willShutdown = false; constructor( - private readonly context: ChatSetupContext, - private readonly requests: ChatSetupRequests, + private readonly context: ChatEntitlementContext, + private readonly requests: ChatEntitlementRequests, @ITelemetryService private readonly telemetryService: ITelemetryService, @IAuthenticationService private readonly authenticationService: IAuthenticationService, - @IAuthenticationExtensionsService private readonly authenticationExtensionsService: IAuthenticationExtensionsService, @IViewsService private readonly viewsService: IViewsService, @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, @IProductService private readonly productService: IProductService, @@ -812,6 +701,7 @@ class ChatSetupController extends Disposable { @IDialogService private readonly dialogService: IDialogService, @IConfigurationService private readonly configurationService: IConfigurationService, @ILifecycleService private readonly lifecycleService: ILifecycleService, + @IQuickInputService private readonly quickInputService: IQuickInputService, ) { super(); @@ -832,7 +722,7 @@ class ChatSetupController extends Disposable { this._onDidChange.fire(); } - async setup(options?: { forceSignIn: boolean }): Promise { + async setup(options?: { forceSignIn?: boolean; notificationProgress?: boolean }): Promise { const watch = new StopWatch(false); const title = localize('setupChatProgress', "Getting Copilot ready..."); const badge = this.activityService.showViewContainerActivity(preferCopilotEditsView(this.viewsService) ? CHAT_EDITING_SIDEBAR_PANEL_ID : CHAT_SIDEBAR_PANEL_ID, { @@ -840,9 +730,9 @@ class ChatSetupController extends Disposable { }); try { - await this.progressService.withProgress({ - location: ProgressLocation.Window, - command: TRIGGER_SETUP_COMMAND_ID, + return await this.progressService.withProgress({ + location: options?.notificationProgress ? ProgressLocation.Notification : ProgressLocation.Window, + command: CHAT_SETUP_ACTION_ID, title, }, () => this.doSetup(options?.forceSignIn ?? false, watch)); } finally { @@ -850,12 +740,14 @@ class ChatSetupController extends Disposable { } } - private async doSetup(forceSignIn: boolean, watch: StopWatch): Promise { + private async doSetup(forceSignIn: boolean, watch: StopWatch): Promise { this.context.suspend(); // reduces flicker let focusChatInput = false; + let success = false; try { - const providerId = ChatSetupRequests.providerId(this.configurationService); + const setupFromDialog = Boolean(this.configurationService.getValue('chat.experimental.setupFromDialog')); + const providerId = ChatEntitlementRequests.providerId(this.configurationService); let session: AuthenticationSession | undefined; let entitlement: ChatEntitlement | undefined; @@ -864,8 +756,8 @@ class ChatSetupController extends Disposable { this.setStep(ChatSetupStep.SigningIn); const result = await this.signIn(providerId); if (!result.session) { - this.telemetryService.publicLog2('commandCenter.chatInstall', { installResult: 'failedNotSignedIn', installDuration: watch.elapsed(), signUpErrorCode: undefined }); - return; + this.telemetryService.publicLog2('commandCenter.chatInstall', { installResult: 'failedNotSignedIn', installDuration: watch.elapsed(), signUpErrorCode: undefined, setupFromDialog }); + return false; } session = result.session; @@ -876,15 +768,15 @@ class ChatSetupController extends Disposable { message: localize('copilotWorkspaceTrust', "Copilot is currently only supported in trusted workspaces.") }); if (!trusted) { - this.telemetryService.publicLog2('commandCenter.chatInstall', { installResult: 'failedNotTrusted', installDuration: watch.elapsed(), signUpErrorCode: undefined }); - return; + this.telemetryService.publicLog2('commandCenter.chatInstall', { installResult: 'failedNotTrusted', installDuration: watch.elapsed(), signUpErrorCode: undefined, setupFromDialog }); + return false; } const activeElement = getActiveElement(); // Install this.setStep(ChatSetupStep.Installing); - await this.install(session, entitlement ?? this.context.state.entitlement, providerId, watch); + success = await this.install(session, entitlement ?? this.context.state.entitlement, providerId, watch); const currentActiveElement = getActiveElement(); focusChatInput = activeElement === currentActiveElement || currentActiveElement === mainWindow.document.body; @@ -896,20 +788,17 @@ class ChatSetupController extends Disposable { if (focusChatInput) { (await showCopilotView(this.viewsService, this.layoutService))?.focusInput(); } + + return success; } private async signIn(providerId: string): Promise<{ session: AuthenticationSession | undefined; entitlement: ChatEntitlement | undefined }> { let session: AuthenticationSession | undefined; - let entitlement: ChatEntitlement | undefined; + let entitlements; try { showCopilotView(this.viewsService, this.layoutService); - session = await this.authenticationService.createSession(providerId, defaultChat.providerScopes[0]); - - this.authenticationExtensionsService.updateAccountPreference(defaultChat.extensionId, providerId, session.account); - this.authenticationExtensionsService.updateAccountPreference(defaultChat.chatExtensionId, providerId, session.account); - - entitlement = await this.requests.forceResolveEntitlement(session); + ({ session, entitlements } = await this.requests.signIn()); } catch (e) { this.logService.error(`[chat setup] signIn: error ${e}`); } @@ -917,7 +806,7 @@ class ChatSetupController extends Disposable { if (!session && !this.willShutdown) { const { confirmed } = await this.dialogService.confirm({ type: Severity.Error, - message: localize('unknownSignInError', "Failed to sign in to {0}. Would you like to try again?", ChatSetupRequests.providerId(this.configurationService) === defaultChat.enterpriseProviderId ? defaultChat.enterpriseProviderName : defaultChat.providerName), + message: localize('unknownSignInError', "Failed to sign in to {0}. Would you like to try again?", ChatEntitlementRequests.providerId(this.configurationService) === defaultChat.enterpriseProviderId ? defaultChat.enterpriseProviderName : defaultChat.providerName), detail: localize('unknownSignInErrorDetail', "You must be signed in to use Copilot."), primaryButton: localize('retry', "Retry") }); @@ -927,12 +816,13 @@ class ChatSetupController extends Disposable { } } - return { session, entitlement }; + return { session, entitlement: entitlements?.entitlement }; } - private async install(session: AuthenticationSession | undefined, entitlement: ChatEntitlement, providerId: string, watch: StopWatch,): Promise { + private async install(session: AuthenticationSession | undefined, entitlement: ChatEntitlement, providerId: string, watch: StopWatch,): Promise { const wasInstalled = this.context.state.installed; let signUpResult: boolean | { errorCode: number } | undefined = undefined; + const setupFromDialog = Boolean(this.configurationService.getValue('chat.experimental.setupFromDialog')); try { showCopilotView(this.viewsService, this.layoutService); @@ -950,26 +840,26 @@ class ChatSetupController extends Disposable { } if (!session) { - this.telemetryService.publicLog2('commandCenter.chatInstall', { installResult: 'failedNoSession', installDuration: watch.elapsed(), signUpErrorCode: undefined }); - return; // unexpected + this.telemetryService.publicLog2('commandCenter.chatInstall', { installResult: 'failedNoSession', installDuration: watch.elapsed(), signUpErrorCode: undefined, setupFromDialog }); + return false; // unexpected } } signUpResult = await this.requests.signUpLimited(session); if (typeof signUpResult !== 'boolean' /* error */) { - this.telemetryService.publicLog2('commandCenter.chatInstall', { installResult: 'failedSignUp', installDuration: watch.elapsed(), signUpErrorCode: signUpResult.errorCode }); + this.telemetryService.publicLog2('commandCenter.chatInstall', { installResult: 'failedSignUp', installDuration: watch.elapsed(), signUpErrorCode: signUpResult.errorCode, setupFromDialog }); } } await this.doInstall(); } catch (error) { this.logService.error(`[chat setup] install: error ${error}`); - this.telemetryService.publicLog2('commandCenter.chatInstall', { installResult: isCancellationError(error) ? 'cancelled' : 'failedInstall', installDuration: watch.elapsed(), signUpErrorCode: undefined }); - return; + this.telemetryService.publicLog2('commandCenter.chatInstall', { installResult: isCancellationError(error) ? 'cancelled' : 'failedInstall', installDuration: watch.elapsed(), signUpErrorCode: undefined, setupFromDialog }); + return false; } - this.telemetryService.publicLog2('commandCenter.chatInstall', { installResult: 'installed', installDuration: watch.elapsed(), signUpErrorCode: undefined }); + this.telemetryService.publicLog2('commandCenter.chatInstall', { installResult: 'installed', installDuration: watch.elapsed(), signUpErrorCode: undefined, setupFromDialog }); if (wasInstalled && signUpResult === true) { refreshTokens(this.commandService); @@ -979,6 +869,8 @@ class ChatSetupController extends Disposable { timeout(5000), // helps prevent flicker with sign-in welcome view Event.toPromise(this.chatAgentService.onDidChangeAgents) // https://github.com/microsoft/vscode-copilot/issues/9274 ]); + + return true; } private async doInstall(): Promise { @@ -1013,143 +905,20 @@ class ChatSetupController extends Disposable { throw error; } } -} - -class ChatSetupWelcomeContent extends Disposable { - - readonly element = $('.chat-setup-view'); - - constructor( - private readonly controller: ChatSetupController, - private readonly context: ChatSetupContext, - @IInstantiationService private readonly instantiationService: IInstantiationService, - @IContextMenuService private readonly contextMenuService: IContextMenuService, - @IConfigurationService private readonly configurationService: IConfigurationService, - @ITelemetryService private readonly telemetryService: ITelemetryService, - @IQuickInputService private readonly quickInputService: IQuickInputService, - @IDialogService private readonly dialogService: IDialogService, - ) { - super(); - - this.create(); - } - - private create(): void { - const markdown = this.instantiationService.createInstance(MarkdownRenderer, {}); - - // Header - { - const header = localize({ key: 'header', comment: ['{Locked="[Copilot]({0})"}'] }, "[Copilot]({0}) is your AI pair programmer.", this.context.state.installed ? 'command:github.copilot.open.walkthrough' : defaultChat.documentationUrl); - this.element.appendChild($('p')).appendChild(this._register(markdown.render(new MarkdownString(header, { isTrusted: true }))).element); - - const featuresParent = this.element.appendChild($('div.chat-features-container')); - this.element.appendChild(featuresParent); - - const featuresContainer = this.element.appendChild($('div')); - featuresParent.appendChild(featuresContainer); - - const featureChatContainer = featuresContainer.appendChild($('div.chat-feature-container')); - featureChatContainer.appendChild(renderIcon(Codicon.code)); - - const featureChatLabel = featureChatContainer.appendChild($('span')); - featureChatLabel.textContent = localize('featureChat', "Code faster with Completions"); - - const featureEditsContainer = featuresContainer.appendChild($('div.chat-feature-container')); - featureEditsContainer.appendChild(renderIcon(Codicon.editSession)); - - const featureEditsLabel = featureEditsContainer.appendChild($('span')); - featureEditsLabel.textContent = localize('featureEdits', "Build features with Copilot Edits"); - - const featureExploreContainer = featuresContainer.appendChild($('div.chat-feature-container')); - featureExploreContainer.appendChild(renderIcon(Codicon.commentDiscussion)); - - const featureExploreLabel = featureExploreContainer.appendChild($('span')); - featureExploreLabel.textContent = localize('featureExplore', "Explore your codebase with Chat"); - } - - // Limited SKU - const free = localize({ key: 'free', comment: ['{Locked="[]({0})"}'] }, "$(sparkle-filled) We now offer [Copilot for free]({0}).", defaultChat.skusDocumentationUrl); - const freeContainer = this.element.appendChild($('p')); - freeContainer.appendChild(this._register(markdown.render(new MarkdownString(free, { isTrusted: true, supportThemeIcons: true }))).element); - - // Setup Button - const buttonContainer = this.element.appendChild($('p')); - buttonContainer.classList.add('button-container'); - const button = this._register(new ButtonWithDropdown(buttonContainer, { - actions: [ - toAction({ id: 'chatSetup.setupWithProvider', label: localize('setupWithProvider', "Sign in with a {0} Account", defaultChat.providerName), run: () => this.setupWithProvider(false) }), - toAction({ id: 'chatSetup.setupWithEnterpriseProvider', label: localize('setupWithEnterpriseProvider', "Sign in with a {0} Account", defaultChat.enterpriseProviderName), run: () => this.setupWithProvider(true) }) - ], - addPrimaryActionToDropdown: false, - contextMenuProvider: this.contextMenuService, - supportIcons: true, - ...defaultButtonStyles - })); - this._register(button.onDidClick(() => this.controller.setup())); - - // Terms - const terms = localize({ key: 'terms', comment: ['{Locked="["}', '{Locked="]({0})"}', '{Locked="]({1})"}'] }, "By continuing, you agree to the [Terms]({0}) and [Privacy Policy]({1}).", defaultChat.termsStatementUrl, defaultChat.privacyStatementUrl); - this.element.appendChild($('p')).appendChild(this._register(markdown.render(new MarkdownString(terms, { isTrusted: true }))).element); - - // SKU Settings - const settings = localize({ key: 'settings', comment: ['{Locked="["}', '{Locked="]({0})"}', '{Locked="]({1})"}'] }, "Copilot Free and Pro may show [public code]({0}) suggestions and we may use your data for product improvement. You can change these [settings]({1}) at any time.", defaultChat.publicCodeMatchesUrl, defaultChat.manageSettingsUrl); - const settingsContainer = this.element.appendChild($('p')); - settingsContainer.appendChild(this._register(markdown.render(new MarkdownString(settings, { isTrusted: true }))).element); - - // Update based on model state - this._register(Event.runAndSubscribe(this.controller.onDidChange, () => this.update(freeContainer, settingsContainer, button))); - } - - private update(freeContainer: HTMLElement, settingsContainer: HTMLElement, button: ButtonWithDropdown): void { - const showSettings = this.telemetryService.telemetryLevel !== TelemetryLevel.NONE; - let showFree: boolean; - let buttonLabel: string; - - switch (this.context.state.entitlement) { - case ChatEntitlement.Unknown: - showFree = true; - buttonLabel = this.context.state.registered ? localize('signUp', "Sign in to Use Copilot") : localize('signUpFree', "Sign in to Use Copilot for Free"); - break; - case ChatEntitlement.Unresolved: - showFree = true; - buttonLabel = this.context.state.registered ? localize('startUp', "Use Copilot") : localize('startUpLimited', "Use Copilot for Free"); - break; - case ChatEntitlement.Available: - case ChatEntitlement.Limited: - showFree = true; - buttonLabel = localize('startUpLimited', "Use Copilot for Free"); - break; - case ChatEntitlement.Pro: - case ChatEntitlement.Unavailable: - showFree = false; - buttonLabel = localize('startUp', "Use Copilot"); - break; - } - - switch (this.controller.step) { - case ChatSetupStep.SigningIn: - buttonLabel = localize('setupChatSignIn', "$(loading~spin) Signing in to {0}...", ChatSetupRequests.providerId(this.configurationService) === defaultChat.enterpriseProviderId ? defaultChat.enterpriseProviderName : defaultChat.providerName); - break; - case ChatSetupStep.Installing: - buttonLabel = localize('setupChatInstalling', "$(loading~spin) Getting Copilot Ready..."); - break; - } - - setVisibility(showFree, freeContainer); - setVisibility(showSettings, settingsContainer); - - button.label = buttonLabel; - button.enabled = this.controller.step === ChatSetupStep.Initial; - } - private async setupWithProvider(useEnterpriseProvider: boolean): Promise { + async setupWithProvider(useEnterpriseProvider: boolean): Promise { const registry = Registry.as(ConfigurationExtensions.Configuration); registry.registerConfiguration({ 'id': 'copilot.setup', 'type': 'object', 'properties': { - [defaultChat.providerSetting]: { - 'type': 'string' + [defaultChat.completionsAdvancedSetting]: { + 'type': 'object', + 'properties': { + 'authProvider': { + 'type': 'string' + } + } }, [defaultChat.providerUriSetting]: { 'type': 'string' @@ -1160,47 +929,62 @@ class ChatSetupWelcomeContent extends Disposable { if (useEnterpriseProvider) { const success = await this.handleEnterpriseInstance(); if (!success) { - return; // not properly configured, abort + return false; // not properly configured, abort } - await this.configurationService.updateValue(defaultChat.providerSetting, defaultChat.enterpriseProviderId, ConfigurationTarget.USER); + } + + let existingAdvancedSetting = this.configurationService.inspect(defaultChat.completionsAdvancedSetting).user?.value; + if (!isObject(existingAdvancedSetting)) { + existingAdvancedSetting = {}; + } + + if (useEnterpriseProvider) { + await this.configurationService.updateValue(`${defaultChat.completionsAdvancedSetting}`, { + ...existingAdvancedSetting, + 'authProvider': defaultChat.enterpriseProviderId + }, ConfigurationTarget.USER); } else { - await this.configurationService.updateValue(defaultChat.providerSetting, undefined, ConfigurationTarget.USER); + await this.configurationService.updateValue(`${defaultChat.completionsAdvancedSetting}`, Object.keys(existingAdvancedSetting).length > 0 ? { + ...existingAdvancedSetting, + 'authProvider': undefined + } : undefined, ConfigurationTarget.USER); await this.configurationService.updateValue(defaultChat.providerUriSetting, undefined, ConfigurationTarget.USER); } - return this.controller.setup({ forceSignIn: true }); + return this.setup({ forceSignIn: true }); } private async handleEnterpriseInstance(): Promise { + const domainRegEx = /^[a-zA-Z\-_]+$/; + const fullUriRegEx = /^(https:\/\/)?([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.ghe\.com\/?$/; + const uri = this.configurationService.getValue(defaultChat.providerUriSetting); - if (uri) { - return true; // already setup + if (typeof uri === 'string' && fullUriRegEx.test(uri)) { + return true; // already setup with a valid URI } let isSingleWord = false; const result = await this.quickInputService.input({ prompt: localize('enterpriseInstance', "What is your {0} instance?", defaultChat.enterpriseProviderName), placeHolder: localize('enterpriseInstancePlaceholder', 'i.e. "octocat" or "https://octocat.ghe.com"...'), + value: uri, validateInput: async value => { isSingleWord = false; if (!value) { return undefined; } - if (/^[a-zA-Z\-_]+$/.test(value)) { + if (domainRegEx.test(value)) { isSingleWord = true; return { content: localize('willResolveTo', "Will resolve to {0}", `https://${value}.ghe.com`), severity: Severity.Info }; - } else { - const regex = /^(https:\/\/)?([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+\.ghe\.com\/?$/; - if (!regex.test(value)) { - return { - content: localize('invalidEnterpriseInstance', 'Please enter a valid {0} instance (i.e. "octocat" or "https://octocat.ghe.com")', defaultChat.enterpriseProviderName), - severity: Severity.Error - }; - } + } if (!fullUriRegEx.test(value)) { + return { + content: localize('invalidEnterpriseInstance', 'Please enter a valid {0} instance (i.e. "octocat" or "https://octocat.ghe.com")', defaultChat.enterpriseProviderName), + severity: Severity.Error + }; } return undefined; @@ -1238,136 +1022,122 @@ class ChatSetupWelcomeContent extends Disposable { } } -//#endregion - -//#region Context - -interface IChatSetupContextState { - entitlement: ChatEntitlement; - hidden?: boolean; - installed?: boolean; - registered?: boolean; -} - -class ChatSetupContext extends Disposable { - - private static readonly CHAT_SETUP_CONTEXT_STORAGE_KEY = 'chat.setupContext'; - - private readonly canSignUpContextKey = ChatContextKeys.Setup.canSignUp.bindTo(this.contextKeyService); - private readonly signedOutContextKey = ChatContextKeys.Setup.signedOut.bindTo(this.contextKeyService); - private readonly limitedContextKey = ChatContextKeys.Setup.limited.bindTo(this.contextKeyService); - private readonly proContextKey = ChatContextKeys.Setup.pro.bindTo(this.contextKeyService); - private readonly hiddenContext = ChatContextKeys.Setup.hidden.bindTo(this.contextKeyService); - private readonly installedContext = ChatContextKeys.Setup.installed.bindTo(this.contextKeyService); - - private _state: IChatSetupContextState = this.storageService.getObject(ChatSetupContext.CHAT_SETUP_CONTEXT_STORAGE_KEY, StorageScope.PROFILE) ?? { entitlement: ChatEntitlement.Unknown }; - private suspendedState: IChatSetupContextState | undefined = undefined; - get state(): IChatSetupContextState { - return this.suspendedState ?? this._state; - } - - private readonly _onDidChange = this._register(new Emitter()); - readonly onDidChange = this._onDidChange.event; +class ChatSetupWelcomeContent extends Disposable { - private updateBarrier: Barrier | undefined = undefined; + readonly element = $('.chat-setup-view'); constructor( - @IContextKeyService private readonly contextKeyService: IContextKeyService, - @IStorageService private readonly storageService: IStorageService, - @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, - @IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService, - @ILogService private readonly logService: ILogService, - @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, + private readonly controller: ChatSetupController, + private readonly context: ChatEntitlementContext, + @IInstantiationService private readonly instantiationService: IInstantiationService, + @IContextMenuService private readonly contextMenuService: IContextMenuService, + @IConfigurationService private readonly configurationService: IConfigurationService, + @ITelemetryService private readonly telemetryService: ITelemetryService, ) { super(); - this.checkExtensionInstallation(); - this.updateContextSync(); - } - - private async checkExtensionInstallation(): Promise { - - // Await extensions to be ready to be queried - await this.extensionsWorkbenchService.queryLocal(); - - // Listen to change and process extensions once - this._register(Event.runAndSubscribe(this.extensionsWorkbenchService.onChange, e => { - if (e && !ExtensionIdentifier.equals(e.identifier.id, defaultChat.extensionId)) { - return; // unrelated event - } - - const defaultChatExtension = this.extensionsWorkbenchService.local.find(value => ExtensionIdentifier.equals(value.identifier.id, defaultChat.extensionId)); - this.update({ installed: !!defaultChatExtension?.local && this.extensionEnablementService.isEnabled(defaultChatExtension.local) }); - })); + this.create(); } - update(context: { installed: boolean }): Promise; - update(context: { hidden: boolean }): Promise; - update(context: { entitlement: ChatEntitlement }): Promise; - update(context: { installed?: boolean; hidden?: boolean; entitlement?: ChatEntitlement }): Promise { - this.logService.trace(`[chat setup] update(): ${JSON.stringify(context)}`); - - if (typeof context.installed === 'boolean') { - this._state.installed = context.installed; - - if (context.installed) { - context.hidden = false; // allows to fallback to setup view if the extension is uninstalled - } - } + private create(): void { + const markdown = this.instantiationService.createInstance(MarkdownRenderer, {}); - if (typeof context.hidden === 'boolean') { - this._state.hidden = context.hidden; + // Header + { + const header = localize({ key: 'header', comment: ['{Locked="[Copilot]({0})"}'] }, "[Copilot]({0}) is your AI pair programmer.", this.context.state.installed ? `command:${defaultChat.walkthroughCommand}` : defaultChat.documentationUrl); + this.element.appendChild($('p', undefined, this._register(markdown.render(new MarkdownString(header, { isTrusted: true }))).element)); + + this.element.appendChild( + $('div.chat-features-container', undefined, + $('div', undefined, + $('div.chat-feature-container', undefined, + renderIcon(Codicon.code), + $('span', undefined, localize('featureChat', "Code faster with Completions")) + ), + $('div.chat-feature-container', undefined, + renderIcon(Codicon.editSession), + $('span', undefined, localize('featureEdits', "Build features with Copilot Edits")) + ), + $('div.chat-feature-container', undefined, + renderIcon(Codicon.commentDiscussion), + $('span', undefined, localize('featureExplore', "Explore your codebase with Chat")) + ) + ) + ) + ); } - if (typeof context.entitlement === 'number') { - this._state.entitlement = context.entitlement; - - if (this._state.entitlement === ChatEntitlement.Limited || this._state.entitlement === ChatEntitlement.Pro) { - this._state.registered = true; // remember that the user did register to improve setup screen - } else if (this._state.entitlement === ChatEntitlement.Available) { - this._state.registered = false; // only restore when signed-in user can sign-up for limited - } - } + // Limited SKU + const free = localize({ key: 'free', comment: ['{Locked="[]({0})"}'] }, "$(sparkle-filled) We now offer [Copilot for free]({0}).", defaultChat.skusDocumentationUrl); + const freeContainer = this.element.appendChild($('p', undefined, this._register(markdown.render(new MarkdownString(free, { isTrusted: true, supportThemeIcons: true }))).element)); - this.storageService.store(ChatSetupContext.CHAT_SETUP_CONTEXT_STORAGE_KEY, this._state, StorageScope.PROFILE, StorageTarget.MACHINE); + // Setup Button + const buttonContainer = this.element.appendChild($('p')); + buttonContainer.classList.add('button-container'); + const button = this._register(new ButtonWithDropdown(buttonContainer, { + actions: [ + toAction({ id: 'chatSetup.setupWithProvider', label: localize('setupWithProvider', "Sign in with a {0} Account", defaultChat.providerName), run: () => this.controller.setupWithProvider(false) }), + toAction({ id: 'chatSetup.setupWithEnterpriseProvider', label: localize('setupWithEnterpriseProvider', "Sign in with a {0} Account", defaultChat.enterpriseProviderName), run: () => this.controller.setupWithProvider(true) }) + ], + addPrimaryActionToDropdown: false, + contextMenuProvider: this.contextMenuService, + supportIcons: true, + ...defaultButtonStyles + })); + this._register(button.onDidClick(() => this.controller.setup())); - return this.updateContext(); - } + // Terms + const terms = localize({ key: 'terms', comment: ['{Locked="["}', '{Locked="]({0})"}', '{Locked="]({1})"}'] }, "By continuing, you agree to the [Terms]({0}) and [Privacy Policy]({1}).", defaultChat.termsStatementUrl, defaultChat.privacyStatementUrl); + this.element.appendChild($('p', undefined, this._register(markdown.render(new MarkdownString(terms, { isTrusted: true }))).element)); - private async updateContext(): Promise { - await this.updateBarrier?.wait(); + // SKU Settings + const settings = localize({ key: 'settings', comment: ['{Locked="["}', '{Locked="]({0})"}', '{Locked="]({1})"}'] }, "Copilot Free and Pro may show [public code]({0}) suggestions and we may use your data for product improvement. You can change these [settings]({1}) at any time.", defaultChat.publicCodeMatchesUrl, defaultChat.manageSettingsUrl); + const settingsContainer = this.element.appendChild($('p', undefined, this._register(markdown.render(new MarkdownString(settings, { isTrusted: true }))).element)); - this.updateContextSync(); + // Update based on model state + this._register(Event.runAndSubscribe(this.controller.onDidChange, () => this.update(freeContainer, settingsContainer, button))); } - private updateContextSync(): void { - this.logService.trace(`[chat setup] updateContext(): ${JSON.stringify(this._state)}`); + private update(freeContainer: HTMLElement, settingsContainer: HTMLElement, button: ButtonWithDropdown): void { + const showSettings = this.telemetryService.telemetryLevel !== TelemetryLevel.NONE; + let showFree: boolean; + let buttonLabel: string; - if (!this._state.hidden && !this._state.installed) { - // this is ugly but fixes flicker from a previous chat install - this.storageService.remove('chat.welcomeMessageContent.panel', StorageScope.APPLICATION); - this.storageService.remove('interactive.sessions', this.workspaceContextService.getWorkspace().folders.length ? StorageScope.WORKSPACE : StorageScope.APPLICATION); + switch (this.context.state.entitlement) { + case ChatEntitlement.Unknown: + showFree = true; + buttonLabel = this.context.state.registered ? localize('signUp', "Sign in to Use Copilot") : localize('signUpFree', "Sign in to Use Copilot for Free"); + break; + case ChatEntitlement.Unresolved: + showFree = true; + buttonLabel = this.context.state.registered ? localize('startUp', "Use Copilot") : localize('startUpLimited', "Use Copilot for Free"); + break; + case ChatEntitlement.Available: + case ChatEntitlement.Limited: + showFree = true; + buttonLabel = localize('startUpLimited', "Use Copilot for Free"); + break; + case ChatEntitlement.Pro: + case ChatEntitlement.Unavailable: + showFree = false; + buttonLabel = localize('startUp', "Use Copilot"); + break; } - this.signedOutContextKey.set(this._state.entitlement === ChatEntitlement.Unknown); - this.canSignUpContextKey.set(this._state.entitlement === ChatEntitlement.Available); - this.limitedContextKey.set(this._state.entitlement === ChatEntitlement.Limited); - this.proContextKey.set(this._state.entitlement === ChatEntitlement.Pro); - this.hiddenContext.set(!!this._state.hidden); - this.installedContext.set(!!this._state.installed); - - this._onDidChange.fire(); - } + switch (this.controller.step) { + case ChatSetupStep.SigningIn: + buttonLabel = localize('setupChatSignIn', "$(loading~spin) Signing in to {0}...", ChatEntitlementRequests.providerId(this.configurationService) === defaultChat.enterpriseProviderId ? defaultChat.enterpriseProviderName : defaultChat.providerName); + break; + case ChatSetupStep.Installing: + buttonLabel = localize('setupChatInstalling', "$(loading~spin) Getting Copilot Ready..."); + break; + } - suspend(): void { - this.suspendedState = { ...this._state }; - this.updateBarrier = new Barrier(); - } + setVisibility(showFree, freeContainer); + setVisibility(showSettings, settingsContainer); - resume(): void { - this.suspendedState = undefined; - this.updateBarrier?.open(); - this.updateBarrier = undefined; + button.label = buttonLabel; + button.enabled = this.controller.step === ChatSetupStep.Initial; } } @@ -1375,6 +1145,6 @@ class ChatSetupContext extends Disposable { function refreshTokens(commandService: ICommandService): void { // ugly, but we need to signal to the extension that entitlements changed - commandService.executeCommand('github.copilot.signIn'); - commandService.executeCommand('github.copilot.refreshToken'); + commandService.executeCommand(defaultChat.completionsRefreshTokenCommand); + commandService.executeCommand(defaultChat.chatRefreshTokenCommand); } diff --git a/src/vs/workbench/contrib/chat/browser/chatStatus.ts b/src/vs/workbench/contrib/chat/browser/chatStatus.ts new file mode 100644 index 000000000000..6d1030537c48 --- /dev/null +++ b/src/vs/workbench/contrib/chat/browser/chatStatus.ts @@ -0,0 +1,482 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import './media/chatStatus.css'; +import { safeIntl } from '../../../../base/common/date.js'; +import { Disposable, DisposableStore, MutableDisposable } from '../../../../base/common/lifecycle.js'; +import { language } from '../../../../base/common/platform.js'; +import { localize } from '../../../../nls.js'; +import { IWorkbenchContribution } from '../../../common/contributions.js'; +import { IStatusbarEntry, IStatusbarEntryAccessor, IStatusbarService, ShowTooltipCommand, StatusbarAlignment, StatusbarEntryKind } from '../../../services/statusbar/browser/statusbar.js'; +import { $, addDisposableListener, append, clearNode, EventHelper, EventType } from '../../../../base/browser/dom.js'; +import { ChatEntitlement, ChatEntitlementService, ChatSentiment, IChatEntitlementService } from '../common/chatEntitlementService.js'; +import { CancellationToken } from '../../../../base/common/cancellation.js'; +import { defaultButtonStyles, defaultCheckboxStyles } from '../../../../platform/theme/browser/defaultStyles.js'; +import { Checkbox } from '../../../../base/browser/ui/toggle/toggle.js'; +import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; +import { ICommandService } from '../../../../platform/commands/common/commands.js'; +import { Lazy } from '../../../../base/common/lazy.js'; +import { contrastBorder, inputValidationErrorBorder, inputValidationInfoBorder, inputValidationWarningBorder, registerColor, transparent } from '../../../../platform/theme/common/colorRegistry.js'; +import { IHoverService } from '../../../../platform/hover/browser/hover.js'; +import { Color } from '../../../../base/common/color.js'; +import { Gesture, EventType as TouchEventType } from '../../../../base/browser/touch.js'; +import { IEditorService } from '../../../services/editor/common/editorService.js'; +import product from '../../../../platform/product/common/product.js'; +import { isObject } from '../../../../base/common/types.js'; +import { ILanguageService } from '../../../../editor/common/languages/language.js'; +import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; +import { Button } from '../../../../base/browser/ui/button/button.js'; + +//#region --- colors + +const gaugeBackground = registerColor('gauge.background', { + dark: inputValidationInfoBorder, + light: inputValidationInfoBorder, + hcDark: contrastBorder, + hcLight: contrastBorder +}, localize('gaugeBackground', "Gauge background color.")); + +registerColor('gauge.foreground', { + dark: transparent(gaugeBackground, 0.3), + light: transparent(gaugeBackground, 0.3), + hcDark: Color.white, + hcLight: Color.white +}, localize('gaugeForeground', "Gauge foreground color.")); + +registerColor('gauge.border', { + dark: null, + light: null, + hcDark: contrastBorder, + hcLight: contrastBorder +}, localize('gaugeBorder', "Gauge border color.")); + +const gaugeWarningBackground = registerColor('gauge.warningBackground', { + dark: inputValidationWarningBorder, + light: inputValidationWarningBorder, + hcDark: contrastBorder, + hcLight: contrastBorder +}, localize('gaugeWarningBackground', "Gauge warning background color.")); + +registerColor('gauge.warningForeground', { + dark: transparent(gaugeWarningBackground, 0.3), + light: transparent(gaugeWarningBackground, 0.3), + hcDark: Color.white, + hcLight: Color.white +}, localize('gaugeWarningForeground', "Gauge warning foreground color.")); + +const gaugeErrorBackground = registerColor('gauge.errorBackground', { + dark: inputValidationErrorBorder, + light: inputValidationErrorBorder, + hcDark: contrastBorder, + hcLight: contrastBorder +}, localize('gaugeErrorBackground', "Gauge error background color.")); + +registerColor('gauge.errorForeground', { + dark: transparent(gaugeErrorBackground, 0.3), + light: transparent(gaugeErrorBackground, 0.3), + hcDark: Color.white, + hcLight: Color.white +}, localize('gaugeErrorForeground', "Gauge error foreground color.")); + +//#endregion + +const defaultChat = { + extensionId: product.defaultChatAgent?.extensionId ?? '', + completionsEnablementSetting: product.defaultChatAgent?.completionsEnablementSetting ?? '', + nextEditSuggestionsSetting: product.defaultChatAgent?.nextEditSuggestionsSetting ?? '' +}; + +export class ChatStatusBarEntry extends Disposable implements IWorkbenchContribution { + + static readonly ID = 'chat.statusBarEntry'; + + private static readonly SETTING = 'chat.experimental.statusIndicator.enabled'; + + private entry: IStatusbarEntryAccessor | undefined = undefined; + + private dashboard = new Lazy(() => this.instantiationService.createInstance(ChatStatusDashboard)); + + constructor( + @IStatusbarService private readonly statusbarService: IStatusbarService, + @IChatEntitlementService private readonly chatEntitlementService: ChatEntitlementService, + @IConfigurationService private readonly configurationService: IConfigurationService, + @IInstantiationService private readonly instantiationService: IInstantiationService + ) { + super(); + + this.create(); + this.registerListeners(); + } + + private async create(): Promise { + const hidden = this.chatEntitlementService.sentiment === ChatSentiment.Disabled; + const disabled = this.configurationService.getValue(ChatStatusBarEntry.SETTING) === false; + + if (!hidden && !disabled) { + this.entry ||= this.statusbarService.addEntry(this.getEntryProps(), ChatStatusBarEntry.ID, StatusbarAlignment.RIGHT, { location: { id: 'status.editor.mode', priority: 100.1 }, alignment: StatusbarAlignment.RIGHT }); + + // TODO@bpasero: remove this eventually + const completionsStatusId = `${defaultChat.extensionId}.status`; + this.statusbarService.updateEntryVisibility(completionsStatusId, false); + this.statusbarService.overrideEntry(completionsStatusId, { name: localize('codeCompletionsStatus', "Copilot Code Completions"), text: localize('codeCompletionsStatusText', "$(copilot) Completions") }); + } else { + this.entry?.dispose(); + this.entry = undefined; + } + } + + private registerListeners(): void { + this._register(this.configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(ChatStatusBarEntry.SETTING)) { + this.create(); + } + })); + + this._register(this.chatEntitlementService.onDidChangeQuotaExceeded(() => this.entry?.update(this.getEntryProps()))); + this._register(this.chatEntitlementService.onDidChangeSentiment(() => this.entry?.update(this.getEntryProps()))); + this._register(this.chatEntitlementService.onDidChangeEntitlement(() => this.entry?.update(this.getEntryProps()))); + } + + private getEntryProps(): IStatusbarEntry { + let text = '$(copilot)'; + let ariaLabel = localize('chatStatus', "Copilot Status"); + let kind: StatusbarEntryKind | undefined; + + if (!isNewUser(this.chatEntitlementService)) { + const { chatQuotaExceeded, completionsQuotaExceeded } = this.chatEntitlementService.quotas; + + // Signed out + if (this.chatEntitlementService.entitlement === ChatEntitlement.Unknown) { + const signedOutWarning = localize('notSignedIntoCopilot', "Signed out"); + + text = `$(copilot-not-connected) ${signedOutWarning}`; + ariaLabel = signedOutWarning; + kind = 'prominent'; + } + + // Quota Exceeded + else if (chatQuotaExceeded || completionsQuotaExceeded) { + let quotaWarning: string; + if (chatQuotaExceeded && !completionsQuotaExceeded) { + quotaWarning = localize('chatQuotaExceededStatus', "Chat limit reached"); + } else if (completionsQuotaExceeded && !chatQuotaExceeded) { + quotaWarning = localize('completionsQuotaExceededStatus', "Completions limit reached"); + } else { + quotaWarning = localize('chatAndCompletionsQuotaExceededStatus', "Limit reached"); + } + + text = `$(copilot-warning) ${quotaWarning}`; + ariaLabel = quotaWarning; + kind = 'prominent'; + } + } + + return { + name: localize('chatStatus', "Copilot Status"), + text, + ariaLabel, + command: ShowTooltipCommand, + showInAllWindows: true, + kind, + tooltip: { element: token => this.dashboard.value.show(token) } + }; + } + + override dispose(): void { + super.dispose(); + + this.entry?.dispose(); + this.entry = undefined; + } +} + +function isNewUser(chatEntitlementService: IChatEntitlementService): boolean { + return chatEntitlementService.sentiment !== ChatSentiment.Installed || // copilot not installed + chatEntitlementService.entitlement === ChatEntitlement.Available; // not yet signed up to copilot +} + +function canUseCopilot(chatEntitlementService: IChatEntitlementService): boolean { + const newUser = isNewUser(chatEntitlementService); + const signedOut = chatEntitlementService.entitlement === ChatEntitlement.Unknown; + const allQuotaReached = chatEntitlementService.quotas.chatQuotaExceeded && chatEntitlementService.quotas.completionsQuotaExceeded; + + return !newUser && !signedOut && !allQuotaReached; +} + +interface ISettingsAccessor { + readSetting: () => boolean; + writeSetting: (value: boolean) => Promise; +} + +class ChatStatusDashboard extends Disposable { + + private readonly element = $('div.chat-status-bar-entry-tooltip'); + + private dateFormatter = new Lazy(() => safeIntl.DateTimeFormat(language, { year: 'numeric', month: 'long', day: 'numeric' })); + private readonly entryDisposables = this._register(new MutableDisposable()); + + constructor( + @IChatEntitlementService private readonly chatEntitlementService: ChatEntitlementService, + @IConfigurationService private readonly configurationService: IConfigurationService, + @IHoverService private readonly hoverService: IHoverService, + @IEditorService private readonly editorService: IEditorService, + @ILanguageService private readonly languageService: ILanguageService, + @ICommandService private readonly commandService: ICommandService + ) { + super(); + } + + show(token: CancellationToken): HTMLElement { + clearNode(this.element); + + const disposables = this.entryDisposables.value = new DisposableStore(); + disposables.add(token.onCancellationRequested(() => disposables.dispose())); + + let needsSeparator = false; + const addSeparator = (label: string | undefined) => { + if (needsSeparator) { + this.element.appendChild($('hr')); + needsSeparator = false; + } + + if (label) { + this.element.appendChild($('div.header', undefined, label)); + } + + needsSeparator = true; + }; + + // Quota Indicator + if (this.chatEntitlementService.entitlement === ChatEntitlement.Limited) { + const { chatTotal, chatRemaining, completionsTotal, completionsRemaining, quotaResetDate, chatQuotaExceeded, completionsQuotaExceeded } = this.chatEntitlementService.quotas; + + addSeparator(localize('usageTitle', "Copilot Free Usage")); + + const chatQuotaIndicator = this.createQuotaIndicator(this.element, chatTotal, chatRemaining, localize('chatsLabel', "Chat messages")); + const completionsQuotaIndicator = this.createQuotaIndicator(this.element, completionsTotal, completionsRemaining, localize('completionsLabel', "Code completions")); + + this.element.appendChild($('div.description', undefined, localize('limitQuota', "Limits will reset on {0}.", this.dateFormatter.value.format(quotaResetDate)))); + + if (chatQuotaExceeded || completionsQuotaExceeded) { + const upgradePlanButton = disposables.add(new Button(this.element, { ...defaultButtonStyles, secondary: canUseCopilot(this.chatEntitlementService) /* use secondary color when copilot can still be used */ })); + upgradePlanButton.label = localize('upgradeToCopilotPro', "Upgrade to Copilot Pro"); + disposables.add(upgradePlanButton.onDidClick(() => this.runCommandAndClose({ id: 'workbench.action.chat.upgradePlan', args: ['chat-status'] }))); + } + + (async () => { + await this.chatEntitlementService.update(token); + if (token.isCancellationRequested) { + return; + } + + const { chatTotal, chatRemaining, completionsTotal, completionsRemaining } = this.chatEntitlementService.quotas; + + chatQuotaIndicator(chatTotal, chatRemaining); + completionsQuotaIndicator(completionsTotal, completionsRemaining); + })(); + } + + // Settings + { + addSeparator(localize('settingsTitle', "Settings")); + + this.createSettings(this.element, disposables); + } + + // New to Copilot / Signed out + { + const newUser = isNewUser(this.chatEntitlementService); + const signedOut = this.chatEntitlementService.entitlement === ChatEntitlement.Unknown; + if (newUser || signedOut) { + addSeparator(undefined); + + this.element.appendChild($('div.description', undefined, newUser ? localize('activateDescription', "You need to set up Copilot.") : localize('signInDescription', "You need to sign in to use Copilot."))); + + const button = disposables.add(new Button(this.element, { ...defaultButtonStyles })); + button.label = newUser ? localize('activateCopilotButton', "Set Up Copilot") : localize('signInToUseCopilotButton', "Sign In"); + disposables.add(button.onDidClick(() => this.runCommandAndClose(newUser ? { id: 'workbench.action.chat.triggerSetup' } : () => this.chatEntitlementService.requests?.value.signIn()))); + } + } + + return this.element; + } + + private runCommandAndClose(command: { id: string; args?: unknown[] } | Function): void { + if (typeof command === 'function') { + command(); + } else { + this.commandService.executeCommand(command.id, ...(command.args ?? [])); + } + this.hoverService.hideHover(true); + } + + private createQuotaIndicator(container: HTMLElement, total: number | undefined, remaining: number | undefined, label: string): (total: number | undefined, remaining: number | undefined) => void { + const quotaText = $('span'); + const quotaBit = $('div.quota-bit'); + + const quotaIndicator = container.appendChild($('div.quota-indicator', undefined, + $('div.quota-label', undefined, + $('span', undefined, label), + quotaText + ), + $('div.quota-bar', undefined, + quotaBit + ) + )); + + const update = (total: number | undefined, remaining: number | undefined) => { + quotaIndicator.classList.remove('error'); + quotaIndicator.classList.remove('warning'); + + if (typeof total === 'number' && typeof remaining === 'number') { + let usedPercentage = Math.round(((total - remaining) / total) * 100); + if (total !== remaining && usedPercentage === 0) { + usedPercentage = 1; // indicate minimal usage as 1% + } + + quotaText.textContent = localize('quotaDisplay', "{0}%", usedPercentage); + quotaBit.style.width = `${usedPercentage}%`; + + if (usedPercentage >= 90) { + quotaIndicator.classList.add('error'); + } else if (usedPercentage >= 75) { + quotaIndicator.classList.add('warning'); + } + } + }; + + update(total, remaining); + + return update; + } + + private createSettings(container: HTMLElement, disposables: DisposableStore): HTMLElement { + const modeId = this.editorService.activeTextEditorLanguageId; + const settings = container.appendChild($('div.settings')); + + // --- Code Completions + { + const globalSetting = append(settings, $('div.setting')); + this.createCodeCompletionsSetting(globalSetting, localize('settings.codeCompletions', "Code Completions (all files)"), '*', disposables); + + if (modeId) { + const languageSetting = append(settings, $('div.setting')); + this.createCodeCompletionsSetting(languageSetting, localize('settings.codeCompletionsLanguage', "Code Completions ({0})", this.languageService.getLanguageName(modeId) ?? modeId), modeId, disposables); + } + } + + // --- Next Edit Suggestions + { + const setting = append(settings, $('div.setting')); + this.createNextEditSuggestionsSetting(setting, localize('settings.nextEditSuggestions', "Next Edit Suggestions"), modeId, this.getCompletionsSettingAccessor(modeId), disposables); + } + + return settings; + } + + private createSetting(container: HTMLElement, settingId: string, label: string, accessor: ISettingsAccessor, disposables: DisposableStore): Checkbox { + const checkbox = disposables.add(new Checkbox(label, Boolean(accessor.readSetting()), defaultCheckboxStyles)); + container.appendChild(checkbox.domNode); + + const settingLabel = append(container, $('span.setting-label', undefined, label)); + disposables.add(Gesture.addTarget(settingLabel)); + [EventType.CLICK, TouchEventType.Tap].forEach(eventType => { + disposables.add(addDisposableListener(settingLabel, eventType, e => { + if (checkbox?.enabled) { + EventHelper.stop(e, true); + + checkbox.checked = !checkbox.checked; + accessor.writeSetting(checkbox.checked); + checkbox.focus(); + } + })); + }); + + disposables.add(checkbox.onChange(() => { + accessor.writeSetting(checkbox.checked); + })); + + disposables.add(this.configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(settingId)) { + checkbox.checked = Boolean(accessor.readSetting()); + } + })); + + if (!canUseCopilot(this.chatEntitlementService)) { + container.classList.add('disabled'); + checkbox.disable(); + } + + return checkbox; + } + + private createCodeCompletionsSetting(container: HTMLElement, label: string, modeId: string | undefined, disposables: DisposableStore): void { + this.createSetting(container, defaultChat.completionsEnablementSetting, label, this.getCompletionsSettingAccessor(modeId), disposables); + } + + private getCompletionsSettingAccessor(modeId = '*'): ISettingsAccessor { + const settingId = defaultChat.completionsEnablementSetting; + + return { + readSetting: () => { + const result = this.configurationService.getValue>(settingId); + if (!isObject(result)) { + return false; + } + + if (typeof result[modeId] !== 'undefined') { + return Boolean(result[modeId]); // go with setting if explicitly defined + } + + return Boolean(result['*']); // fallback to global setting otherwise + }, + writeSetting: (value: boolean) => { + let result = this.configurationService.getValue>(settingId); + if (!isObject(result)) { + result = Object.create(null); + } + + return this.configurationService.updateValue(settingId, { ...result, [modeId]: value }); + } + }; + } + + private createNextEditSuggestionsSetting(container: HTMLElement, label: string, modeId: string | undefined, completionsSettingAccessor: ISettingsAccessor, disposables: DisposableStore): void { + const nesSettingId = defaultChat.nextEditSuggestionsSetting; + const completionsSettingId = defaultChat.completionsEnablementSetting; + + const checkbox = this.createSetting(container, nesSettingId, label, { + readSetting: () => this.configurationService.getValue(nesSettingId, { overrideIdentifier: modeId }), + writeSetting: (value: boolean) => { + const { overrideIdentifiers } = this.configurationService.inspect(nesSettingId, { overrideIdentifier: modeId }); + if (modeId && overrideIdentifiers?.includes(modeId)) { + return this.configurationService.updateValue(nesSettingId, value, { overrideIdentifier: modeId }); + } + + return this.configurationService.updateValue(nesSettingId, value); + } + }, disposables); + + // enablement of NES depends on completions setting + // so we have to update our checkbox state accordingly + + if (!completionsSettingAccessor.readSetting()) { + container.classList.add('disabled'); + checkbox.disable(); + } + + disposables.add(this.configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(completionsSettingId)) { + if (completionsSettingAccessor.readSetting() && canUseCopilot(this.chatEntitlementService)) { + checkbox.enable(); + container.classList.remove('disabled'); + } else { + checkbox.disable(); + container.classList.add('disabled'); + } + } + })); + } +} diff --git a/src/vs/workbench/contrib/chat/browser/chatVariables.ts b/src/vs/workbench/contrib/chat/browser/chatVariables.ts index dde6ec6e59f7..81e667f8d37e 100644 --- a/src/vs/workbench/contrib/chat/browser/chatVariables.ts +++ b/src/vs/workbench/contrib/chat/browser/chatVariables.ts @@ -4,14 +4,14 @@ *--------------------------------------------------------------------------------------------*/ import { coalesce } from '../../../../base/common/arrays.js'; -import { ThemeIcon } from '../../../../base/common/themables.js'; import { URI } from '../../../../base/common/uri.js'; import { Location } from '../../../../editor/common/languages.js'; +import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { IViewsService } from '../../../services/views/common/viewsService.js'; -import { ChatAgentLocation } from '../common/chatAgents.js'; import { IChatRequestVariableData, IChatRequestVariableEntry } from '../common/chatModel.js'; import { ChatRequestDynamicVariablePart, ChatRequestToolPart, IParsedChatRequest } from '../common/chatParserTypes.js'; import { IChatVariablesService, IDynamicVariable } from '../common/chatVariables.js'; +import { ChatAgentLocation, ChatConfiguration } from '../common/constants.js'; import { IChatWidgetService, showChatView, showEditsView } from './chat.js'; import { ChatDynamicVariableModel } from './contrib/chatDynamicVariables.js'; @@ -21,6 +21,7 @@ export class ChatVariablesService implements IChatVariablesService { constructor( @IChatWidgetService private readonly chatWidgetService: IChatWidgetService, @IViewsService private readonly viewsService: IViewsService, + @IConfigurationService private readonly configurationService: IConfigurationService, ) { } @@ -29,10 +30,8 @@ export class ChatVariablesService implements IChatVariablesService { prompt.parts .forEach((part, i) => { - if (part instanceof ChatRequestDynamicVariablePart) { - resolvedVariables[i] = { id: part.id, name: part.referenceText, range: part.range, value: part.data, fullName: part.fullName, icon: part.icon, isFile: part.isFile }; - } else if (part instanceof ChatRequestToolPart) { - resolvedVariables[i] = { id: part.toolId, name: part.toolName, range: part.range, value: undefined, isTool: true, icon: ThemeIcon.isThemeIcon(part.icon) ? part.icon : undefined, fullName: part.displayName }; + if (part instanceof ChatRequestDynamicVariablePart || part instanceof ChatRequestToolPart) { + resolvedVariables[i] = part.toVariableEntry(); } }); @@ -76,7 +75,8 @@ export class ChatVariablesService implements IChatVariablesService { return; } - const widget = location === ChatAgentLocation.EditingSession + const unifiedViewEnabled = !!this.configurationService.getValue(ChatConfiguration.UnifiedChatView); + const widget = location === ChatAgentLocation.EditingSession && !unifiedViewEnabled ? await showEditsView(this.viewsService) : (this.chatWidgetService.lastFocusedWidget ?? await showChatView(this.viewsService)); if (!widget || !widget.viewModel) { @@ -90,5 +90,10 @@ export class ChatVariablesService implements IChatVariablesService { widget.attachmentModel.addFile(uri, range); return; } + + if (key === 'folder' && URI.isUri(value)) { + widget.attachmentModel.addFolder(value); + return; + } } } diff --git a/src/vs/workbench/contrib/chat/browser/chatViewPane.ts b/src/vs/workbench/contrib/chat/browser/chatViewPane.ts index a7ad1507eb6c..3cf5196fdd41 100644 --- a/src/vs/workbench/contrib/chat/browser/chatViewPane.ts +++ b/src/vs/workbench/contrib/chat/browser/chatViewPane.ts @@ -18,7 +18,6 @@ import { ILayoutService } from '../../../../platform/layout/browser/layoutServic import { ILogService } from '../../../../platform/log/common/log.js'; import { IOpenerService } from '../../../../platform/opener/common/opener.js'; import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; -import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; import { editorBackground } from '../../../../platform/theme/common/colorRegistry.js'; import { IThemeService } from '../../../../platform/theme/common/themeService.js'; import { IViewPaneOptions, ViewPane } from '../../../browser/parts/views/viewPane.js'; @@ -26,11 +25,12 @@ import { Memento } from '../../../common/memento.js'; import { SIDE_BAR_FOREGROUND } from '../../../common/theme.js'; import { IViewDescriptorService } from '../../../common/views.js'; import { IChatViewTitleActionContext } from '../common/chatActions.js'; -import { ChatAgentLocation, IChatAgentService } from '../common/chatAgents.js'; +import { IChatAgentService } from '../common/chatAgents.js'; import { ChatContextKeys } from '../common/chatContextKeys.js'; import { ChatModelInitState, IChatModel } from '../common/chatModel.js'; import { CHAT_PROVIDER_ID } from '../common/chatParticipantContribTypes.js'; import { IChatService } from '../common/chatService.js'; +import { ChatAgentLocation, ChatMode } from '../common/constants.js'; import { ChatWidget, IChatViewState } from './chatWidget.js'; import { ChatViewWelcomeController, IViewWelcomeDelegate } from './viewsWelcome/chatViewWelcomeController.js'; @@ -62,7 +62,6 @@ export class ChatViewPane extends ViewPane implements IViewWelcomeDelegate { @IInstantiationService instantiationService: IInstantiationService, @IOpenerService openerService: IOpenerService, @IThemeService themeService: IThemeService, - @ITelemetryService telemetryService: ITelemetryService, @IHoverService hoverService: IHoverService, @IStorageService private readonly storageService: IStorageService, @IChatService private readonly chatService: IChatService, @@ -70,7 +69,7 @@ export class ChatViewPane extends ViewPane implements IViewWelcomeDelegate { @ILogService private readonly logService: ILogService, @ILayoutService private readonly layoutService: ILayoutService, ) { - super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService, hoverService); + super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, hoverService); // View state for the ViewPane is currently global per-provider basically, but some other strictly per-model state will require a separate memento. this.memento = new Memento('interactive-session-view-' + CHAT_PROVIDER_ID + (this.chatOptions.location === ChatAgentLocation.EditingSession ? `-edits` : ''), this.storageService); @@ -78,8 +77,8 @@ export class ChatViewPane extends ViewPane implements IViewWelcomeDelegate { this._register(this.chatAgentService.onDidChangeAgents(() => { if (this.chatAgentService.getDefaultAgent(this.chatOptions?.location)) { if (!this._widget?.viewModel) { - const sessionId = this.getSessionId(); - const model = sessionId ? this.chatService.getOrRestoreSession(sessionId) : undefined; + const info = this.getTransferredOrPersistedSessionInfo(); + const model = info.sessionId ? this.chatService.getOrRestoreSession(info.sessionId) : undefined; // The widget may be hidden at this point, because welcome views were allowed. Use setVisible to // avoid doing a render while the widget is hidden. This is changing the condition in `shouldShowWelcome` @@ -87,7 +86,7 @@ export class ChatViewPane extends ViewPane implements IViewWelcomeDelegate { const wasVisible = this._widget.visible; try { this._widget.setVisible(false); - this.updateModel(model); + this.updateModel(model, info.inputValue || info.mode ? { inputState: { chatMode: info.mode }, inputValue: info.inputValue } : undefined); this.defaultParticipantRegistrationFailed = false; this.didUnregisterProvider = false; this._onDidChangeViewWelcomeState.fire(); @@ -120,7 +119,7 @@ export class ChatViewPane extends ViewPane implements IViewWelcomeDelegate { private updateModel(model?: IChatModel | undefined, viewState?: IChatViewState): void { this.modelDisposables.clear(); - model = model ?? (this.chatService.transferredSessionData?.sessionId + model = model ?? (this.chatService.transferredSessionData?.sessionId && this.chatService.transferredSessionData?.location === this.chatOptions.location ? this.chatService.getOrRestoreSession(this.chatService.transferredSessionData.sessionId) : this.chatService.startSession(this.chatOptions.location, CancellationToken.None)); if (!model) { @@ -146,15 +145,17 @@ export class ChatViewPane extends ViewPane implements IViewWelcomeDelegate { return !!shouldShow; } - private getSessionId() { - let sessionId: string | undefined; - if (this.chatService.transferredSessionData) { - sessionId = this.chatService.transferredSessionData.sessionId; - this.viewState.inputValue = this.chatService.transferredSessionData.inputValue; + private getTransferredOrPersistedSessionInfo(): { sessionId?: string; inputValue?: string; mode?: ChatMode } { + if (this.chatService.transferredSessionData?.location === this.chatOptions.location) { + const sessionId = this.chatService.transferredSessionData.sessionId; + return { + sessionId, + inputValue: this.chatService.transferredSessionData.inputValue, + mode: this.chatService.transferredSessionData.mode + }; } else { - sessionId = this.viewState.sessionId; + return { sessionId: this.viewState.sessionId }; } - return sessionId; } protected override renderBody(parent: HTMLElement): void { @@ -173,20 +174,22 @@ export class ChatViewPane extends ViewPane implements IViewWelcomeDelegate { this.chatOptions.location, { viewId: this.id }, { - autoScroll: this.chatOptions.location === ChatAgentLocation.EditingSession, + autoScroll: mode => mode !== ChatMode.Chat, renderFollowups: this.chatOptions.location === ChatAgentLocation.Panel, supportsFileReferences: true, supportsAdditionalParticipants: this.chatOptions.location === ChatAgentLocation.Panel, rendererOptions: { - renderCodeBlockPills: this.chatOptions.location === ChatAgentLocation.EditingSession, + renderCodeBlockPills: mode => mode !== ChatMode.Chat, renderTextEditsAsSummary: (uri) => { - return this.chatOptions.location === ChatAgentLocation.EditingSession; + return this.chatService.isEditingLocation(this.chatOptions.location); }, - referencesExpandedWhenEmptyResponse: this.chatOptions.location !== ChatAgentLocation.EditingSession, - progressMessageAtBottomOfResponse: this.chatOptions.location === ChatAgentLocation.EditingSession, + referencesExpandedWhenEmptyResponse: !this.chatService.isEditingLocation(this.chatOptions.location), + progressMessageAtBottomOfResponse: this.chatService.isEditingLocation(this.chatOptions.location), }, editorOverflowWidgetsDomNode: editorOverflowNode, - enableWorkingSet: this.chatOptions.location === ChatAgentLocation.EditingSession ? 'explicit' : undefined + enableImplicitContext: this.chatOptions.location === ChatAgentLocation.Panel || this.chatService.isEditingLocation(this.chatOptions.location), + enableWorkingSet: this.chatService.isEditingLocation(this.chatOptions.location) ? 'explicit' : undefined, + supportsChangingModes: this.chatService.isEditingLocation(this.chatOptions.location), }, { listForeground: SIDE_BAR_FOREGROUND, @@ -202,7 +205,7 @@ export class ChatViewPane extends ViewPane implements IViewWelcomeDelegate { this._register(this._widget.onDidClear(() => this.clear())); this._widget.render(parent); - const sessionId = this.getSessionId(); + const info = this.getTransferredOrPersistedSessionInfo(); const disposeListener = this._register(this.chatService.onDidDisposeSession((e) => { // Render the welcome view if provider registration fails, eg when signed out. This activates for any session, but the problem is the same regardless if (e.reason === 'initializationFailed') { @@ -211,9 +214,9 @@ export class ChatViewPane extends ViewPane implements IViewWelcomeDelegate { this._onDidChangeViewWelcomeState.fire(); } })); - const model = sessionId ? this.chatService.getOrRestoreSession(sessionId) : undefined; + const model = info.sessionId ? this.chatService.getOrRestoreSession(info.sessionId) : undefined; - this.updateModel(model); + this.updateModel(model, info.inputValue || info.mode ? { inputState: { chatMode: info.mode }, inputValue: info.inputValue } : undefined); } catch (e) { this.logService.error(e); throw e; diff --git a/src/vs/workbench/contrib/chat/browser/chatWidget.ts b/src/vs/workbench/contrib/chat/browser/chatWidget.ts index f2474822d75f..7828304eb924 100644 --- a/src/vs/workbench/contrib/chat/browser/chatWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/chatWidget.ts @@ -8,11 +8,12 @@ import { Button } from '../../../../base/browser/ui/button/button.js'; import { ITreeContextMenuEvent, ITreeElement } from '../../../../base/browser/ui/tree/tree.js'; import { disposableTimeout, timeout } from '../../../../base/common/async.js'; import { Codicon } from '../../../../base/common/codicons.js'; +import { memoize } from '../../../../base/common/decorators.js'; import { toErrorMessage } from '../../../../base/common/errorMessage.js'; import { Emitter, Event } from '../../../../base/common/event.js'; import { FuzzyScore } from '../../../../base/common/filters.js'; import { MarkdownString } from '../../../../base/common/htmlContent.js'; -import { Disposable, DisposableStore, IDisposable, MutableDisposable, combinedDisposable, toDisposable } from '../../../../base/common/lifecycle.js'; +import { combinedDisposable, Disposable, DisposableStore, IDisposable, MutableDisposable, toDisposable } from '../../../../base/common/lifecycle.js'; import { ResourceSet } from '../../../../base/common/map.js'; import { Schemas } from '../../../../base/common/network.js'; import { autorunWithStore, observableFromEvent, observableValue } from '../../../../base/common/observable.js'; @@ -23,6 +24,7 @@ import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js'; import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js'; import { localize } from '../../../../nls.js'; import { MenuId } from '../../../../platform/actions/common/actions.js'; +import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { IContextKey, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js'; import { ITextResourceEditorInput } from '../../../../platform/editor/common/editor.js'; @@ -31,23 +33,24 @@ import { ServiceCollection } from '../../../../platform/instantiation/common/ser import { WorkbenchObjectTree } from '../../../../platform/list/browser/listService.js'; import { ILogService } from '../../../../platform/log/common/log.js'; import { bindContextKey } from '../../../../platform/observable/common/platformObservableUtils.js'; -import { IProductService } from '../../../../platform/product/common/productService.js'; import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; import { buttonSecondaryBackground, buttonSecondaryForeground, buttonSecondaryHoverBackground } from '../../../../platform/theme/common/colorRegistry.js'; import { asCssVariable } from '../../../../platform/theme/common/colorUtils.js'; import { IThemeService } from '../../../../platform/theme/common/themeService.js'; -import { ChatAgentLocation, IChatAgentCommand, IChatAgentData, IChatAgentService, IChatWelcomeMessageContent, isChatWelcomeMessageContent } from '../common/chatAgents.js'; +import { checkModeOption } from '../common/chat.js'; +import { IChatAgentCommand, IChatAgentData, IChatAgentService, IChatWelcomeMessageContent, isChatWelcomeMessageContent } from '../common/chatAgents.js'; import { ChatContextKeys } from '../common/chatContextKeys.js'; -import { applyingChatEditsFailedContextKey, decidedChatEditingResourceContextKey, hasAppliedChatEditsContextKey, hasUndecidedChatEditingResourceContextKey, IChatEditingService, IChatEditingSession, inChatEditingSessionContextKey, WorkingSetEntryRemovalReason, WorkingSetEntryState } from '../common/chatEditingService.js'; +import { applyingChatEditsFailedContextKey, decidedChatEditingResourceContextKey, hasAppliedChatEditsContextKey, hasUndecidedChatEditingResourceContextKey, IChatEditingService, IChatEditingSession, inChatEditingSessionContextKey, WorkingSetEntryState } from '../common/chatEditingService.js'; import { ChatPauseState, IChatModel, IChatRequestVariableEntry, IChatResponseModel } from '../common/chatModel.js'; -import { ChatRequestAgentPart, IParsedChatRequest, chatAgentLeader, chatSubcommandLeader, formatChatQuestion } from '../common/chatParserTypes.js'; +import { chatAgentLeader, ChatRequestAgentPart, chatSubcommandLeader, formatChatQuestion, IParsedChatRequest } from '../common/chatParserTypes.js'; import { ChatRequestParser } from '../common/chatRequestParser.js'; import { IChatFollowup, IChatLocationData, IChatSendRequestOptions, IChatService } from '../common/chatService.js'; import { IChatSlashCommandService } from '../common/chatSlashCommands.js'; import { ChatViewModel, IChatResponseViewModel, isRequestVM, isResponseVM } from '../common/chatViewModel.js'; import { IChatInputState } from '../common/chatWidgetHistoryService.js'; import { CodeBlockModelCollection } from '../common/codeBlockModelCollection.js'; +import { ChatAgentLocation, ChatConfiguration, ChatMode } from '../common/constants.js'; import { ChatTreeItem, IChatAcceptInputOptions, IChatAccessibilityService, IChatCodeBlockInfo, IChatFileTreeInfo, IChatListItemRendererOptions, IChatWidget, IChatWidgetService, IChatWidgetViewContext, IChatWidgetViewOptions } from './chat.js'; import { ChatAccessibilityProvider } from './chatAccessibilityProvider.js'; import { ChatAttachmentModel } from './chatAttachmentModel.js'; @@ -149,6 +152,7 @@ export class ChatWidget extends Disposable implements IChatWidget { private container!: HTMLElement; private welcomeMessageContainer!: HTMLElement; private persistedWelcomeMessage: IChatWelcomeMessageContent | undefined; + private readonly welcomePart: MutableDisposable = this._register(new MutableDisposable()); private bodyDimension: dom.Dimension | undefined; private visibleChangeCount = 0; @@ -157,6 +161,7 @@ export class ChatWidget extends Disposable implements IChatWidget { private canRequestBePaused: IContextKey; private agentInInput: IContextKey; + private _visible = false; public get visible() { return this._visible; @@ -200,7 +205,7 @@ export class ChatWidget extends Disposable implements IChatWidget { return { text: '', parts: [] }; } - this.parsedChatRequest = this.instantiationService.createInstance(ChatRequestParser).parseChatRequest(this.viewModel!.sessionId, this.getInput(), this.location, { selectedAgent: this._lastSelectedAgent }); + this.parsedChatRequest = this.instantiationService.createInstance(ChatRequestParser).parseChatRequest(this.viewModel!.sessionId, this.getInput(), this.location, { selectedAgent: this._lastSelectedAgent, mode: this.input.currentMode }); } return this.parsedChatRequest; @@ -217,12 +222,18 @@ export class ChatWidget extends Disposable implements IChatWidget { readonly viewContext: IChatWidgetViewContext; + @memoize + get isUnifiedPanelWidget(): boolean { + return this._location.location === ChatAgentLocation.Panel && !!this.viewOptions.supportsChangingModes && this.configurationService.getValue(ChatConfiguration.UnifiedChatView); + } + constructor( location: ChatAgentLocation | IChatWidgetLocationOptions, _viewContext: IChatWidgetViewContext | undefined, private readonly viewOptions: IChatWidgetViewOptions, private readonly styles: IChatWidgetStyles, @ICodeEditorService codeEditorService: ICodeEditorService, + @IConfigurationService private readonly configurationService: IConfigurationService, @IContextKeyService private readonly contextKeyService: IContextKeyService, @IInstantiationService private readonly instantiationService: IInstantiationService, @IChatService private readonly chatService: IChatService, @@ -236,7 +247,6 @@ export class ChatWidget extends Disposable implements IChatWidget { @IChatEditingService chatEditingService: IChatEditingService, @IStorageService private readonly storageService: IStorageService, @ITelemetryService private readonly telemetryService: ITelemetryService, - @IProductService private readonly productService: IProductService, ) { super(); @@ -253,6 +263,8 @@ export class ChatWidget extends Disposable implements IChatWidget { ChatContextKeys.inChatSession.bindTo(contextKeyService).set(true); ChatContextKeys.location.bindTo(contextKeyService).set(this._location.location); ChatContextKeys.inQuickChat.bindTo(contextKeyService).set(isQuickChat(this)); + ChatContextKeys.inUnifiedChat.bindTo(contextKeyService) + .set(this._location.location === ChatAgentLocation.Panel && !!this.viewOptions.supportsChangingModes && this.configurationService.getValue(ChatConfiguration.UnifiedChatView)); this.agentInInput = ChatContextKeys.inputHasAgent.bindTo(contextKeyService); this.requestInProgress = ChatContextKeys.requestInProgress.bindTo(contextKeyService); this.isRequestPaused = ChatContextKeys.isRequestPaused.bindTo(contextKeyService); @@ -300,8 +312,13 @@ export class ChatWidget extends Disposable implements IChatWidget { return lastResponse?.result?.errorDetails && !lastResponse?.result?.errorDetails.responseIsIncomplete; })); - this._codeBlockModelCollection = this._register(instantiationService.createInstance(CodeBlockModelCollection)); + this._codeBlockModelCollection = this._register(instantiationService.createInstance(CodeBlockModelCollection, undefined)); + this._register(this.configurationService.onDidChangeConfiguration((e) => { + if (e.affectsConfiguration('chat.renderRelatedFiles')) { + this.renderChatEditingSessionState(); + } + })); this._register(autorunWithStore((r, store) => { @@ -338,7 +355,7 @@ export class ChatWidget extends Disposable implements IChatWidget { this.renderChatEditingSessionState(); })); - if (this._location.location === ChatAgentLocation.EditingSession) { + if (this._location.location === ChatAgentLocation.EditingSession || this.chatService.unifiedViewEnabled) { let currentEditSession: IChatEditingSession | undefined = undefined; this._register(this.onDidChangeViewModel(async () => { const sessionId = this._viewModel?.sessionId; @@ -464,7 +481,6 @@ export class ChatWidget extends Disposable implements IChatWidget { this.container = dom.append(parent, $('.interactive-session')); this.welcomeMessageContainer = dom.append(this.container, $('.chat-welcome-view-container', { style: 'display: none' })); - this.renderWelcomeViewContentIfNeeded(); if (renderInputOnTop) { this.createInput(this.container, { renderFollowups, renderStyle }); this.listContainer = dom.append(this.container, $(`.interactive-list`)); @@ -473,6 +489,7 @@ export class ChatWidget extends Disposable implements IChatWidget { this.createInput(this.container, { renderFollowups, renderStyle }); } + this.renderWelcomeViewContentIfNeeded(); this.createList(this.listContainer, { ...this.viewOptions.rendererOptions, renderStyle }); const scrollDownButton = this._register(new Button(this.listContainer, { @@ -533,7 +550,7 @@ export class ChatWidget extends Disposable implements IChatWidget { if (!this.viewModel) { return; } - this.parsedChatRequest = this.instantiationService.createInstance(ChatRequestParser).parseChatRequest(this.viewModel.sessionId, this.getInput(), this.location, { selectedAgent: this._lastSelectedAgent }); + this.parsedChatRequest = this.instantiationService.createInstance(ChatRequestParser).parseChatRequest(this.viewModel.sessionId, this.getInput(), this.location, { selectedAgent: this._lastSelectedAgent, mode: this.input.currentMode }); this._onDidChangeParsedInput.fire(); } @@ -609,7 +626,7 @@ export class ChatWidget extends Disposable implements IChatWidget { if (this.lastItem && isResponseVM(this.lastItem) && this.lastItem.isComplete) { this.renderFollowups(this.lastItem.replyFollowups, this.lastItem); } else if (!treeItems.length && this.viewModel) { - this.renderFollowups(this.viewModel.model.sampleQuestions); + this.renderSampleQuestions(); } else { this.renderFollowups(undefined); } @@ -622,21 +639,22 @@ export class ChatWidget extends Disposable implements IChatWidget { } const numItems = this.viewModel?.getItems().length ?? 0; - const welcomeContent = this.viewModel?.model.welcomeMessage ?? this.persistedWelcomeMessage; - if (welcomeContent && !numItems && (this.welcomeMessageContainer.children.length === 0 || this.location === ChatAgentLocation.EditingSession)) { + const defaultAgent = this.chatAgentService.getDefaultAgent(this.location, this.input.currentMode); + const welcomeContent = defaultAgent?.metadata.welcomeMessageContent ?? this.persistedWelcomeMessage; + if (welcomeContent && !numItems && (this.welcomeMessageContainer.children.length === 0 || this.chatService.unifiedViewEnabled)) { dom.clearNode(this.welcomeMessageContainer); const tips = this.viewOptions.supportsAdditionalParticipants ? new MarkdownString(localize('chatWidget.tips', "{0} or type {1} to attach context\n\n{2} to chat with extensions\n\nType {3} to use commands", '$(attach)', '#', '$(mention)', '/'), { supportThemeIcons: true }) : new MarkdownString(localize('chatWidget.tips.withoutParticipants', "{0} or type {1} to attach context", '$(attach)', '#'), { supportThemeIcons: true }); - const welcomePart = this._register(this.instantiationService.createInstance( + this.welcomePart.value = this.instantiationService.createInstance( ChatViewWelcomePart, { ...welcomeContent, tips, }, { location: this.location, - isWidgetWelcomeViewContent: true + isWidgetAgentWelcomeViewContent: this.input?.currentMode === ChatMode.Agent } - )); - dom.append(this.welcomeMessageContainer, welcomePart.element); + ); + dom.append(this.welcomeMessageContainer, this.welcomePart.value.element); } if (this.viewModel) { @@ -649,13 +667,20 @@ export class ChatWidget extends Disposable implements IChatWidget { if (!this.inputPart) { return; } - this.inputPart.renderChatEditingSessionState(this._editingSession.get() ?? null, this); + this.inputPart.renderChatEditingSessionState(this._editingSession.get() ?? null); if (this.bodyDimension) { this.layout(this.bodyDimension.height, this.bodyDimension.width); } } + private renderSampleQuestions() { + if (this.viewModel) { + // TODO@roblourens hack- only Chat mode supports sample questions + this.renderFollowups(this.input.currentMode === ChatMode.Chat ? this.viewModel.model.sampleQuestions : undefined); + } + } + private async renderFollowups(items: IChatFollowup[] | undefined, response?: IChatResponseViewModel): Promise { this.inputPart.renderFollowups(items, response); @@ -690,7 +715,8 @@ export class ChatWidget extends Disposable implements IChatWidget { const rendererDelegate: IChatRendererDelegate = { getListLength: () => this.tree.getNode(null).visibleChildrenCount, onDidScroll: this.onDidScroll, - container: listContainer + container: listContainer, + currentChatMode: () => this.input.currentMode, }; // Create a dom element to hold UI from editor widgets embedded in chat messages @@ -830,7 +856,9 @@ export class ChatWidget extends Disposable implements IChatWidget { renderStyle: options?.renderStyle === 'minimal' ? 'compact' : options?.renderStyle, menus: { executeToolbar: MenuId.ChatExecute, ...this.viewOptions.menus }, editorOverflowWidgetsDomNode: this.viewOptions.editorOverflowWidgetsDomNode, - renderWorkingSet: this.viewOptions.enableWorkingSet === 'explicit' + enableImplicitContext: this.viewOptions.enableImplicitContext, + renderWorkingSet: this.viewOptions.enableWorkingSet === 'explicit', + supportsChangingModes: this.viewOptions.supportsChangingModes, }, this.styles, () => this.collectInputState() @@ -853,7 +881,7 @@ export class ChatWidget extends Disposable implements IChatWidget { } let msg = ''; - if (e.followup.agentId && e.followup.agentId !== this.chatAgentService.getDefaultAgent(this.location)?.id) { + if (e.followup.agentId && e.followup.agentId !== this.chatAgentService.getDefaultAgent(this.location, this.input.currentMode)?.id) { const agent = this.chatAgentService.getAgent(e.followup.agentId); if (!agent) { return; @@ -911,6 +939,11 @@ export class ChatWidget extends Disposable implements IChatWidget { // Tools agent loads -> welcome content changes this.renderWelcomeViewContentIfNeeded(); })); + this._register(this.input.onDidChangeCurrentChatMode(() => { + this.renderSampleQuestions(); + this.renderWelcomeViewContentIfNeeded(); + this.refreshParsedInput(); + })); } private onDidStyleChange(): void { @@ -944,10 +977,7 @@ export class ChatWidget extends Disposable implements IChatWidget { this.requestInProgress.set(this.viewModel.requestInProgress); this.isRequestPaused.set(this.viewModel.requestPausibility === ChatPauseState.Paused); - // todo@connor4312: disabled for stable 1.97 release. - if (this.productService.quality !== 'stable') { - this.canRequestBePaused.set(this.viewModel.requestPausibility !== ChatPauseState.NotPausable); - } + this.canRequestBePaused.set(this.viewModel.requestPausibility !== ChatPauseState.NotPausable); this.onDidChangeItems(); if (events.some(e => e?.kind === 'addRequest') && this.visible) { @@ -1075,7 +1105,7 @@ export class ChatWidget extends Disposable implements IChatWidget { if (this.viewModel) { this._onDidAcceptInput.fire(); - if (!this.viewOptions.autoScroll) { + if (!checkModeOption(this.input.currentMode, this.viewOptions.autoScroll)) { this.scrollLock = false; } @@ -1100,42 +1130,8 @@ export class ChatWidget extends Disposable implements IChatWidget { let attachedContext = this.inputPart.getAttachedAndImplicitContext(this.viewModel.sessionId); if (this.viewOptions.enableWorkingSet !== undefined) { - const currentEditingSession = this._editingSession; - - const unconfirmedSuggestions = new ResourceSet(); const uniqueWorkingSetEntries = new ResourceSet(); // NOTE: this is used for bookkeeping so the UI can avoid rendering references in the UI that are already shown in the working set const editingSessionAttachedContext: IChatRequestVariableEntry[] = attachedContext; - // Pick up everything that the user sees is part of the working set. - for (const [uri, state] of currentEditingSession.get()?.workingSet || []) { - // Skip over any suggested files that haven't been confirmed yet in the working set - if (state.state === WorkingSetEntryState.Suggested) { - unconfirmedSuggestions.add(uri); - } else { - uniqueWorkingSetEntries.add(uri); - editingSessionAttachedContext.unshift(this.attachmentModel.asVariableEntry(uri, undefined, state.isMarkedReadonly)); - } - } - - // Then take any attachments that are not files - for (const attachment of this.attachmentModel.attachments) { - if (!URI.isUri(attachment.value)) { - editingSessionAttachedContext.push(attachment); - } - } - - // add prompt instruction references to the attached context, if enabled - const promptInstructionUris = new ResourceSet(promptInstructions.chatAttachments.map((v) => v.value) as URI[]); - if (instructionsEnabled) { - editingSessionAttachedContext - .push(...promptInstructions.chatAttachments); - } - - for (const file of uniqueWorkingSetEntries) { - // Make sure that any files that we sent are part of the working set - // but do not permanently add file variables from previous requests to the working set - // since the user may subsequently edit the chat history - currentEditingSession.get()?.addFileToWorkingSet(file); - } // Collect file variables from previous requests before sending the request const previousRequests = this.viewModel.model.getRequests(); @@ -1143,7 +1139,7 @@ export class ChatWidget extends Disposable implements IChatWidget { for (const variable of request.variableData.variables) { if (URI.isUri(variable.value) && variable.isFile) { const uri = variable.value; - if (!uniqueWorkingSetEntries.has(uri) && !promptInstructionUris.has(uri)) { + if (!uniqueWorkingSetEntries.has(uri)) { editingSessionAttachedContext.push(variable); uniqueWorkingSetEntries.add(variable.value); } @@ -1163,19 +1159,20 @@ export class ChatWidget extends Disposable implements IChatWidget { actualSize: number; }; this.telemetryService.publicLog2('chatEditing/workingSetSize', { originalSize: uniqueWorkingSetEntries.size, actualSize: uniqueWorkingSetEntries.size }); - currentEditingSession.get()?.remove(WorkingSetEntryRemovalReason.User, ...unconfirmedSuggestions); } this.chatService.cancelCurrentRequestForSession(this.viewModel.sessionId); const result = await this.chatService.sendRequest(this.viewModel.sessionId, input, { + mode: this.inputPart.currentMode, userSelectedModelId: this.inputPart.currentLanguageModel, location: this.location, locationData: this._location.resolveData?.(), - parserContext: { selectedAgent: this._lastSelectedAgent }, + parserContext: { selectedAgent: this._lastSelectedAgent, mode: this.inputPart.currentMode }, attachedContext, noCommandDetection: options?.noCommandDetection, hasInstructionAttachments: this.inputPart.hasInstructionAttachments, + userSelectedTools: this.input.currentMode === ChatMode.Agent ? this.inputPart.selectedToolsModel.tools.get().map(tool => tool.id) : undefined }); if (result) { @@ -1241,8 +1238,10 @@ export class ChatWidget extends Disposable implements IChatWidget { const lastElementVisible = this.tree.scrollTop + this.tree.renderHeight >= this.tree.scrollHeight - 2; const listHeight = Math.max(0, height - inputPartHeight); - if (!this.viewOptions.autoScroll) { + if (!checkModeOption(this.input.currentMode, this.viewOptions.autoScroll)) { this.listContainer.style.setProperty('--chat-current-response-min-height', listHeight * .75 + 'px'); + } else { + this.listContainer.style.removeProperty('--chat-current-response-min-height'); } this.tree.layout(listHeight, width); @@ -1261,7 +1260,7 @@ export class ChatWidget extends Disposable implements IChatWidget { const lastItem = this.viewModel?.getItems().at(-1); const lastResponseIsRendering = isResponseVM(lastItem) && lastItem.renderData; - if (lastElementVisible && (!lastResponseIsRendering || this.viewOptions.autoScroll)) { + if (lastElementVisible && (!lastResponseIsRendering || checkModeOption(this.input.currentMode, this.viewOptions.autoScroll))) { this.scrollToEnd(); } @@ -1372,8 +1371,9 @@ export class ChatWidget extends Disposable implements IChatWidget { saveState(): void { this.inputPart.saveState(); - if (this.viewModel?.model.welcomeMessage) { - this.storageService.store(`${PersistWelcomeMessageContentKey}.${this.location}`, this.viewModel?.model.welcomeMessage, StorageScope.APPLICATION, StorageTarget.MACHINE); + const welcomeContent = this.chatAgentService.getDefaultAgent(this.location, this.input.currentMode)?.metadata.welcomeMessageContent; + if (welcomeContent) { + this.storageService.store(`${PersistWelcomeMessageContentKey}.${this.location}`, welcomeContent, StorageScope.APPLICATION, StorageTarget.MACHINE); } } diff --git a/src/vs/workbench/contrib/chat/browser/codeBlockPart.ts b/src/vs/workbench/contrib/chat/browser/codeBlockPart.ts index db1da2cdcdc7..05276d45608c 100644 --- a/src/vs/workbench/contrib/chat/browser/codeBlockPart.ts +++ b/src/vs/workbench/contrib/chat/browser/codeBlockPart.ts @@ -86,7 +86,7 @@ export interface ICodeBlockData { readonly range?: Range; readonly parentContextKeyService?: IContextKeyService; - readonly hideToolbar?: boolean; + readonly renderOptions?: ICodeBlockRenderOptions; } /** @@ -135,6 +135,13 @@ export interface ICodeBlockActionContext { element: unknown; } +export interface ICodeBlockRenderOptions { + hideToolbar?: boolean; + verticalPadding?: number; + reserveWidth?: number; + editorOptions?: IEditorOptions; +} + const defaultCodeblockPadding = 10; export class CodeBlockPart extends Disposable { protected readonly _onDidChangeContentHeight = this._register(new Emitter()); @@ -156,8 +163,12 @@ export class CodeBlockPart extends Disposable { private resourceContextKey: ResourceContextKey; + private get verticalPadding(): number { + return this.currentCodeBlockData?.renderOptions?.verticalPadding ?? defaultCodeblockPadding; + } + constructor( - private readonly options: ChatEditorOptions, + private readonly editorOptions: ChatEditorOptions, readonly menuId: MenuId, delegate: IChatRendererDelegate, overflowWidgetsDomNode: HTMLElement | undefined, @@ -182,7 +193,7 @@ export class CodeBlockPart extends Disposable { scrollBeyondLastLine: false, lineDecorationsWidth: 8, dragAndDrop: false, - padding: { top: defaultCodeblockPadding, bottom: defaultCodeblockPadding }, + padding: { top: this.verticalPadding, bottom: this.verticalPadding }, mouseWheelZoom: false, scrollbar: { vertical: 'hidden', @@ -246,7 +257,7 @@ export class CodeBlockPart extends Disposable { } })); - this._register(this.options.onDidChange(() => { + this._register(this.editorOptions.onDidChange(() => { this.editor.updateOptions(this.getEditorOptionsFromConfig()); })); @@ -319,9 +330,9 @@ export class CodeBlockPart extends Disposable { const horizontalScrollbarVisible = this.currentScrollWidth > this.editor.getLayoutInfo().contentWidth; const scrollbarHeight = this.editor.getLayoutInfo().horizontalScrollbarHeight; const bottomPadding = horizontalScrollbarVisible ? - Math.max(defaultCodeblockPadding - scrollbarHeight, 2) : - defaultCodeblockPadding; - this.editor.updateOptions({ padding: { top: defaultCodeblockPadding, bottom: bottomPadding } }); + Math.max(this.verticalPadding - scrollbarHeight, 2) : + this.verticalPadding; + this.editor.updateOptions({ padding: { top: this.verticalPadding, bottom: bottomPadding } }); } private _configureForScreenReader(): void { @@ -336,22 +347,24 @@ export class CodeBlockPart extends Disposable { private getEditorOptionsFromConfig(): IEditorOptions { return { - wordWrap: this.options.configuration.resultEditor.wordWrap, - fontLigatures: this.options.configuration.resultEditor.fontLigatures, - bracketPairColorization: this.options.configuration.resultEditor.bracketPairColorization, - fontFamily: this.options.configuration.resultEditor.fontFamily === 'default' ? + wordWrap: this.editorOptions.configuration.resultEditor.wordWrap, + fontLigatures: this.editorOptions.configuration.resultEditor.fontLigatures, + bracketPairColorization: this.editorOptions.configuration.resultEditor.bracketPairColorization, + fontFamily: this.editorOptions.configuration.resultEditor.fontFamily === 'default' ? EDITOR_FONT_DEFAULTS.fontFamily : - this.options.configuration.resultEditor.fontFamily, - fontSize: this.options.configuration.resultEditor.fontSize, - fontWeight: this.options.configuration.resultEditor.fontWeight, - lineHeight: this.options.configuration.resultEditor.lineHeight, + this.editorOptions.configuration.resultEditor.fontFamily, + fontSize: this.editorOptions.configuration.resultEditor.fontSize, + fontWeight: this.editorOptions.configuration.resultEditor.fontWeight, + lineHeight: this.editorOptions.configuration.resultEditor.lineHeight, + ...this.currentCodeBlockData?.renderOptions?.editorOptions, }; } layout(width: number): void { const contentHeight = this.getContentHeight(); const editorBorder = 2; - this.editor.layout({ width: width - editorBorder, height: contentHeight }); + width = width - editorBorder - (this.currentCodeBlockData?.renderOptions?.reserveWidth ?? 0); + this.editor.layout({ width, height: contentHeight }); this.updatePaddingForLayout(); } @@ -370,7 +383,7 @@ export class CodeBlockPart extends Disposable { this.contextKeyService.updateParent(data.parentContextKeyService); } - if (this.options.configuration.resultEditor.wordWrap === 'on') { + if (this.getEditorOptionsFromConfig().wordWrap === 'on') { // Initialize the editor with the new proper width so that getContentHeight // will be computed correctly in the next call to layout() this.layout(width); @@ -382,9 +395,12 @@ export class CodeBlockPart extends Disposable { } this.layout(width); - this.editor.updateOptions({ ariaLabel: localize('chat.codeBlockLabel', "Code block {0}", data.codeBlockIndex + 1) }); + this.editor.updateOptions({ + ...this.getEditorOptionsFromConfig(), + ariaLabel: localize('chat.codeBlockLabel', "Code block {0}", data.codeBlockIndex + 1), + }); this.toolbar.setAriaLabel(localize('chat.codeBlockToolbarLabel', "Toolbar for code block {0}", data.codeBlockIndex + 1)); - if (data.hideToolbar) { + if (data.renderOptions?.hideToolbar) { dom.hide(this.toolbar.getElement()); } else { dom.show(this.toolbar.getElement()); diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatDynamicVariables.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatDynamicVariables.ts index 9c805598653b..67848145eb8f 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatDynamicVariables.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatDynamicVariables.ts @@ -3,12 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { coalesce } from '../../../../../base/common/arrays.js'; +import { coalesce, groupBy } from '../../../../../base/common/arrays.js'; +import { assertNever } from '../../../../../base/common/assert.js'; +import { CancellationToken } from '../../../../../base/common/cancellation.js'; +import { Codicon } from '../../../../../base/common/codicons.js'; import { isCancellationError } from '../../../../../base/common/errors.js'; +import * as glob from '../../../../../base/common/glob.js'; import { IMarkdownString, MarkdownString } from '../../../../../base/common/htmlContent.js'; import { Disposable, DisposableStore } from '../../../../../base/common/lifecycle.js'; import { ResourceSet } from '../../../../../base/common/map.js'; import { basename, dirname, joinPath, relativePath } from '../../../../../base/common/resources.js'; +import { ThemeIcon } from '../../../../../base/common/themables.js'; import { URI } from '../../../../../base/common/uri.js'; import { IRange, Range } from '../../../../../editor/common/core/range.js'; import { IDecorationOptions } from '../../../../../editor/common/editorCommon.js'; @@ -22,20 +27,19 @@ import { FileType, IFileService } from '../../../../../platform/files/common/fil import { IInstantiationService, ServicesAccessor } from '../../../../../platform/instantiation/common/instantiation.js'; import { ILabelService } from '../../../../../platform/label/common/label.js'; import { ILogService } from '../../../../../platform/log/common/log.js'; +import { IMarkerService, MarkerSeverity } from '../../../../../platform/markers/common/markers.js'; +import { PromptsConfig } from '../../../../../platform/prompts/common/config.js'; import { IQuickAccessOptions } from '../../../../../platform/quickinput/common/quickAccess.js'; -import { IQuickInputService, IQuickPickItem } from '../../../../../platform/quickinput/common/quickInput.js'; +import { IQuickInputService, IQuickPickItem, IQuickPickSeparator } from '../../../../../platform/quickinput/common/quickInput.js'; +import { IUriIdentityService } from '../../../../../platform/uriIdentity/common/uriIdentity.js'; import { IWorkspaceContextService } from '../../../../../platform/workspace/common/workspace.js'; -import { getExcludes, ISearchConfiguration, IFileQuery, QueryType, ISearchComplete, ISearchService } from '../../../../services/search/common/search.js'; +import { getExcludes, IFileQuery, ISearchComplete, ISearchConfiguration, ISearchService, QueryType } from '../../../../services/search/common/search.js'; import { ISymbolQuickPickItem } from '../../../search/browser/symbolsQuickAccess.js'; -import { IChatRequestVariableValue, IDynamicVariable } from '../../common/chatVariables.js'; -import * as glob from '../../../../../base/common/glob.js'; +import { IDiagnosticVariableEntryFilterData } from '../../common/chatModel.js'; +import { IChatRequestProblemsVariable, IChatRequestVariableValue, IDynamicVariable } from '../../common/chatVariables.js'; import { IChatWidget } from '../chat.js'; import { ChatWidget, IChatWidgetContrib } from '../chatWidget.js'; import { ChatFileReference } from './chatDynamicVariables/chatFileReference.js'; -import { CancellationToken } from '../../../../../base/common/cancellation.js'; -import { ThemeIcon } from '../../../../../base/common/themables.js'; -import { Codicon } from '../../../../../base/common/codicons.js'; -import { PromptsConfig } from '../../../../../platform/prompts/common/config.js'; export const dynamicVariableDecorationType = 'chat-dynamic-variable'; @@ -345,6 +349,7 @@ export class SelectAndInsertFolderAction extends Action2 { context.widget.getContrib(ChatDynamicVariableModel.ID)?.addReference({ id: 'vscode.folder', isFile: false, + isDirectory: true, prefix: 'folder', range: { startLineNumber: range.startLineNumber, startColumn: range.startColumn, endLineNumber: range.endLineNumber, endColumn: range.startColumn + text.length }, data: folder @@ -389,6 +394,7 @@ export async function createFolderQuickPick(accessor: ServicesAccessor): Promise searchFolders( workspace, value, + true, undefined, undefined, configurationService, @@ -416,6 +422,7 @@ export async function createFolderQuickPick(accessor: ServicesAccessor): Promise type: 'item', id: folder.toString(), resource: folder, + alwaysShow: true, label: basename(folder), description: labelService.getUriLabel(dirname(folder), { relative: true }), iconClass: ThemeIcon.asClassName(Codicon.folder), @@ -446,11 +453,14 @@ export async function getTopLevelFolders(workspaces: URI[], fileService: IFileSe export async function searchFolders( workspace: URI, pattern: string, + fuzzyMatch: boolean, token: CancellationToken | undefined, cacheKey: string | undefined, configurationService: IConfigurationService, searchService: ISearchService ): Promise { + const segmentMatchPattern = caseInsensitiveGlobPattern(fuzzyMatch ? fuzzyMatchingGlobPattern(pattern) : continousMatchingGlobPattern(pattern)); + const searchExcludePattern = getExcludes(configurationService.getValue({ resource: workspace })) || {}; const searchOptions: IFileQuery = { folderQueries: [{ @@ -465,7 +475,7 @@ export async function searchFolders( let folderResults: ISearchComplete | undefined; try { - folderResults = await searchService.fileSearch({ ...searchOptions, filePattern: `**/*${pattern}*/**` }, token); + folderResults = await searchService.fileSearch({ ...searchOptions, filePattern: `**/${segmentMatchPattern}/**` }, token); } catch (e) { if (!isCancellationError(e)) { throw e; @@ -476,13 +486,40 @@ export async function searchFolders( return []; } - const folderResources = getMatchingFoldersFromFiles(folderResults.results.map(result => result.resource), workspace, pattern); + const folderResources = getMatchingFoldersFromFiles(folderResults.results.map(result => result.resource), workspace, segmentMatchPattern); return folderResources; } +function fuzzyMatchingGlobPattern(pattern: string): string { + if (!pattern) { + return '*'; + } + return '*' + pattern.split('').join('*') + '*'; +} + +function continousMatchingGlobPattern(pattern: string): string { + if (!pattern) { + return '*'; + } + return '*' + pattern + '*'; +} + +function caseInsensitiveGlobPattern(pattern: string): string { + let caseInsensitiveFilePattern = ''; + for (let i = 0; i < pattern.length; i++) { + const char = pattern[i]; + if (/[a-zA-Z]/.test(char)) { + caseInsensitiveFilePattern += `[${char.toLowerCase()}${char.toUpperCase()}]`; + } else { + caseInsensitiveFilePattern += char; + } + } + return caseInsensitiveFilePattern; +} + // TODO: remove this and have support from the search service -function getMatchingFoldersFromFiles(resources: URI[], workspace: URI, pattern: string): URI[] { +function getMatchingFoldersFromFiles(resources: URI[], workspace: URI, segmentMatchPattern: string): URI[] { const uniqueFolders = new ResourceSet(); for (const resource of resources) { const relativePathToRoot = relativePath(workspace, resource); @@ -502,7 +539,7 @@ function getMatchingFoldersFromFiles(resources: URI[], workspace: URI, pattern: for (const folderResource of uniqueFolders) { const stats = folderResource.path.split('/'); const dirStat = stats[stats.length - 1]; - if (!dirStat || !glob.match(`*${pattern}*`, dirStat)) { + if (!dirStat || !glob.match(segmentMatchPattern, dirStat)) { continue; } @@ -645,3 +682,133 @@ export class AddDynamicVariableAction extends Action2 { } } registerAction2(AddDynamicVariableAction); + +export async function createMarkersQuickPick(accessor: ServicesAccessor, level: 'problem' | 'file', onBackgroundAccept?: (item: IDiagnosticVariableEntryFilterData[]) => void): Promise { + const markers = accessor.get(IMarkerService).read({ severities: MarkerSeverity.Error | MarkerSeverity.Warning | MarkerSeverity.Info }); + if (!markers.length) { + return; + } + + const uriIdentityService = accessor.get(IUriIdentityService); + const labelService = accessor.get(ILabelService); + const grouped = groupBy(markers, (a, b) => uriIdentityService.extUri.compare(a.resource, b.resource)); + + const severities = new Set(); + type MarkerPickItem = IQuickPickItem & { resource?: URI; entry: IDiagnosticVariableEntryFilterData }; + const items: (MarkerPickItem | IQuickPickSeparator)[] = []; + + let pickCount = 0; + for (const group of grouped) { + const resource = group[0].resource; + if (level === 'problem') { + items.push({ type: 'separator', label: labelService.getUriLabel(resource, { relative: true }) }); + for (const marker of group) { + pickCount++; + severities.add(marker.severity); + items.push({ + type: 'item', + resource: marker.resource, + label: marker.message, + description: localize('markers.panel.at.ln.col.number', "[Ln {0}, Col {1}]", '' + marker.startLineNumber, '' + marker.startColumn), + entry: IDiagnosticVariableEntryFilterData.fromMarker(marker), + }); + } + } else if (level === 'file') { + const entry = { filterUri: resource }; + pickCount++; + items.push({ + type: 'item', + resource, + label: IDiagnosticVariableEntryFilterData.label(entry), + description: group[0].message + (group.length > 1 ? localize('problemsMore', '+ {0} more', group.length - 1) : ''), + entry, + }); + for (const marker of group) { + severities.add(marker.severity); + } + } else { + assertNever(level); + } + } + + if (pickCount < 2) { // single error in a URI + return items.find((i): i is MarkerPickItem => i.type === 'item')?.entry; + } + + if (level === 'file') { + items.unshift({ type: 'separator', label: localize('markers.panel.files', 'Files') }); + } + + items.unshift({ type: 'item', label: localize('markers.panel.allErrors', 'All Problems'), entry: { filterSeverity: MarkerSeverity.Info } }); + + const quickInputService = accessor.get(IQuickInputService); + const store = new DisposableStore(); + const quickPick = store.add(quickInputService.createQuickPick({ useSeparators: true })); + quickPick.canAcceptInBackground = !onBackgroundAccept; + quickPick.placeholder = localize('pickAProblem', 'Pick a problem to attach...'); + quickPick.items = items; + + return new Promise(resolve => { + store.add(quickPick.onDidHide(() => resolve(undefined))); + store.add(quickPick.onDidAccept(ev => { + if (ev.inBackground) { + onBackgroundAccept?.(quickPick.selectedItems.map(i => i.entry)); + } else { + resolve(quickPick.selectedItems[0]?.entry); + quickPick.dispose(); + } + })); + quickPick.show(); + }).finally(() => store.dispose()); +} + +export class SelectAndInsertProblemAction extends Action2 { + static readonly Name = 'problems'; + static readonly ID = 'workbench.action.chat.selectAndInsertProblems'; + + constructor() { + super({ + id: SelectAndInsertProblemAction.ID, + title: '' // not displayed + }); + } + + async run(accessor: ServicesAccessor, ...args: any[]) { + const logService = accessor.get(ILogService); + const context = args[0]; + if (!isSelectAndInsertActionContext(context)) { + return; + } + + const doCleanup = () => { + // Failed, remove the dangling `problem` + context.widget.inputEditor.executeEdits('chatInsertProblems', [{ range: context.range, text: `` }]); + }; + + const pick = await createMarkersQuickPick(accessor, 'file'); + if (!pick) { + doCleanup(); + return; + } + + const editor = context.widget.inputEditor; + const originalRange = context.range; + const insertText = `#${SelectAndInsertProblemAction.Name}:${pick.filterUri ? basename(pick.filterUri) : MarkerSeverity.toString(pick.filterSeverity!)}`; + + const varRange = new Range(originalRange.startLineNumber, originalRange.startColumn, originalRange.endLineNumber, originalRange.startColumn + insertText.length); + const success = editor.executeEdits('chatInsertProblems', [{ range: varRange, text: insertText + ' ' }]); + if (!success) { + logService.trace(`SelectAndInsertProblemsAction: failed to insert "${insertText}"`); + doCleanup(); + return; + } + + context.widget.getContrib(ChatDynamicVariableModel.ID)?.addReference({ + id: 'vscode.problems', + prefix: SelectAndInsertProblemAction.Name, + range: varRange, + data: { id: 'vscode.problems', filter: pick } satisfies IChatRequestProblemsVariable, + }); + } +} +registerAction2(SelectAndInsertProblemAction); diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatImplicitContext.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatImplicitContext.ts index aeb6c1911d1b..afcd62f22183 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatImplicitContext.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatImplicitContext.ts @@ -6,16 +6,22 @@ import { CancellationTokenSource } from '../../../../../base/common/cancellation.js'; import { Emitter, Event } from '../../../../../base/common/event.js'; import { Disposable, DisposableStore, MutableDisposable } from '../../../../../base/common/lifecycle.js'; +import { Schemas } from '../../../../../base/common/network.js'; +import { autorun } from '../../../../../base/common/observable.js'; import { basename } from '../../../../../base/common/resources.js'; import { URI } from '../../../../../base/common/uri.js'; import { ICodeEditor, isCodeEditor, isDiffEditor } from '../../../../../editor/browser/editorBrowser.js'; import { ICodeEditorService } from '../../../../../editor/browser/services/codeEditorService.js'; import { Location } from '../../../../../editor/common/languages.js'; +import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; import { IWorkbenchContribution } from '../../../../common/contributions.js'; import { EditorsOrder } from '../../../../common/editor.js'; import { IEditorService } from '../../../../services/editor/common/editorService.js'; -import { ChatAgentLocation } from '../../common/chatAgents.js'; +import { getNotebookEditorFromEditorPane, INotebookEditor } from '../../../notebook/browser/notebookBrowser.js'; +import { IChatEditingService } from '../../common/chatEditingService.js'; import { IBaseChatRequestVariableEntry, IChatRequestImplicitVariableEntry } from '../../common/chatModel.js'; +import { IChatService } from '../../common/chatService.js'; +import { ChatAgentLocation } from '../../common/constants.js'; import { ILanguageModelIgnoredFilesService } from '../../common/ignoredFiles.js'; import { IChatWidget, IChatWidgetService } from '../chat.js'; @@ -24,17 +30,23 @@ export class ChatImplicitContextContribution extends Disposable implements IWork private readonly _currentCancelTokenSource = this._register(new MutableDisposable()); + private _implicitContextEnablement = this.configurationService.getValue<{ [mode: string]: string }>('chat.implicitContext.enabled'); + constructor( @ICodeEditorService private readonly codeEditorService: ICodeEditorService, @IEditorService private readonly editorService: IEditorService, @IChatWidgetService private readonly chatWidgetService: IChatWidgetService, + @IChatService private readonly chatService: IChatService, + @IChatEditingService private readonly chatEditingService: IChatEditingService, + @IConfigurationService private readonly configurationService: IConfigurationService, @ILanguageModelIgnoredFilesService private readonly ignoredFilesService: ILanguageModelIgnoredFilesService, ) { super(); const activeEditorDisposables = this._register(new DisposableStore()); + this._register(Event.runAndSubscribe( - editorService.onDidVisibleEditorsChange, + editorService.onDidActiveEditorChange, (() => { activeEditorDisposables.clear(); const codeEditor = this.findActiveCodeEditor(); @@ -48,15 +60,51 @@ export class ChatImplicitContextContribution extends Disposable implements IWork 500)(() => this.updateImplicitContext())); } + const notebookEditor = this.findActiveNotebookEditor(); + if (notebookEditor) { + activeEditorDisposables.add(Event.debounce( + Event.any( + notebookEditor.onDidChangeModel, + notebookEditor.onDidChangeActiveCell + ), + () => undefined, + 500)(() => this.updateImplicitContext())); + } + this.updateImplicitContext(); }))); - this._register(chatWidgetService.onDidAddWidget(widget => this.updateImplicitContext(widget))); + this._register(autorun((reader) => { + this.chatEditingService.editingSessionsObs.read(reader); + this.updateImplicitContext(); + })); + this._register(this.configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration('chat.implicitContext.enabled')) { + this._implicitContextEnablement = this.configurationService.getValue<{ [mode: string]: string }>('chat.implicitContext.enabled'); + this.updateImplicitContext(); + } + })); + this._register(this.chatService.onDidSubmitRequest(({ chatSessionId }) => { + const widget = this.chatWidgetService.getWidgetBySessionId(chatSessionId); + if (!widget?.input.implicitContext) { + return; + } + if (this._implicitContextEnablement[widget.location] === 'first' && widget.viewModel?.getItems().length !== 0) { + widget.input.implicitContext.setValue(undefined, false); + } + })); + this._register(this.chatWidgetService.onDidAddWidget(async (widget) => { + await this.updateImplicitContext(widget); + })); } private findActiveCodeEditor(): ICodeEditor | undefined { const codeEditor = this.codeEditorService.getActiveCodeEditor(); if (codeEditor) { const model = codeEditor.getModel(); + if (model?.uri.scheme === Schemas.vscodeNotebookCell) { + return undefined; + } + if (model) { return codeEditor; } @@ -79,6 +127,10 @@ export class ChatImplicitContextContribution extends Disposable implements IWork return undefined; } + private findActiveNotebookEditor(): INotebookEditor | undefined { + return getNotebookEditorFromEditorPane(this.editorService.activeEditorPane); + } + private async updateImplicitContext(updateWidget?: IChatWidget): Promise { const cancelTokenSource = this._currentCancelTokenSource.value = new CancellationTokenSource(); const codeEditor = this.findActiveCodeEditor(); @@ -106,6 +158,16 @@ export class ChatImplicitContextContribution extends Disposable implements IWork } } + const notebookEditor = this.findActiveNotebookEditor(); + if (notebookEditor) { + const activeCell = notebookEditor.getActiveCell(); + if (activeCell) { + newValue = activeCell.uri; + } else { + newValue = notebookEditor.textModel?.uri; + } + } + const uri = newValue instanceof URI ? newValue : newValue?.uri; if (uri && await this.ignoredFilesService.fileIsIgnored(uri, cancelTokenSource.token)) { newValue = undefined; @@ -115,10 +177,19 @@ export class ChatImplicitContextContribution extends Disposable implements IWork return; } - const widgets = updateWidget ? [updateWidget] : [...this.chatWidgetService.getWidgetsByLocations(ChatAgentLocation.Panel), ...this.chatWidgetService.getWidgetsByLocations(ChatAgentLocation.Editor)]; + const widgets = updateWidget ? [updateWidget] : [...this.chatWidgetService.getWidgetsByLocations(ChatAgentLocation.Panel), ...this.chatWidgetService.getWidgetsByLocations(ChatAgentLocation.EditingSession), ...this.chatWidgetService.getWidgetsByLocations(ChatAgentLocation.Editor)]; for (const widget of widgets) { - if (widget.input.implicitContext) { + if (!widget.input.implicitContext) { + continue; + } + const setting = this._implicitContextEnablement[widget.location]; + const isFirstInteraction = widget.viewModel?.getItems().length === 0; + if (setting === 'first' && !isFirstInteraction) { + widget.input.implicitContext.setValue(undefined, false); + } else if (setting === 'always' || setting === 'first' && isFirstInteraction) { widget.input.implicitContext.setValue(newValue, isSelection); + } else if (setting === 'never') { + widget.input.implicitContext.setValue(undefined, false); } } } diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts index c5c3fedf9a6d..61e0ce11612c 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts @@ -27,6 +27,7 @@ import { IConfigurationService } from '../../../../../platform/configuration/com import { IFileService } from '../../../../../platform/files/common/files.js'; import { IInstantiationService, ServicesAccessor } from '../../../../../platform/instantiation/common/instantiation.js'; import { ILabelService } from '../../../../../platform/label/common/label.js'; +import { IMarkerService } from '../../../../../platform/markers/common/markers.js'; import { Registry } from '../../../../../platform/registry/common/platform.js'; import { IWorkspaceContextService } from '../../../../../platform/workspace/common/workspace.js'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from '../../../../common/contributions.js'; @@ -35,16 +36,17 @@ import { IHistoryService } from '../../../../services/history/common/history.js' import { LifecyclePhase } from '../../../../services/lifecycle/common/lifecycle.js'; import { QueryBuilder } from '../../../../services/search/common/queryBuilder.js'; import { ISearchService } from '../../../../services/search/common/search.js'; -import { ChatAgentLocation, IChatAgentData, IChatAgentNameService, IChatAgentService, getFullyQualifiedId } from '../../common/chatAgents.js'; +import { IChatAgentData, IChatAgentNameService, IChatAgentService, getFullyQualifiedId } from '../../common/chatAgents.js'; import { IChatEditingService } from '../../common/chatEditingService.js'; import { ChatRequestAgentPart, ChatRequestAgentSubcommandPart, ChatRequestTextPart, ChatRequestToolPart, chatAgentLeader, chatSubcommandLeader, chatVariableLeader } from '../../common/chatParserTypes.js'; import { IChatSlashCommandService } from '../../common/chatSlashCommands.js'; import { IDynamicVariable } from '../../common/chatVariables.js'; +import { ChatAgentLocation, ChatMode } from '../../common/constants.js'; import { ILanguageModelToolsService } from '../../common/languageModelToolsService.js'; import { ChatEditingSessionSubmitAction, ChatSubmitAction } from '../actions/chatExecuteActions.js'; import { IChatWidget, IChatWidgetService } from '../chat.js'; import { ChatInputPart } from '../chatInputPart.js'; -import { ChatDynamicVariableModel, getTopLevelFolders, searchFolders, SelectAndInsertFolderAction, SelectAndInsertFileAction, SelectAndInsertSymAction } from './chatDynamicVariables.js'; +import { ChatDynamicVariableModel, SelectAndInsertFileAction, SelectAndInsertFolderAction, SelectAndInsertProblemAction, SelectAndInsertSymAction, getTopLevelFolders, searchFolders } from './chatDynamicVariables.js'; class SlashCommandCompletions extends Disposable { constructor( @@ -214,7 +216,7 @@ class AgentCompletions extends Disposable { provideCompletionItems: async (model: ITextModel, position: Position, _context: CompletionContext, token: CancellationToken) => { const widget = this.chatWidgetService.getWidgetByInputUri(model.uri); const viewModel = widget?.viewModel; - if (!widget || !viewModel) { + if (!widget || !viewModel || widget.input.currentMode !== ChatMode.Chat) { return; } @@ -264,7 +266,7 @@ class AgentCompletions extends Disposable { return { suggestions: justAgents.concat( coalesce(agents.flatMap(agent => agent.slashCommands.map((c, i) => { - if (agent.isDefault && this.chatAgentService.getDefaultAgent(widget.location)?.id !== agent.id) { + if (agent.isDefault && this.chatAgentService.getDefaultAgent(widget.location, widget.input.currentMode)?.id !== agent.id) { return; } @@ -304,7 +306,7 @@ class AgentCompletions extends Disposable { provideCompletionItems: async (model: ITextModel, position: Position, _context: CompletionContext, token: CancellationToken) => { const widget = this.chatWidgetService.getWidgetByInputUri(model.uri); const viewModel = widget?.viewModel; - if (!widget || !viewModel) { + if (!widget || !viewModel || widget.input.currentMode !== ChatMode.Chat) { return; } @@ -323,7 +325,7 @@ class AgentCompletions extends Disposable { return { suggestions: coalesce(agents.flatMap(agent => agent.slashCommands.map((c, i) => { - if (agent.isDefault && this.chatAgentService.getDefaultAgent(widget.location)?.id !== agent.id) { + if (agent.isDefault && this.chatAgentService.getDefaultAgent(widget.location, widget.input.currentMode)?.id !== agent.id) { return; } @@ -365,7 +367,7 @@ class AgentCompletions extends Disposable { } const widget = this.chatWidgetService.getWidgetByInputUri(model.uri); - if (widget?.location !== ChatAgentLocation.Panel) { + if (widget?.location !== ChatAgentLocation.Panel || widget.input.currentMode !== ChatMode.Chat) { return; } @@ -467,6 +469,7 @@ class BuiltinDynamicCompletions extends Disposable { @IEditorService private readonly editorService: IEditorService, @IConfigurationService private readonly configurationService: IConfigurationService, @IFileService private readonly fileService: IFileService, + @IMarkerService markerService: IMarkerService, ) { super(); @@ -560,7 +563,7 @@ class BuiltinDynamicCompletions extends Disposable { sortText: 'z', command: { id: BuiltinDynamicCompletions.addReferenceCommand, title: '', arguments: [new ReferenceArgument(widget, { - id: 'vscode.file', + id: 'vscode.selection', prefix: 'file', isFile: true, range: { startLineNumber: range.replace.startLineNumber, startColumn: range.replace.startColumn, endLineNumber: range.replace.endLineNumber, endColumn: range.replace.startColumn + text.length }, @@ -598,6 +601,30 @@ class BuiltinDynamicCompletions extends Disposable { return result; }); + // Problems completions, we just attach all problems in this case + this.registerVariableCompletions(SelectAndInsertProblemAction.Name, ({ widget, range, position, model }, token) => { + const stats = markerService.getStatistics(); + if (!stats.errors && !stats.warnings) { + return null; + } + + const result: CompletionList = { suggestions: [] }; + + const completedText = `${chatVariableLeader}${SelectAndInsertProblemAction.Name}:`; + const afterTextRange = new Range(position.lineNumber, range.replace.startColumn, position.lineNumber, range.replace.startColumn + completedText.length); + result.suggestions.push({ + label: `${chatVariableLeader}${SelectAndInsertProblemAction.Name}`, + insertText: completedText, + documentation: localize('pickProblemsLabel', "Problems in your workspace"), + range, + kind: CompletionItemKind.Text, + command: { id: SelectAndInsertProblemAction.ID, title: SelectAndInsertProblemAction.ID, arguments: [{ widget, range: afterTextRange }] }, + sortText: 'z' + }); + + return result; + }); + this._register(CommandsRegistry.registerCommand(BuiltinDynamicCompletions.addReferenceCommand, (_services, arg) => this.cmdAddReference(arg))); this.queryBuilder = this.instantiationService.createInstance(QueryBuilder); @@ -665,7 +692,7 @@ class BuiltinDynamicCompletions extends Disposable { const len = result.suggestions.length; // RELATED FILES - if (widget.location === ChatAgentLocation.EditingSession && widget.viewModel && this._chatEditingService.getEditingSession(widget.viewModel.sessionId)) { + if (widget.input.currentMode !== ChatMode.Chat && widget.viewModel && this._chatEditingService.getEditingSession(widget.viewModel.sessionId)) { const relatedFiles = (await raceTimeout(this._chatEditingService.getRelatedFiles(widget.viewModel.sessionId, widget.getInput(), widget.attachmentModel.fileAttachments, token), 200)) ?? []; for (const relatedFileGroup of relatedFiles) { for (const relatedFile of relatedFileGroup.files) { @@ -755,6 +782,7 @@ class BuiltinDynamicCompletions extends Disposable { id: 'vscode.folder', prefix: 'folder', isFile: false, + isDirectory: true, range: { startLineNumber: info.replace.startLineNumber, startColumn: info.replace.startColumn, endLineNumber: info.replace.endLineNumber, endColumn: info.replace.startColumn + text.length }, data: resource })] @@ -781,7 +809,7 @@ class BuiltinDynamicCompletions extends Disposable { const cacheKey = this.updateCacheKey(); - const folders = await Promise.all(workspaces.map(workspace => searchFolders(workspace, pattern, token, cacheKey.key, this.configurationService, this.searchService))); + const folders = await Promise.all(workspaces.map(workspace => searchFolders(workspace, pattern, true, token, cacheKey.key, this.configurationService, this.searchService))); for (const resource of folders.flat()) { if (seen.has(resource)) { // already included via history @@ -802,12 +830,11 @@ class BuiltinDynamicCompletions extends Disposable { const makeSymbolCompletionItem = (symbolItem: { name: string; location: Location; kind: SymbolKind }, pattern: string): CompletionItem => { const text = `${chatVariableLeader}sym:${symbolItem.name}`; const resource = symbolItem.location.uri; - const symbolLabelWithIcon = `$(${SymbolKinds.toIcon(symbolItem.kind).id}) ${symbolItem.name}`; const uriLabel = this.labelService.getUriLabel(resource, { relative: true }); const sortText = pattern ? '{' /* after z */ : '|' /* after { */; return { - label: { label: symbolLabelWithIcon, description: uriLabel }, + label: { label: symbolItem.name, description: uriLabel }, filterText: `${chatVariableLeader}${symbolItem.name}`, insertText: info.varWord?.endColumn === info.replace.endColumn ? `${text} ` : text, range: info, @@ -817,7 +844,7 @@ class BuiltinDynamicCompletions extends Disposable { id: BuiltinDynamicCompletions.addReferenceCommand, title: '', arguments: [new ReferenceArgument(widget, { id: 'vscode.symbol', prefix: 'sym', - fullName: symbolLabelWithIcon, + fullName: symbolItem.name, range: { startLineNumber: info.replace.startLineNumber, startColumn: info.replace.startColumn, endLineNumber: info.replace.endLineNumber, endColumn: info.replace.startColumn + text.length }, data: symbolItem.location })] diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts index e0997913f21d..0ebde64d5230 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatInputEditorContrib.ts @@ -3,21 +3,39 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { MarkdownString } from '../../../../../base/common/htmlContent.js'; -import { Disposable, MutableDisposable } from '../../../../../base/common/lifecycle.js'; -import { ICodeEditorService } from '../../../../../editor/browser/services/codeEditorService.js'; -import { Range } from '../../../../../editor/common/core/range.js'; -import { IDecorationOptions } from '../../../../../editor/common/editorCommon.js'; -import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; -import { inputPlaceholderForeground } from '../../../../../platform/theme/common/colorRegistry.js'; -import { IThemeService } from '../../../../../platform/theme/common/themeService.js'; -import { IChatAgentCommand, IChatAgentData, IChatAgentService } from '../../common/chatAgents.js'; -import { chatSlashCommandBackground, chatSlashCommandForeground } from '../../common/chatColors.js'; -import { ChatRequestAgentPart, ChatRequestAgentSubcommandPart, ChatRequestSlashCommandPart, ChatRequestTextPart, ChatRequestToolPart, IParsedChatRequestPart, chatAgentLeader, chatSubcommandLeader } from '../../common/chatParserTypes.js'; -import { ChatRequestParser } from '../../common/chatRequestParser.js'; -import { IChatWidget } from '../chat.js'; -import { ChatWidget } from '../chatWidget.js'; -import { dynamicVariableDecorationType } from './chatDynamicVariables.js'; +import { raceCancellation } from 'vs/base/common/async'; +import { CancellationToken } from 'vs/base/common/cancellation'; +import { Disposable, MutableDisposable } from 'vs/base/common/lifecycle'; +import { ICodeEditorService } from 'vs/editor/browser/services/codeEditorService'; +import { Position } from 'vs/editor/common/core/position'; +import { Range } from 'vs/editor/common/core/range'; +import { IWordAtPosition, getWordAtText } from 'vs/editor/common/core/wordHelper'; +import { IDecorationOptions, IEditorDecorationsCollection } from 'vs/editor/common/editorCommon'; +import { CompletionContext, CompletionItem, CompletionItemKind, CompletionList } from 'vs/editor/common/languages'; +import { IModelDeltaDecoration, ITextModel } from 'vs/editor/common/model'; +import { ILanguageFeaturesService } from 'vs/editor/common/services/languageFeatures'; +import { localize } from 'vs/nls'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { IProductService } from 'vs/platform/product/common/productService'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { inputPlaceholderForeground } from 'vs/platform/theme/common/colorRegistry'; +import { IThemeService } from 'vs/platform/theme/common/themeService'; +import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from 'vs/workbench/common/contributions'; +import { SubmitAction } from 'vs/workbench/contrib/chat/browser/actions/chatExecuteActions'; +import { IChatWidget, IChatWidgetService } from 'vs/workbench/contrib/chat/browser/chat'; +import { ChatInputPart } from 'vs/workbench/contrib/chat/browser/chatInputPart'; +import { ChatWidget } from 'vs/workbench/contrib/chat/browser/chatWidget'; +import { SelectAndInsertFileAction, dynamicVariableDecorationType } from 'vs/workbench/contrib/chat/browser/contrib/chatDynamicVariables'; +import { IChatAgentCommand, IChatAgentData, IChatAgentService } from 'vs/workbench/contrib/chat/common/chatAgents'; +import { chatSlashCommandBackground, chatSlashCommandForeground } from 'vs/workbench/contrib/chat/common/chatColors'; +import { ChatRequestAgentPart, ChatRequestAgentSubcommandPart, ChatRequestSlashCommandPart, ChatRequestTextPart, ChatRequestVariablePart, IParsedChatRequestPart, chatAgentLeader, chatSubcommandLeader, chatVariableLeader } from 'vs/workbench/contrib/chat/common/chatParserTypes'; +import { ChatRequestParser } from 'vs/workbench/contrib/chat/common/chatRequestParser'; +import { IChatService } from 'vs/workbench/contrib/chat/common/chatService'; +import { IChatSlashCommandService } from 'vs/workbench/contrib/chat/common/chatSlashCommands'; +import { IChatVariablesService } from 'vs/workbench/contrib/chat/common/chatVariables'; +import { isResponseVM } from 'vs/workbench/contrib/chat/common/chatViewModel'; +import { LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; const decorationDescription = 'chat'; const placeholderDecorationType = 'chat-session-detail'; @@ -36,6 +54,8 @@ class InputEditorDecorations extends Disposable { private readonly viewModelDisposables = this._register(new MutableDisposable()); + private readonly placeholderDecorations: IEditorDecorationsCollection; + constructor( private readonly widget: IChatWidget, @ICodeEditorService private readonly codeEditorService: ICodeEditorService, @@ -45,6 +65,7 @@ class InputEditorDecorations extends Disposable { super(); this.codeEditorService.registerDecorationType(decorationDescription, placeholderDecorationType, {}); + this.placeholderDecorations = this.widget.inputEditor.createDecorationsCollection(); this._register(this.themeService.onDidColorThemeChange(() => this.updateRegisteredDecorationTypes())); this.updateRegisteredDecorationTypes(); @@ -111,127 +132,139 @@ class InputEditorDecorations extends Disposable { return; } - if (!inputValue) { - const defaultAgent = this.chatAgentService.getDefaultAgent(this.widget.location); - const decoration: IDecorationOptions[] = [ - { - range: { - startLineNumber: 1, - endLineNumber: 1, - startColumn: 1, - endColumn: 1000 + // if (!inputValue) { + const viewModelPlaceholder = this.widget.viewModel?.inputPlaceholder; + const placeholder = viewModelPlaceholder ?? ''; + const decoration: IModelDeltaDecoration[] = [ + { + range: { + startLineNumber: 1, + endLineNumber: 1, + startColumn: 1, + endColumn: 1000 + }, + options: { + description: decorationDescription, + after: { + content: placeholder, + inlineClassName: 'chat-input-placeholder' }, - renderOptions: { - after: { - contentText: viewModel.inputPlaceholder || (defaultAgent?.description ?? ''), - color: this.getPlaceholderColor() - } - } } - ]; - this.widget.inputEditor.setDecorationsByType(decorationDescription, placeholderDecorationType, decoration); - return; - } - - const parsedRequest = this.widget.parsedInput.parts; - - let placeholderDecoration: IDecorationOptions[] | undefined; - const agentPart = parsedRequest.find((p): p is ChatRequestAgentPart => p instanceof ChatRequestAgentPart); - const agentSubcommandPart = parsedRequest.find((p): p is ChatRequestAgentSubcommandPart => p instanceof ChatRequestAgentSubcommandPart); - const slashCommandPart = parsedRequest.find((p): p is ChatRequestSlashCommandPart => p instanceof ChatRequestSlashCommandPart); - - const exactlyOneSpaceAfterPart = (part: IParsedChatRequestPart): boolean => { - const partIdx = parsedRequest.indexOf(part); - if (parsedRequest.length > partIdx + 2) { - return false; - } - - const nextPart = parsedRequest[partIdx + 1]; - return nextPart && nextPart instanceof ChatRequestTextPart && nextPart.text === ' '; - }; - - const getRangeForPlaceholder = (part: IParsedChatRequestPart) => ({ - startLineNumber: part.editorRange.startLineNumber, - endLineNumber: part.editorRange.endLineNumber, - startColumn: part.editorRange.endColumn + 1, - endColumn: 1000 - }); - - const onlyAgentAndWhitespace = agentPart && parsedRequest.every(p => p instanceof ChatRequestTextPart && !p.text.trim().length || p instanceof ChatRequestAgentPart); - if (onlyAgentAndWhitespace) { - // Agent reference with no other text - show the placeholder - const isFollowupSlashCommand = this.previouslyUsedAgents.has(agentAndCommandToKey(agentPart.agent, undefined)); - const shouldRenderFollowupPlaceholder = isFollowupSlashCommand && agentPart.agent.metadata.followupPlaceholder; - if (agentPart.agent.description && exactlyOneSpaceAfterPart(agentPart)) { - placeholderDecoration = [{ - range: getRangeForPlaceholder(agentPart), - renderOptions: { - after: { - contentText: shouldRenderFollowupPlaceholder ? agentPart.agent.metadata.followupPlaceholder : agentPart.agent.description, - color: this.getPlaceholderColor(), - } - } - }]; - } - } - - const onlyAgentAndAgentCommandAndWhitespace = agentPart && agentSubcommandPart && parsedRequest.every(p => p instanceof ChatRequestTextPart && !p.text.trim().length || p instanceof ChatRequestAgentPart || p instanceof ChatRequestAgentSubcommandPart); - if (onlyAgentAndAgentCommandAndWhitespace) { - // Agent reference and subcommand with no other text - show the placeholder - const isFollowupSlashCommand = this.previouslyUsedAgents.has(agentAndCommandToKey(agentPart.agent, agentSubcommandPart.command.name)); - const shouldRenderFollowupPlaceholder = isFollowupSlashCommand && agentSubcommandPart.command.followupPlaceholder; - if (agentSubcommandPart?.command.description && exactlyOneSpaceAfterPart(agentSubcommandPart)) { - placeholderDecoration = [{ - range: getRangeForPlaceholder(agentSubcommandPart), - renderOptions: { - after: { - contentText: shouldRenderFollowupPlaceholder ? agentSubcommandPart.command.followupPlaceholder : agentSubcommandPart.command.description, - color: this.getPlaceholderColor(), - } - } - }]; - } - } - - const onlyAgentCommandAndWhitespace = agentSubcommandPart && parsedRequest.every(p => p instanceof ChatRequestTextPart && !p.text.trim().length || p instanceof ChatRequestAgentSubcommandPart); - if (onlyAgentCommandAndWhitespace) { - // Agent subcommand with no other text - show the placeholder - if (agentSubcommandPart?.command.description && exactlyOneSpaceAfterPart(agentSubcommandPart)) { - placeholderDecoration = [{ - range: getRangeForPlaceholder(agentSubcommandPart), - renderOptions: { - after: { - contentText: agentSubcommandPart.command.description, - color: this.getPlaceholderColor(), - } - } - }]; } - } - - this.widget.inputEditor.setDecorationsByType(decorationDescription, placeholderDecorationType, placeholderDecoration ?? []); - - const textDecorations: IDecorationOptions[] | undefined = []; - if (agentPart) { - textDecorations.push({ range: agentPart.editorRange }); - } - if (agentSubcommandPart) { - textDecorations.push({ range: agentSubcommandPart.editorRange, hoverMessage: new MarkdownString(agentSubcommandPart.command.description) }); - } - - if (slashCommandPart) { - textDecorations.push({ range: slashCommandPart.editorRange }); - } - - this.widget.inputEditor.setDecorationsByType(decorationDescription, slashCommandTextDecorationType, textDecorations); - - const varDecorations: IDecorationOptions[] = []; - const toolParts = parsedRequest.filter((p): p is ChatRequestToolPart => p instanceof ChatRequestToolPart); - for (const tool of toolParts) { - varDecorations.push({ range: tool.editorRange }); - } - - this.widget.inputEditor.setDecorationsByType(decorationDescription, variableTextDecorationType, varDecorations); + ]; + this.placeholderDecorations.set(decoration); + return; + // } + + // const parsedRequest = (await this.instantiationService.createInstance(ChatRequestParser).parseChatRequest(viewModel.sessionId, inputValue)).parts; + + // let placeholderDecoration: IModelDeltaDecoration[] | undefined; + // const agentPart = parsedRequest.find((p): p is ChatRequestAgentPart => p instanceof ChatRequestAgentPart); + // const agentSubcommandPart = parsedRequest.find((p): p is ChatRequestAgentSubcommandPart => p instanceof ChatRequestAgentSubcommandPart); + // const slashCommandPart = parsedRequest.find((p): p is ChatRequestSlashCommandPart => p instanceof ChatRequestSlashCommandPart); + + // const exactlyOneSpaceAfterPart = (part: IParsedChatRequestPart): boolean => { + // const partIdx = parsedRequest.indexOf(part); + // if (parsedRequest.length > partIdx + 2) { + // return false; + // } + + // const nextPart = parsedRequest[partIdx + 1]; + // return nextPart && nextPart instanceof ChatRequestTextPart && nextPart.text === ' '; + // }; + + // const onlyAgentAndWhitespace = agentPart && parsedRequest.every(p => p instanceof ChatRequestTextPart && !p.text.trim().length || p instanceof ChatRequestAgentPart); + // if (onlyAgentAndWhitespace) { + // // Agent reference with no other text - show the placeholder + // if (agentPart.agent.metadata.description && exactlyOneSpaceAfterPart(agentPart)) { + // placeholderDecoration = [{ + // range: { + // startLineNumber: agentPart.editorRange.startLineNumber, + // endLineNumber: agentPart.editorRange.endLineNumber, + // startColumn: agentPart.editorRange.endColumn + 1, + // endColumn: 1000 + // }, + // options: { + // description: decorationDescription, + // after: { + // content: agentPart.agent.metadata.description, + // inlineClassName: 'chat-input-placeholder' + // } + // } + // }]; + // } + // } + + // const onlyAgentCommandAndWhitespace = agentPart && agentSubcommandPart && parsedRequest.every(p => p instanceof ChatRequestTextPart && !p.text.trim().length || p instanceof ChatRequestAgentPart || p instanceof ChatRequestAgentSubcommandPart); + // if (onlyAgentCommandAndWhitespace) { + // // Agent reference and subcommand with no other text - show the placeholder + // const isFollowupSlashCommand = this.previouslyUsedAgents.has(agentAndCommandToKey(agentPart.agent.id, agentSubcommandPart.command.name)); + // const shouldRenderFollowupPlaceholder = isFollowupSlashCommand && agentSubcommandPart.command.followupPlaceholder; + // if (agentSubcommandPart?.command.description && exactlyOneSpaceAfterPart(agentSubcommandPart)) { + // placeholderDecoration = [{ + // range: { + // startLineNumber: agentSubcommandPart.editorRange.startLineNumber, + // endLineNumber: agentSubcommandPart.editorRange.endLineNumber, + // startColumn: agentSubcommandPart.editorRange.endColumn + 1, + // endColumn: 1000 + // }, + // options: { + // description: decorationDescription, + // after: { + // content: (shouldRenderFollowupPlaceholder ? agentSubcommandPart.command.followupPlaceholder : agentSubcommandPart.command.description) ?? '', + // inlineClassName: 'chat-input-placeholder' + // } + // } + // }]; + // } + // } + + // const onlySlashCommandAndWhitespace = slashCommandPart && parsedRequest.every(p => p instanceof ChatRequestTextPart && !p.text.trim().length || p instanceof ChatRequestSlashCommandPart); + // if (onlySlashCommandAndWhitespace) { + // // Command reference with no other text - show the placeholder + // if (slashCommandPart.slashCommand.detail && exactlyOneSpaceAfterPart(slashCommandPart)) { + // placeholderDecoration = [{ + // range: { + // startLineNumber: slashCommandPart.editorRange.startLineNumber, + // endLineNumber: slashCommandPart.editorRange.endLineNumber, + // startColumn: slashCommandPart.editorRange.endColumn + 1, + // endColumn: 1000 + // }, + // options: { + // description: decorationDescription, + // after: { + // content: slashCommandPart.slashCommand.detail, + // inlineClassName: 'chat-input-placeholder' + // } + // } + // }]; + // } + // } + + // // this.widget.inputEditor.setDecorationsByType(decorationDescription, placeholderDecorationType, placeholderDecoration ?? []); + // // this.placeholderDecorations.set(placeholderDecoration ?? []); + + // const textDecorations: IDecorationOptions[] | undefined = []; + // if (agentPart) { + // textDecorations.push({ range: agentPart.editorRange }); + // if (agentSubcommandPart) { + // textDecorations.push({ range: agentSubcommandPart.editorRange }); + // } + // } + + // if (slashCommandPart) { + // textDecorations.push({ range: slashCommandPart.editorRange }); + // } + + // // this.widget.inputEditor.setDecorationsByType(decorationDescription, slashCommandTextDecorationType, textDecorations); + + // const varDecorations: IDecorationOptions[] = []; + // const variableParts = parsedRequest.filter((p): p is ChatRequestVariablePart => p instanceof ChatRequestVariablePart); + // for (const variable of variableParts) { + // varDecorations.push({ range: variable.editorRange }); + // } + + // this.widget.inputEditor.setDecorationsByType(decorationDescription, variableTextDecorationType, varDecorations); } } @@ -301,7 +334,7 @@ class ChatTokenDeleter extends Disposable { // If this was a simple delete, try to find out whether it was inside a token if (!change.text && this.widget.viewModel) { - const previousParsedValue = parser.parseChatRequest(this.widget.viewModel.sessionId, previousInputValue, widget.location, { selectedAgent: previousSelectedAgent }); + const previousParsedValue = parser.parseChatRequest(this.widget.viewModel.sessionId, previousInputValue, widget.location, { selectedAgent: previousSelectedAgent, mode: this.widget.input.currentMode }); // For dynamic variables, this has to happen in ChatDynamicVariableModel with the other bookkeeping const deletableTokens = previousParsedValue.parts.filter(p => p instanceof ChatRequestAgentPart || p instanceof ChatRequestAgentSubcommandPart || p instanceof ChatRequestSlashCommandPart || p instanceof ChatRequestToolPart); diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatInputRelatedFilesContrib.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatInputRelatedFilesContrib.ts index 3860b5d5efa5..44c4d6d89465 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatInputRelatedFilesContrib.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatInputRelatedFilesContrib.ts @@ -4,14 +4,15 @@ *--------------------------------------------------------------------------------------------*/ import { CancellationToken } from '../../../../../base/common/cancellation.js'; -import { Event } from '../../../../../base/common/event.js'; +import { Emitter, Event } from '../../../../../base/common/event.js'; import { Disposable, DisposableStore } from '../../../../../base/common/lifecycle.js'; import { ResourceMap, ResourceSet } from '../../../../../base/common/map.js'; import { autorun } from '../../../../../base/common/observable.js'; +import { isEqual } from '../../../../../base/common/resources.js'; import { URI } from '../../../../../base/common/uri.js'; import { localize } from '../../../../../nls.js'; import { IWorkbenchContribution } from '../../../../common/contributions.js'; -import { IChatEditingService, IChatEditingSession, WorkingSetEntryRemovalReason, WorkingSetEntryState } from '../../common/chatEditingService.js'; +import { IChatEditingService, IChatEditingSession } from '../../common/chatEditingService.js'; import { IChatWidget, IChatWidgetService } from '../chat.js'; export class ChatRelatedFilesContribution extends Disposable implements IWorkbenchContribution { @@ -22,7 +23,7 @@ export class ChatRelatedFilesContribution extends Disposable implements IWorkben constructor( @IChatEditingService private readonly chatEditingService: IChatEditingService, - @IChatWidgetService private readonly chatWidgetService: IChatWidgetService + @IChatWidgetService private readonly chatWidgetService: IChatWidgetService, ) { super(); @@ -50,7 +51,7 @@ export class ChatRelatedFilesContribution extends Disposable implements IWorkben this._currentRelatedFilesRetrievalOperation = this.chatEditingService.getRelatedFiles(currentEditingSession.chatSessionId, widget.getInput(), widget.attachmentModel.fileAttachments, CancellationToken.None) .then((files) => { - if (!files?.length || !widget.viewModel?.sessionId) { + if (!files?.length || !widget.viewModel?.sessionId || !widget.input.relatedFiles) { return; } @@ -59,13 +60,13 @@ export class ChatRelatedFilesContribution extends Disposable implements IWorkben return; // Might have disposed while we were calculating } - const existingFiles = new ResourceSet(widget.attachmentModel.fileAttachments); + const existingFiles = new ResourceSet([...widget.attachmentModel.fileAttachments, ...widget.input.relatedFiles.removedFiles]); if (!existingFiles.size) { return; } // Pick up to 2 related files - const newSuggestions = new ResourceMap<{ description: string; group: string }>(); + const newSuggestions = new ResourceMap(); for (const group of files) { for (const file of group.files) { if (newSuggestions.size >= 2) { @@ -74,24 +75,12 @@ export class ChatRelatedFilesContribution extends Disposable implements IWorkben if (existingFiles.has(file.uri)) { continue; } - newSuggestions.set(file.uri, { group: group.group, description: file.description }); + newSuggestions.set(file.uri, localize('relatedFile', "{0} (Suggested)", file.description)); existingFiles.add(file.uri); } } - // Remove the existing related file suggestions from the working set - const existingSuggestedEntriesToRemove: URI[] = []; - for (const entry of currentEditingSession.workingSet) { - if (entry[1].state === WorkingSetEntryState.Suggested && !newSuggestions.has(entry[0])) { - existingSuggestedEntriesToRemove.push(entry[0]); - } - } - currentEditingSession?.remove(WorkingSetEntryRemovalReason.Programmatic, ...existingSuggestedEntriesToRemove); - - // Add the new related file suggestions to the working set - for (const [uri, data] of newSuggestions) { - currentEditingSession.addFileToWorkingSet(uri, localize('relatedFile', "{0} (Suggested)", data.description), WorkingSetEntryState.Suggested); - } + widget.input.relatedFiles.value = [...newSuggestions.entries()].map(([uri, description]) => ({ uri, description })); }) .finally(() => { this._currentRelatedFilesRetrievalOperation = undefined; @@ -115,6 +104,10 @@ export class ChatRelatedFilesContribution extends Disposable implements IWorkben disposableStore.add(currentEditingSession.onDidDispose(() => { disposableStore.dispose(); })); + disposableStore.add(widget.onDidAcceptInput(() => { + widget.input.relatedFiles?.clear(); + this._updateRelatedFileSuggestions(currentEditingSession, widget); + })); this.chatEditingSessionDisposables.set(currentEditingSession.chatSessionId, disposableStore); } @@ -125,3 +118,44 @@ export class ChatRelatedFilesContribution extends Disposable implements IWorkben super.dispose(); } } + +export interface IChatRelatedFile { + uri: URI; + description: string; +} +export class ChatRelatedFiles extends Disposable { + + private readonly _onDidChange = this._register(new Emitter()); + readonly onDidChange: Event = this._onDidChange.event; + + private _removedFiles = new ResourceSet(); + get removedFiles() { + return this._removedFiles; + } + + private _value: IChatRelatedFile[] = []; + get value() { + return this._value; + } + + set value(value: IChatRelatedFile[]) { + this._value = value; + this._onDidChange.fire(); + } + + remove(uri: URI) { + this._value = this._value.filter(file => !isEqual(file.uri, uri)); + this._removedFiles.add(uri); + this._onDidChange.fire(); + } + + clearRemovedFiles() { + this._removedFiles.clear(); + } + + clear() { + this._value = []; + this._removedFiles.clear(); + this._onDidChange.fire(); + } +} diff --git a/src/vs/workbench/contrib/chat/browser/imageUtils.ts b/src/vs/workbench/contrib/chat/browser/imageUtils.ts index 20114790df86..b5f426737fd6 100644 --- a/src/vs/workbench/contrib/chat/browser/imageUtils.ts +++ b/src/vs/workbench/contrib/chat/browser/imageUtils.ts @@ -11,7 +11,12 @@ * @returns A promise that resolves to the UInt8Array string of the resized image. */ -export async function resizeImage(data: Uint8Array): Promise { +export async function resizeImage(data: Uint8Array | string): Promise { + + if (typeof data === 'string') { + data = convertStringToUInt8Array(data); + } + const blob = new Blob([data]); const img = new Image(); const url = URL.createObjectURL(blob); @@ -22,7 +27,7 @@ export async function resizeImage(data: Uint8Array): Promise { URL.revokeObjectURL(url); let { width, height } = img; - if (width < 768 || height < 768) { + if (width <= 768 || height <= 768) { resolve(data); return; } @@ -66,3 +71,34 @@ export async function resizeImage(data: Uint8Array): Promise { }; }); } + +export function convertStringToUInt8Array(data: string): Uint8Array { + const base64Data = data.includes(',') ? data.split(',')[1] : data; + if (isValidBase64(base64Data)) { + return Uint8Array.from(atob(base64Data), char => char.charCodeAt(0)); + } + return new TextEncoder().encode(data); +} + +// Only used for URLs +export function convertUint8ArrayToString(data: Uint8Array): string { + try { + const decoder = new TextDecoder(); + const decodedString = decoder.decode(data); + return decodedString; + } catch { + return ''; + } +} + +function isValidBase64(str: string): boolean { + // checks if the string is a valid base64 string that is NOT encoded + return /^[A-Za-z0-9+/]*={0,2}$/.test(str) && (() => { + try { + atob(str); + return true; + } catch { + return false; + } + })(); +} diff --git a/src/vs/workbench/contrib/chat/browser/languageModelToolsService.ts b/src/vs/workbench/contrib/chat/browser/languageModelToolsService.ts index 2ed4ea4bbe8f..253d448671d4 100644 --- a/src/vs/workbench/contrib/chat/browser/languageModelToolsService.ts +++ b/src/vs/workbench/contrib/chat/browser/languageModelToolsService.ts @@ -10,11 +10,12 @@ import { CancellationError, isCancellationError } from '../../../../base/common/ import { Emitter } from '../../../../base/common/event.js'; import { Iterable } from '../../../../base/common/iterator.js'; import { Disposable, DisposableStore, dispose, IDisposable, toDisposable } from '../../../../base/common/lifecycle.js'; -import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; +import { IContextKey, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js'; import { ILogService } from '../../../../platform/log/common/log.js'; import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; import { IExtensionService } from '../../../services/extensions/common/extensions.js'; +import { ChatContextKeys } from '../common/chatContextKeys.js'; import { ChatModel } from '../common/chatModel.js'; import { ChatToolInvocation } from '../common/chatProgressTypes/chatToolInvocation.js'; import { IChatService } from '../common/chatService.js'; @@ -36,7 +37,7 @@ export class LanguageModelToolsService extends Disposable implements ILanguageMo private _tools = new Map(); private _toolContextKeys = new Set(); - + private readonly _ctxToolsCount: IContextKey; private _callsByRequestId = new Map(); @@ -56,6 +57,8 @@ export class LanguageModelToolsService extends Disposable implements ILanguageMo this._onDidChangeToolsScheduler.schedule(); } })); + + this._ctxToolsCount = ChatContextKeys.Tools.toolsCount.bindTo(_contextKeyService); } registerToolData(toolData: IToolData): IDisposable { @@ -64,12 +67,14 @@ export class LanguageModelToolsService extends Disposable implements ILanguageMo } this._tools.set(toolData.id, { data: toolData }); + this._ctxToolsCount.set(this._tools.size); this._onDidChangeToolsScheduler.schedule(); toolData.when?.keys().forEach(key => this._toolContextKeys.add(key)); return toDisposable(() => { this._tools.delete(toolData.id); + this._ctxToolsCount.set(this._tools.size); this._refreshAllToolContextKeys(); this._onDidChangeToolsScheduler.schedule(); }); @@ -191,6 +196,8 @@ export class LanguageModelToolsService extends Disposable implements ILanguageMo if (!userConfirmed) { throw new CancellationError(); } + + dto.toolSpecificData = toolInvocation?.toolSpecificData; } } else { const prepared = tool.impl.prepareToolInvocation ? @@ -265,6 +272,7 @@ export class LanguageModelToolsService extends Disposable implements ILanguageMo super.dispose(); this._callsByRequestId.forEach(calls => dispose(calls)); + this._ctxToolsCount.reset(); } } diff --git a/src/vs/workbench/contrib/chat/browser/media/chat.css b/src/vs/workbench/contrib/chat/browser/media/chat.css index e9dd2540fca4..c404e9447ee4 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chat.css +++ b/src/vs/workbench/contrib/chat/browser/media/chat.css @@ -13,6 +13,10 @@ display: none !important; } +.interactive-session .interactive-input-part .chat-input-placeholder { + color: var(--vscode-input-placeholderForeground, red); +} + .interactive-item-container { padding: 12px 16px; display: flex; @@ -370,8 +374,8 @@ margin: 0 0 6px 0; } - .progress-container { - padding-top: 2px; + .progress-container .rendered-markdown [data-code] { + margin: 0; } } @@ -427,6 +431,7 @@ have to be updated for changes to the rules above, or to support more deeply nes background-color: var(--vscode-textPreformat-background); padding: 1px 3px; border-radius: 4px; + white-space: pre-wrap; } } @@ -434,11 +439,6 @@ have to be updated for changes to the rules above, or to support more deeply nes padding: 8px 20px; } -.interactive-item-container.interactive-item-compact.no-padding { - padding: unset; - gap: unset; -} - .interactive-item-container.interactive-item-compact .header { height: 16px; } @@ -568,11 +568,12 @@ have to be updated for changes to the rules above, or to support more deeply nes } .interactive-session .chat-editing-session .chat-editing-session-container { - margin-bottom: -14px; + margin-bottom: -13px; padding: 6px 8px 18px 8px; box-sizing: border-box; background-color: var(--vscode-editor-background); border: 1px solid var(--vscode-input-border, transparent); + border-bottom: none; border-radius: 4px; display: flex; flex-direction: column; @@ -685,11 +686,20 @@ have to be updated for changes to the rules above, or to support more deeply nes justify-content: start; } +.chat-attachments-container { + display: flex; + flex-direction: row; + gap: 4px; + margin-top: 6px; + flex-wrap: wrap; +} + .chat-related-files { - padding: 4px 0 0 0; display: flex; flex-wrap: wrap; align-items: center; + gap: 4px; + max-width: 100%; } .chat-related-files .monaco-button-dropdown .monaco-text-button { @@ -698,6 +708,7 @@ have to be updated for changes to the rules above, or to support more deeply nes width: fit-content; padding: 0px; border: none; + height: 18px; } .chat-related-files .monaco-button-dropdown .monaco-text-button span { @@ -710,22 +721,28 @@ have to be updated for changes to the rules above, or to support more deeply nes border-radius: 4px; height: 18px; border: 1px solid var(--vscode-input-border); + border-style: dashed; align-items: center; overflow: hidden; - padding: 0 0 0 2px; + gap: 2px; + padding: 0 4px; } .chat-related-files .monaco-button.codicon.codicon-add { display: flex; + flex-direction: column; color: var(--vscode-descriptionForeground); - padding-right: 4px; - padding-left: 4px; padding-top: 3px; - height: 100%; + margin-left: -4px; + padding-left: 4px; + font-size: 14px; /* The + codicon is large, make it look more like the x codicon */ + height: calc(100% - 3px); + width: 17px; + outline-offset: -2px !important; } .interactive-session .chat-related-files .monaco-icon-label::before { - padding: 2px 2px 0 0; + padding: 4px 3px 0 2px; } .interactive-session .chat-editing-session .chat-related-files .monaco-button.secondary:first-child { @@ -846,13 +863,28 @@ have to be updated for changes to the rules above, or to support more deeply nes display: flex; } -.interactive-session .chat-input-toolbars :first-child { - margin-right: auto; +@container chat-input-container (max-width: 130px) { + .interactive-session .chat-input-toolbars .chat-modelPicker-item { + /* Hides modelpicker when the container width is 130px or less */ + display: none; + } +} + +.interactive-session .interactive-input-part:not(.compact) .chat-input-toolbars > .chat-execute-toolbar { + container-type: inline-size; + container-name: chat-input-container; + flex: 1; + display: flex; + justify-content: flex-end; } .interactive-session .chat-input-toolbars > .chat-execute-toolbar { min-width: 0px; + .monaco-action-bar { + min-width: 0px; + } + .chat-modelPicker-item { min-width: 0px; @@ -875,7 +907,7 @@ have to be updated for changes to the rules above, or to support more deeply nes color: var(--vscode-icon-foreground) !important; } -.interactive-session .chat-input-toolbars .chat-modelPicker-item .action-label { +.interactive-session .chat-input-toolbars .chat-dropdown-item .action-label { height: 16px; padding: 3px 0px 3px 6px; display: flex; @@ -883,7 +915,7 @@ have to be updated for changes to the rules above, or to support more deeply nes } -.interactive-session .chat-input-toolbars .chat-modelPicker-item .action-label .codicon-chevron-down { +.interactive-session .chat-input-toolbars .chat-dropdown-item .action-label .codicon-chevron-down { font-size: 12px; margin-left: 2px; } @@ -1001,60 +1033,46 @@ have to be updated for changes to the rules above, or to support more deeply nes } /** - * Styles for the `prompt instructions` attachment widget. + * Styles for the `reusable prompts` attachment widget. */ -.chat-attached-context .chat-prompt-instructions-attachments { - display: flex; - flex-direction: column; - align-items: flex-start; - gap: 2px; -} -.chat-attached-context .chat-prompt-instructions-attachment { +.chat-attached-context .chat-prompt-attachment { display: flex; gap: 4px; } -.chat-attached-context .chat-prompt-instructions-attachment .codicon { +.chat-attached-context .chat-prompt-attachment .codicon { color: inherit; text-decoration: none; } -.chat-attached-context .chat-prompt-instructions-attachment .chat-implicit-hint { +.chat-attached-context .chat-prompt-attachment .chat-implicit-hint { opacity: 0.7; font-size: .9em; margin-top: -0.5px; } -.chat-attached-context .chat-prompt-instructions-attachment.warning { +.chat-attached-context .chat-prompt-attachment.warning { color: var(--vscode-notificationsWarningIcon-foreground); } -.chat-attached-context .chat-prompt-instructions-attachment.error { +.chat-attached-context .chat-prompt-attachment.error { color: var(--vscode-notificationsErrorIcon-foreground); } -.chat-attached-context .chat-prompt-instructions-attachment.disabled { +.chat-attached-context .chat-prompt-attachment.disabled { border-style: dashed; opacity: 0.75; } -.chat-attached-context .chat-prompt-instructions-attachment .monaco-button { - border-left: 1px solid var(--vscode-chat-requestBorder, var(--vscode-input-background, transparent)); - margin-left: 3px; -} -.chat-attached-context .chat-prompt-instructions-attachment.error .monaco-button, -.chat-attached-context .chat-prompt-instructions-attachment.warning .monaco-button { - border-left-color: currentColor; -} -.chat-attached-context .chat-prompt-instructions-attachment:focus .monaco-button { +.chat-attached-context .chat-prompt-attachment:focus .monaco-button { border-color: var(--vscode-focusBorder); } -.chat-attached-context .chat-prompt-instructions-attachment .monaco-icon-label-container { +.chat-attached-context .chat-prompt-attachment .monaco-icon-label-container { margin-top: -0.1em; } /* * This overly-specific CSS selector is needed to beat priority of some * styles applied on the `.chat-attached-context-attachment` element. */ -.chat-attached-context .chat-prompt-instructions-attachments .chat-prompt-instructions-attachment.error.implicit, -.chat-attached-context .chat-prompt-instructions-attachments .chat-prompt-instructions-attachment.warning.implicit { +.chat-attached-context .chat-prompt-attachment.error.implicit, +.chat-attached-context .chat-prompt-attachment.warning.implicit { border: 1px solid currentColor; } -.chat-attached-context .chat-prompt-instructions-attachments .chat-prompt-instructions-attachment.implicit .monaco-button { +.chat-attached-context .chat-prompt-attachment.implicit .monaco-button { padding-left: 2px; padding-right: 2px; } @@ -1062,23 +1080,23 @@ have to be updated for changes to the rules above, or to support more deeply nes * If in one of the non-normal states, make sure the `main icon` of * the component has the same color as the component itself */ -.chat-attached-context .chat-prompt-instructions-attachment.error .monaco-icon-label::before, -.chat-attached-context .chat-prompt-instructions-attachment.warning .monaco-icon-label::before, -.chat-attached-context .chat-prompt-instructions-attachment.disabled .monaco-icon-label::before { +.chat-attached-context .chat-prompt-attachment.error .monaco-icon-label::before, +.chat-attached-context .chat-prompt-attachment.warning .monaco-icon-label::before, +.chat-attached-context .chat-prompt-attachment.disabled .monaco-icon-label::before { color: inherit; } -.chat-attached-context .chat-prompt-instructions-attachment.disabled .monaco-icon-label::before { +.chat-attached-context .chat-prompt-attachment.disabled .monaco-icon-label::before { font-style: italic; } -.chat-attached-context .chat-prompt-instructions-attachment.disabled:hover { +.chat-attached-context .chat-prompt-attachment.disabled:hover { opacity: 1; } -.chat-attached-context .chat-prompt-instructions-attachment.disabled .chat-implicit-hint, -.chat-attached-context .chat-prompt-instructions-attachment.disabled .label-name { +.chat-attached-context .chat-prompt-attachment.disabled .chat-implicit-hint, +.chat-attached-context .chat-prompt-attachment.disabled .label-name { font-style: italic; text-decoration: line-through; } -.chat-attached-context .chat-prompt-instructions-attachment.disabled:focus { +.chat-attached-context .chat-prompt-attachment.disabled:focus { outline: none; border-color: var(--vscode-focusBorder); } @@ -1111,6 +1129,21 @@ have to be updated for changes to the rules above, or to support more deeply nes padding: 8px 0 0 0 } +.chat-attachment-toolbar .action-item:not(:last-child) { + margin-right: 4px; +} + +.action-item.chat-attached-context-attachment.chat-add-files { + height: 20px; + color: var(--vscode-descriptionForeground); +} + +.action-item.chat-attached-context-attachment.chat-add-files span.keybinding { + display: none; +} + +.action-item.chat-mcp .action-label, +.action-item.chat-attached-context-attachment.chat-add-files .action-label, .interactive-session .chat-attached-context .chat-attached-context-attachment { display: flex; gap: 2px; @@ -1124,10 +1157,58 @@ have to be updated for changes to the rules above, or to support more deeply nes width: fit-content; } -.interactive-session .chat-attached-context .chat-attached-context-attachment.chat-add-files { - height: 20px; - gap: 0px; +.action-item.chat-attached-context-attachment.chat-add-files .action-label { color: var(--vscode-descriptionForeground); + font-family: unset; + gap: 5px; +} + +.action-item.chat-mcp { + display: flex !important; + + &.chat-mcp-has-action .action-label { + border-top-right-radius: 0; + border-bottom-right-radius: 0; + border-right: 0; + } + + .chat-mcp-action { + align-self: stretch; + padding: 0 2px; + border-radius: 0; + outline: 0; + border: 0; + border-top-right-radius: 4px; + border-bottom-right-radius: 4px; + background: var(--vscode-button-background); + cursor: pointer; + + .codicon { + width: fit-content; + color: var(--vscode-button-foreground); + } + + .codicon::before { + font-size: 14px; + } + + &.chat-mcp-action-error { + background: var(--vscode-activityErrorBadge-background); + + .codicon { + color: var(--vscode-activityErrorBadge-foreground); + } + } + } +} + +.quick-input-list .quick-input-list-rows > .quick-input-list-row .monaco-icon-label.tool-pick .codicon[class*='codicon-'] { + font-size: 14px; +} + + +.action-item.chat-attached-context-attachment.chat-add-files .action-label.codicon::before { + font: normal normal normal 16px/1 codicon; } .interactive-session .chat-attached-context .chat-attached-context-attachment .monaco-button { @@ -1142,7 +1223,7 @@ have to be updated for changes to the rules above, or to support more deeply nes } .chat-related-files .monaco-button.codicon.codicon-add:hover, -.interactive-session .chat-attached-context .chat-attached-context-attachment.chat-add-files:hover, +.action-item.chat-attached-context-attachment.chat-add-files:hover, .interactive-session .chat-attached-context .chat-attached-context-attachment .monaco-button:hover { cursor: pointer; background: var(--vscode-toolbar-hoverBackground); @@ -1158,8 +1239,11 @@ have to be updated for changes to the rules above, or to support more deeply nes } .interactive-session .chat-attached-context .chat-attached-context-attachment .monaco-icon-label-container .monaco-highlighted-label { - display: flex !important; + display: block !important; align-items: center !important; + overflow: hidden; + white-space: nowrap; + text-overflow: ellipsis; } .interactive-session .chat-attached-context .chat-attached-context-attachment .monaco-icon-label .monaco-button.codicon.codicon-close, @@ -1174,14 +1258,14 @@ have to be updated for changes to the rules above, or to support more deeply nes } .interactive-session .chat-attached-context { - padding: 5px 0 0 0; display: flex; flex-wrap: wrap; cursor: default; + gap: 4px; + max-width: 100%; } .interactive-session .interactive-input-part.compact .chat-attached-context { - padding-top: 8px; padding-bottom: 0px; display: flex; gap: 4px; @@ -1230,11 +1314,11 @@ have to be updated for changes to the rules above, or to support more deeply nes } .interactive-session .chat-attached-context .chat-attached-context-attachment .monaco-icon-label { - gap: 3px; + gap: 4px; } .interactive-session .chat-attached-context .chat-attached-context-attachment .monaco-icon-label::before { - height: 90%; + height: 1em; width: auto; padding: 0; line-height: 1em !important; @@ -1247,6 +1331,7 @@ have to be updated for changes to the rules above, or to support more deeply nes .interactive-session .chat-attached-context .chat-attached-context-attachment .monaco-icon-label.predefined-file-icon::before { padding: 0 0 0 2px; + align-content: center; } .interactive-session .interactive-item-container.interactive-request .chat-attached-context .chat-attached-context-attachment { @@ -1521,6 +1606,10 @@ have to be updated for changes to the rules above, or to support more deeply nes gap: 7px; margin: 0 0 6px 4px; + /* Tool calls transition from a progress to a collapsible list part, which needs to have this top padding. + The working progress also can be replaced by a tool progress part. So align this padding so the text doesn't appear to shift. */ + padding-top: 2px; + > .codicon[class*='codicon-'] { height: 12px; font-size: 12px; @@ -1576,7 +1665,7 @@ have to be updated for changes to the rules above, or to support more deeply nes padding: 4px 8px; } -.interactive-item-container .chat-confirmation-widget .rendered-markdown [data-code] { +.interactive-item-container .chat-confirmation-widget .interactive-result-code-block { margin-bottom: 8px; } @@ -1637,6 +1726,11 @@ have to be updated for changes to the rules above, or to support more deeply nes user-select: none; outline: none; border: none; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + max-width: 100%; + display: inline-block; } .chat-attached-context-attachment.show-file-icons.warning .chat-attached-context-custom-text { @@ -1701,3 +1795,7 @@ have to be updated for changes to the rules above, or to support more deeply nes } } } + +.hideSuggestTextIcons .suggest-widget .monaco-list .monaco-list-row .suggest-icon.codicon-symbol-text::before { + display: none; +} diff --git a/src/vs/workbench/contrib/chat/browser/media/chatEditingEditorOverlay.css b/src/vs/workbench/contrib/chat/browser/media/chatEditingEditorOverlay.css new file mode 100644 index 000000000000..9356b690797b --- /dev/null +++ b/src/vs/workbench/contrib/chat/browser/media/chatEditingEditorOverlay.css @@ -0,0 +1,138 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +.chat-editor-overlay-widget { + padding: 0px; + color: var(--vscode-button-foreground); + background-color: var(--vscode-button-background); + border-radius: 5px; + border: 1px solid var(--vscode-contrastBorder); + display: flex; + align-items: center; + z-index: 10; + box-shadow: 0 2px 8px var(--vscode-widget-shadow); + overflow: hidden; +} + +@keyframes pulse { + 0% { + box-shadow: 0 2px 8px 0 var(--vscode-widget-shadow); + } + 50% { + box-shadow: 0 2px 8px 4px var(--vscode-widget-shadow); + } + 100% { + box-shadow: 0 2px 8px 0 var(--vscode-widget-shadow); + } +} + +.chat-editor-overlay-widget.busy { + animation: pulse ease-in 2.3s infinite; +} + +.chat-editor-overlay-widget .chat-editor-overlay-progress { + align-items: center; + display: none; + padding: 0px 5px; + font-size: 12px; + font-variant-numeric: tabular-nums; + overflow: hidden; + white-space: nowrap; +} + +.chat-editor-overlay-widget.busy .chat-editor-overlay-progress { + display: inline-flex; +} + + +@keyframes ellipsis { + 0% { + content: ""; + } + 25% { + content: "."; + } + 50% { + content: ".."; + } + 75% { + content: "..."; + } + 100% { + content: ""; + } +} + +.chat-editor-overlay-widget.busy.paused .chat-editor-overlay-progress { + .codicon-loading { + display: none; + } +} + +.chat-editor-overlay-widget .action-item > .action-label { + padding: 5px; + font-size: 12px; +} + +.chat-editor-overlay-widget.busy .action-item > .action-label.busy::after { + content: ""; + display: inline-flex; + white-space: nowrap; + overflow: hidden; + width: 3ch; + animation: ellipsis steps(4, end) 1s infinite; +} + +.chat-editor-overlay-widget .action-item:first-child > .action-label { + padding-left: 7px; +} + +.chat-editor-overlay-widget .action-item:last-child > .action-label { + padding-right: 7px; +} + +.chat-editor-overlay-widget.busy .chat-editor-overlay-progress .codicon, +.chat-editor-overlay-widget .action-item > .action-label.codicon { + color: var(--vscode-button-foreground); +} + +.chat-diff-change-content-widget .monaco-action-bar .action-item.disabled, +.chat-editor-overlay-widget .monaco-action-bar .action-item.disabled { + + > .action-label.codicon::before, + > .action-label.codicon, + > .action-label, + > .action-label:hover { + color: var(--vscode-button-foreground); + opacity: 0.7; + } +} + + +.chat-editor-overlay-widget .action-item.label-item { + font-variant-numeric: tabular-nums; +} + +.chat-editor-overlay-widget .monaco-action-bar .action-item.label-item > .action-label, +.chat-editor-overlay-widget .monaco-action-bar .action-item.label-item > .action-label:hover { + color: var(--vscode-button-foreground); + opacity: 1; +} + +.chat-editor-overlay-widget .action-item.auto { + position: relative; + overflow: hidden; +} + +.chat-editor-overlay-widget .action-item.auto::before { + content: ''; + position: absolute; + top: 0; + left: var(--vscode-action-item-auto-timeout, -100%); + width: 100%; + height: 100%; + background-color: var(--vscode-toolbar-hoverBackground); + transition: left 0.5s linear; +} diff --git a/src/vs/workbench/contrib/chat/browser/media/chatInlineAnchorWidget.css b/src/vs/workbench/contrib/chat/browser/media/chatInlineAnchorWidget.css index d3e9784d3e21..324e94d42e26 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chatInlineAnchorWidget.css +++ b/src/vs/workbench/contrib/chat/browser/media/chatInlineAnchorWidget.css @@ -39,6 +39,7 @@ display: inline-block; line-height: 100%; overflow: hidden; + font-size: 100% !important; background-size: contain; background-position: center; diff --git a/src/vs/workbench/contrib/chat/browser/media/chatStatus.css b/src/vs/workbench/contrib/chat/browser/media/chatStatus.css new file mode 100644 index 000000000000..82f35aebb209 --- /dev/null +++ b/src/vs/workbench/contrib/chat/browser/media/chatStatus.css @@ -0,0 +1,113 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/* Overall */ + +.chat-status-bar-entry-tooltip { + margin-top: 4px; + margin-bottom: 4px; +} + +.chat-status-bar-entry-tooltip hr { + margin-top: 8px; + margin-bottom: 8px; +} + +.chat-status-bar-entry-tooltip div.header { + color: var(--vscode-descriptionForeground); + margin-bottom: 4px; + font-weight: 600; +} + +.chat-status-bar-entry-tooltip div.description { + color: var(--vscode-descriptionForeground); +} + +.chat-status-bar-entry-tooltip .monaco-button { + margin-top: 5px; + margin-bottom: 5px; +} + +/* Setup for New User */ + +.chat-status-bar-entry-tooltip .setup .chat-feature-container { + display: flex; + align-items: center; + gap: 5px; + padding: 4px; +} + +/* Quota Indicator */ + +.chat-status-bar-entry-tooltip .quota-indicator { + margin-bottom: 6px; +} + +.chat-status-bar-entry-tooltip .quota-indicator .quota-label { + display: flex; + justify-content: space-between; + margin-bottom: 3px; +} + +.chat-status-bar-entry-tooltip .quota-indicator .quota-bar { + width: 100%; + height: 4px; + background-color: var(--vscode-gauge-foreground); + border-radius: 4px; + border: 1px solid var(--vscode-gauge-border); +} + +.chat-status-bar-entry-tooltip .quota-indicator .quota-bar .quota-bit { + height: 100%; + background-color: var(--vscode-gauge-background); + border-radius: 4px; +} + +.chat-status-bar-entry-tooltip .quota-indicator.warning .quota-bar { + background-color: var(--vscode-gauge-warningForeground); +} + +.chat-status-bar-entry-tooltip .quota-indicator.warning .quota-bar .quota-bit { + background-color: var(--vscode-gauge-warningBackground); +} + +.chat-status-bar-entry-tooltip .quota-indicator.error .quota-bar { + background-color: var(--vscode-gauge-errorForeground); +} + +.chat-status-bar-entry-tooltip .quota-indicator.error .quota-bar .quota-bit { + background-color: var(--vscode-gauge-errorBackground); +} + +/* Settings */ + +.chat-status-bar-entry-tooltip .settings { + display: flex; + flex-direction: column; + gap: 5px; +} + +.chat-status-bar-entry-tooltip .settings .setting { + display: flex; + align-items: center; +} + +.chat-status-bar-entry-tooltip .settings .setting .monaco-checkbox { + height: 14px; + width: 14px; + margin-right: 5px; +} + +.chat-status-bar-entry-tooltip .settings .setting .codicon { + font-size: 12px; +} + +.chat-status-bar-entry-tooltip .settings .setting .setting-label { + cursor: pointer; +} + +.chat-status-bar-entry-tooltip .settings .setting.disabled .setting-label { + color: var(--vscode-disabledForeground); +} diff --git a/src/vs/workbench/contrib/chat/browser/media/chatViewSetup.css b/src/vs/workbench/contrib/chat/browser/media/chatViewSetup.css index 785aeaa2fa70..0372805a40a4 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chatViewSetup.css +++ b/src/vs/workbench/contrib/chat/browser/media/chatViewSetup.css @@ -4,11 +4,8 @@ *--------------------------------------------------------------------------------------------*/ .chat-welcome-view .chat-setup-view { - text-align: center; - p { - width: 100%; - } + text-align: center; .chat-features-container { display: flex; @@ -18,6 +15,22 @@ border: 1px solid var(--vscode-chat-requestBorder); background-color: var(--vscode-chat-requestBackground); } +} + +.dialog-message-body .chat-setup-view { + + p.legal { + font-size: 12px; + color: var(--vscode-descriptionForeground); + } +} + +.dialog-message-body .chat-setup-view, +.chat-welcome-view .chat-setup-view { + + p { + width: 100%; + } .chat-feature-container { display: flex; diff --git a/src/vs/workbench/contrib/chat/browser/media/chatViewWelcome.css b/src/vs/workbench/contrib/chat/browser/media/chatViewWelcome.css index 1a0fa8147b42..4d99cd50999a 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chatViewWelcome.css +++ b/src/vs/workbench/contrib/chat/browser/media/chatViewWelcome.css @@ -53,6 +53,7 @@ div.chat-welcome-view { & > .chat-welcome-view-indicator-container { display: flex; + flex-wrap: wrap; margin-top: 5px; gap: 9px; justify-content: center; diff --git a/src/vs/workbench/contrib/chat/browser/promptSyntax/contributions/createPromptCommand/createPromptCommand.ts b/src/vs/workbench/contrib/chat/browser/promptSyntax/contributions/createPromptCommand/createPromptCommand.ts index 550acd2ac187..6f73d7ee0367 100644 --- a/src/vs/workbench/contrib/chat/browser/promptSyntax/contributions/createPromptCommand/createPromptCommand.ts +++ b/src/vs/workbench/contrib/chat/browser/promptSyntax/contributions/createPromptCommand/createPromptCommand.ts @@ -11,12 +11,16 @@ import { askForPromptSourceFolder } from './dialogs/askForPromptSourceFolder.js' import { IFileService } from '../../../../../../../platform/files/common/files.js'; import { ILabelService } from '../../../../../../../platform/label/common/label.js'; import { IOpenerService } from '../../../../../../../platform/opener/common/opener.js'; +import { PromptsConfig } from '../../../../../../../platform/prompts/common/config.js'; +import { ICommandService } from '../../../../../../../platform/commands/common/commands.js'; import { IPromptPath, IPromptsService } from '../../../../common/promptSyntax/service/types.js'; -import { appendToCommandPalette } from '../../../../../files/browser/fileActions.contribution.js'; import { IQuickInputService } from '../../../../../../../platform/quickinput/common/quickInput.js'; import { ServicesAccessor } from '../../../../../../../platform/instantiation/common/instantiation.js'; import { IWorkspaceContextService } from '../../../../../../../platform/workspace/common/workspace.js'; import { KeybindingsRegistry, KeybindingWeight } from '../../../../../../../platform/keybinding/common/keybindingsRegistry.js'; +import { MenuId, MenuRegistry } from '../../../../../../../platform/actions/common/actions.js'; +import { ContextKeyExpr } from '../../../../../../../platform/contextkey/common/contextkey.js'; +import { ChatContextKeys } from '../../../../common/chatContextKeys.js'; /** * Base command ID prefix. @@ -29,19 +33,19 @@ const BASE_COMMAND_ID = 'workbench.command.prompts.create'; const LOCAL_COMMAND_ID = `${BASE_COMMAND_ID}.local`; /** - * Command ID for creating a 'global' prompt. + * Command ID for creating a 'user' prompt. */ -const GLOBAL_COMMAND_ID = `${BASE_COMMAND_ID}.global`; +const USER_COMMAND_ID = `${BASE_COMMAND_ID}.user`; /** * Title of the 'create local prompt' command. */ -const LOCAL_COMMAND_TITLE = localize('commands.prompts.create.title.local', "Create prompt"); +const LOCAL_COMMAND_TITLE = localize('commands.prompts.create.title.local', "Create Prompt"); /** - * Title of the 'create global prompt' command. + * Title of the 'create user prompt' command. */ -const GLOBAL_COMMAND_TITLE = localize('commands.prompts.create.title.global', "Create prompt (global)"); +const USER_COMMAND_TITLE = localize('commands.prompts.create.title.user', "Create User Prompt"); /** * The command implementation. @@ -53,6 +57,7 @@ const command = async ( const fileService = accessor.get(IFileService); const labelService = accessor.get(ILabelService); const openerService = accessor.get(IOpenerService); + const commandService = accessor.get(ICommandService); const promptsService = accessor.get(IPromptsService); const quickInputService = accessor.get(IQuickInputService); const workspaceService = accessor.get(IWorkspaceContextService); @@ -84,6 +89,7 @@ const command = async ( folder: selectedFolder, content, fileService, + commandService, }); await openerService.open(promptUri); @@ -92,48 +98,52 @@ const command = async ( /** * Factory for creating the command handler with specific prompt `type`. */ -const commandFactory = (type: 'local' | 'global') => { +const commandFactory = (type: 'local' | 'user') => { return async (accessor: ServicesAccessor): Promise => { return command(accessor, type); }; }; /** - * Register the "Create Local Prompt" command. + * Register the "Create Prompt" command. */ KeybindingsRegistry.registerCommandAndKeybindingRule({ id: LOCAL_COMMAND_ID, weight: KeybindingWeight.WorkbenchContrib, handler: commandFactory('local'), + when: ContextKeyExpr.and(PromptsConfig.enabledCtx, ChatContextKeys.enabled), }); /** - * Register the "Create Global Prompt" command. + * Register the "Create User Prompt" command. */ KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: GLOBAL_COMMAND_ID, + id: USER_COMMAND_ID, weight: KeybindingWeight.WorkbenchContrib, - handler: commandFactory('global'), + handler: commandFactory('user'), + when: ContextKeyExpr.and(PromptsConfig.enabledCtx, ChatContextKeys.enabled), }); /** - * Register the "Create Local Prompt" command in the command palette. + * Register the "Create Prompt" command in the command palette. */ -appendToCommandPalette( - { +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { + command: { id: LOCAL_COMMAND_ID, title: LOCAL_COMMAND_TITLE, - category: CHAT_CATEGORY, + category: CHAT_CATEGORY }, -); + when: ContextKeyExpr.and(PromptsConfig.enabledCtx, ChatContextKeys.enabled) +}); /** - * Register the "Create Global Prompt" command in the command palette. + * Register the "Create User Prompt" command in the command palette. */ -appendToCommandPalette( - { - id: GLOBAL_COMMAND_ID, - title: GLOBAL_COMMAND_TITLE, +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { + command: { + id: USER_COMMAND_ID, + title: USER_COMMAND_TITLE, category: CHAT_CATEGORY, }, -); + when: ContextKeyExpr.and(PromptsConfig.enabledCtx, ChatContextKeys.enabled) +}); diff --git a/src/vs/workbench/contrib/chat/browser/promptSyntax/contributions/createPromptCommand/dialogs/askForPromptName.ts b/src/vs/workbench/contrib/chat/browser/promptSyntax/contributions/createPromptCommand/dialogs/askForPromptName.ts index 0c7ff2db72f3..cd412256291e 100644 --- a/src/vs/workbench/contrib/chat/browser/promptSyntax/contributions/createPromptCommand/dialogs/askForPromptName.ts +++ b/src/vs/workbench/contrib/chat/browser/promptSyntax/contributions/createPromptCommand/dialogs/askForPromptName.ts @@ -11,7 +11,7 @@ import { IQuickInputService } from '../../../../../../../../platform/quickinput/ * Asks the user for a prompt name. */ export const askForPromptName = async ( - _type: 'local' | 'global', + _type: 'local' | 'user', quickInputService: IQuickInputService, ): Promise => { const result = await quickInputService.input( diff --git a/src/vs/workbench/contrib/chat/browser/promptSyntax/contributions/createPromptCommand/dialogs/askForPromptSourceFolder.ts b/src/vs/workbench/contrib/chat/browser/promptSyntax/contributions/createPromptCommand/dialogs/askForPromptSourceFolder.ts index 94754b2f0142..2f446a81472d 100644 --- a/src/vs/workbench/contrib/chat/browser/promptSyntax/contributions/createPromptCommand/dialogs/askForPromptSourceFolder.ts +++ b/src/vs/workbench/contrib/chat/browser/promptSyntax/contributions/createPromptCommand/dialogs/askForPromptSourceFolder.ts @@ -21,7 +21,7 @@ interface IAskForFolderOptions { /** * Prompt type. */ - readonly type: 'local' | 'global'; + readonly type: 'local' | 'user'; readonly labelService: ILabelService; readonly openerService: IOpenerService; @@ -119,7 +119,7 @@ const showNoFoldersDialog = async ( const docsQuickPick: WithUriValue = { type: 'item', label: localize( - 'commands.prompts.create.ask-folder.no-folders-found.learn-more', + 'commands.prompts.create.ask-folder.empty.docs-label', 'Learn how to configure reusable prompts', ), description: DOCUMENTATION_URL, @@ -131,7 +131,7 @@ const showNoFoldersDialog = async ( [docsQuickPick], { placeHolder: localize( - 'commands.prompts.create.ask-folder.no-folders-found.placeholder', + 'commands.prompts.create.ask-folder.empty.placeholder', 'No prompt source folders found.', ), canPickMany: false, diff --git a/src/vs/workbench/contrib/chat/browser/promptSyntax/contributions/createPromptCommand/errors.ts b/src/vs/workbench/contrib/chat/browser/promptSyntax/contributions/createPromptCommand/errors.ts index 706da39504e0..5d7ed5c84161 100644 --- a/src/vs/workbench/contrib/chat/browser/promptSyntax/contributions/createPromptCommand/errors.ts +++ b/src/vs/workbench/contrib/chat/browser/promptSyntax/contributions/createPromptCommand/errors.ts @@ -24,20 +24,6 @@ export class FolderExists extends BaseCreatePromptError { } } -/** - * Error for when a prompt file already exists at the provided - * prompt file path. - */ -export class PromptExists extends BaseCreatePromptError { - constructor(path: string) { - super(localize( - 'workbench.command.prompts.create.error.prompt-exists', - "Prompt file '{0}' already exists.", - path, - )); - } -} - /** * Error for when an invalid prompt file name is provided. */ diff --git a/src/vs/workbench/contrib/chat/browser/promptSyntax/contributions/createPromptCommand/utils/createPromptFile.ts b/src/vs/workbench/contrib/chat/browser/promptSyntax/contributions/createPromptCommand/utils/createPromptFile.ts index 296937b25efa..710637d2fdff 100644 --- a/src/vs/workbench/contrib/chat/browser/promptSyntax/contributions/createPromptCommand/utils/createPromptFile.ts +++ b/src/vs/workbench/contrib/chat/browser/promptSyntax/contributions/createPromptCommand/utils/createPromptFile.ts @@ -3,12 +3,13 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { FolderExists, InvalidPromptName } from '../errors.js'; import { URI } from '../../../../../../../../base/common/uri.js'; import { assert } from '../../../../../../../../base/common/assert.js'; import { VSBuffer } from '../../../../../../../../base/common/buffer.js'; import { dirname } from '../../../../../../../../base/common/resources.js'; -import { FolderExists, InvalidPromptName, PromptExists } from '../errors.js'; import { IFileService } from '../../../../../../../../platform/files/common/files.js'; +import { ICommandService } from '../../../../../../../../platform/commands/common/commands.js'; import { isPromptFile, PROMPT_FILE_EXTENSION } from '../../../../../../../../platform/prompts/common/constants.js'; /** @@ -32,6 +33,7 @@ interface ICreatePromptFileOptions { readonly content: string; fileService: IFileService; + commandService: ICommandService; } /** @@ -45,7 +47,7 @@ interface ICreatePromptFileOptions { export const createPromptFile = async ( options: ICreatePromptFileOptions, ): Promise => { - const { fileName, folder, content, fileService } = options; + const { fileName, folder, content, fileService, commandService } = options; const promptUri = URI.joinPath(folder, fileName); @@ -58,10 +60,16 @@ export const createPromptFile = async ( if (await fileService.exists(promptUri)) { const promptInfo = await fileService.resolve(promptUri); - // throw appropriate error based on the type of the existing entity - throw (promptInfo.isDirectory) - ? new FolderExists(promptUri.fsPath) - : new PromptExists(promptUri.fsPath); + // if existing object is a folder, throw an error + assert( + !promptInfo.isDirectory, + new FolderExists(promptUri.fsPath), + ); + + // prompt file already exists so open it + await commandService.executeCommand('vscode.open', promptUri); + + return promptUri; } // ensure the parent folder of the prompt file exists diff --git a/src/vs/workbench/contrib/chat/browser/promptSyntax/contributions/usePromptCommand.ts b/src/vs/workbench/contrib/chat/browser/promptSyntax/contributions/usePromptCommand.ts index 0b89425d47b6..87aa3636fe45 100644 --- a/src/vs/workbench/contrib/chat/browser/promptSyntax/contributions/usePromptCommand.ts +++ b/src/vs/workbench/contrib/chat/browser/promptSyntax/contributions/usePromptCommand.ts @@ -8,24 +8,30 @@ import { URI } from '../../../../../../base/common/uri.js'; import { CHAT_CATEGORY } from '../../actions/chatActions.js'; import { IChatWidget, IChatWidgetService } from '../../chat.js'; import { KeyMod, KeyCode } from '../../../../../../base/common/keyCodes.js'; +import { PromptsConfig } from '../../../../../../platform/prompts/common/config.js'; +import { IViewsService } from '../../../../../services/views/common/viewsService.js'; import { isPromptFile } from '../../../../../../platform/prompts/common/constants.js'; import { IEditorService } from '../../../../../services/editor/common/editorService.js'; import { ICommandService } from '../../../../../../platform/commands/common/commands.js'; -import { appendToCommandPalette } from '../../../../files/browser/fileActions.contribution.js'; import { ServicesAccessor } from '../../../../../../platform/instantiation/common/instantiation.js'; import { IActiveCodeEditor, isCodeEditor, isDiffEditor } from '../../../../../../editor/browser/editorBrowser.js'; -import { IChatAttachPromptActionOptions, ATTACH_PROMPT_ACTION_ID } from '../../actions/chatAttachPromptAction.js'; import { KeybindingsRegistry, KeybindingWeight } from '../../../../../../platform/keybinding/common/keybindingsRegistry.js'; +import { IChatAttachPromptActionOptions, ATTACH_PROMPT_ACTION_ID } from '../../actions/chatAttachPromptAction/chatAttachPromptAction.js'; +import { MenuId, MenuRegistry } from '../../../../../../platform/actions/common/actions.js'; +import { ContextKeyExpr } from '../../../../../../platform/contextkey/common/contextkey.js'; +import { ChatContextKeys } from '../../../common/chatContextKeys.js'; /** - * Keybinding for the "Use Prompt" command. + * Command ID of the "Use Prompt" command. */ -const USE_COMMAND_KEY_BINDING = KeyMod.Alt | KeyMod.Shift | KeyCode.KeyE; +export const COMMAND_ID = 'workbench.command.prompts.use'; /** - * Command ID for the "Use Prompt" command. + * Keybinding of the "Use Prompt" command. + * The `cmd + /` is the current keybinding for 'attachment', so we use + * the `alt` key modifier to convey the "prompt attachment" action. */ -const USE_PROMPT_COMMAND_ID = 'use-prompt'; +const COMMAND_KEY_BINDING = KeyMod.CtrlCmd | KeyCode.Slash | KeyMod.Alt; /** * Implementation of the "Use Prompt" command. The command works in the following way. @@ -45,40 +51,21 @@ const USE_PROMPT_COMMAND_ID = 'use-prompt'; * was pressed when the prompt was selected, a `chat edits` panel is used instead * (likewise either the last focused or a new one). */ -const usePromptCommand = async ( +const command = async ( accessor: ServicesAccessor, ): Promise => { const commandService = accessor.get(ICommandService); + const viewsService = accessor.get(IViewsService); const options: IChatAttachPromptActionOptions = { resource: getActivePromptUri(accessor), widget: getFocusedChatWidget(accessor), + viewsService, }; await commandService.executeCommand(ATTACH_PROMPT_ACTION_ID, options); }; -/** - * Register the "Use Prompt" command with its keybinding. - */ -KeybindingsRegistry.registerCommandAndKeybindingRule({ - id: USE_PROMPT_COMMAND_ID, - weight: KeybindingWeight.WorkbenchContrib, - primary: USE_COMMAND_KEY_BINDING, - handler: usePromptCommand, -}); - -/** - * Register the "Use Prompt" command in the `command palette`. - */ -appendToCommandPalette( - { - id: USE_PROMPT_COMMAND_ID, - title: localize('commands.prompts.use-prompt', "Use Prompt"), - category: CHAT_CATEGORY, - }, -); - /** * Get chat widget reference to attach prompt to. */ @@ -139,3 +126,26 @@ const getActivePromptUri = ( return undefined; }; + +/** + * Register the "Use Prompt" command with its keybinding. + */ +KeybindingsRegistry.registerCommandAndKeybindingRule({ + id: COMMAND_ID, + weight: KeybindingWeight.WorkbenchContrib, + primary: COMMAND_KEY_BINDING, + handler: command, + when: ContextKeyExpr.and(PromptsConfig.enabledCtx, ChatContextKeys.enabled), +}); + +/** + * Register the "Use Prompt" command in the `command palette`. + */ +MenuRegistry.appendMenuItem(MenuId.CommandPalette, { + command: { + id: COMMAND_ID, + title: localize('commands.prompts.use.title', "Use Prompt"), + category: CHAT_CATEGORY + }, + when: ContextKeyExpr.and(PromptsConfig.enabledCtx, ChatContextKeys.enabled) +}); diff --git a/src/vs/workbench/contrib/chat/browser/resourcePool.ts b/src/vs/workbench/contrib/chat/browser/resourcePool.ts new file mode 100644 index 000000000000..4ee1949d7d31 --- /dev/null +++ b/src/vs/workbench/contrib/chat/browser/resourcePool.ts @@ -0,0 +1,39 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable, IDisposable } from 'vs/base/common/lifecycle'; + + +export class ResourcePool extends Disposable { + private readonly pool: T[] = []; + + private _inUse = new Set; + public get inUse(): ReadonlySet { + return this._inUse; + } + + constructor( + private readonly _itemFactory: () => T, + ) { + super(); + } + + get(): T { + if (this.pool.length > 0) { + const item = this.pool.pop()!; + this._inUse.add(item); + return item; + } + + const item = this._register(this._itemFactory()); + this._inUse.add(item); + return item; + } + + release(item: T): void { + this._inUse.delete(item); + this.pool.push(item); + } +} diff --git a/src/vs/workbench/contrib/chat/browser/viewsWelcome/chatViewWelcomeController.ts b/src/vs/workbench/contrib/chat/browser/viewsWelcome/chatViewWelcomeController.ts index eb8eadcfa0d3..d4ff4fb01c6b 100644 --- a/src/vs/workbench/contrib/chat/browser/viewsWelcome/chatViewWelcomeController.ts +++ b/src/vs/workbench/contrib/chat/browser/viewsWelcome/chatViewWelcomeController.ts @@ -7,7 +7,7 @@ import * as dom from '../../../../../base/browser/dom.js'; import { Button } from '../../../../../base/browser/ui/button/button.js'; import { renderIcon } from '../../../../../base/browser/ui/iconLabel/iconLabels.js'; import { Event } from '../../../../../base/common/event.js'; -import { IMarkdownString, MarkdownString } from '../../../../../base/common/htmlContent.js'; +import { IMarkdownString } from '../../../../../base/common/htmlContent.js'; import { Disposable, DisposableStore } from '../../../../../base/common/lifecycle.js'; import { ThemeIcon } from '../../../../../base/common/themables.js'; import { MarkdownRenderer } from '../../../../../editor/browser/widget/markdownRenderer/browser/markdownRenderer.js'; @@ -17,7 +17,8 @@ import { IInstantiationService } from '../../../../../platform/instantiation/com import { ILogService } from '../../../../../platform/log/common/log.js'; import { IOpenerService } from '../../../../../platform/opener/common/opener.js'; import { defaultButtonStyles } from '../../../../../platform/theme/browser/defaultStyles.js'; -import { ChatAgentLocation, IChatAgentService } from '../../common/chatAgents.js'; +import { IChatAgentService } from '../../common/chatAgents.js'; +import { ChatAgentLocation } from '../../common/constants.js'; import { chatViewsWelcomeRegistry, IChatViewsWelcomeDescriptor } from './chatViewsWelcome.js'; const $ = dom.$; @@ -116,14 +117,14 @@ export interface IChatViewWelcomeContent { export interface IChatViewWelcomeRenderOptions { firstLinkToButton?: boolean; location: ChatAgentLocation; - isWidgetWelcomeViewContent?: boolean; + isWidgetAgentWelcomeViewContent?: boolean; } export class ChatViewWelcomePart extends Disposable { public readonly element: HTMLElement; constructor( - content: IChatViewWelcomeContent, + public readonly content: IChatViewWelcomeContent, options: IChatViewWelcomeRenderOptions | undefined, @IOpenerService private openerService: IOpenerService, @IInstantiationService private instantiationService: IInstantiationService, @@ -147,10 +148,7 @@ export class ChatViewWelcomePart extends Disposable { title.textContent = content.title; // Preview indicator - if (options?.location === ChatAgentLocation.EditingSession && typeof content.message !== 'function' && chatAgentService.toolsAgentModeEnabled && options.isWidgetWelcomeViewContent) { - // Override welcome message for the agent. Sort of a hack, should it come from the participant? This case is different because the welcome content typically doesn't change per ChatWidget - const agentMessage = localize({ key: 'agentMessage', comment: ['{Locked="["}', '{Locked="]({0})"}'] }, "Ask Copilot to edit your files in [agent mode]({0}). Copilot will automatically use multiple requests to pick files to edit, run terminal commands, and iterate on errors.\n\nCopilot is powered by AI, so mistakes are possible. Review output carefully before use.", 'https://aka.ms/vscode-copilot-agent'); - content.message = new MarkdownString(agentMessage); + if (typeof content.message !== 'function' && options?.isWidgetAgentWelcomeViewContent) { const container = dom.append(this.element, $('.chat-welcome-view-indicator-container')); dom.append(container, $('.chat-welcome-view-subtitle', undefined, localize('agentModeSubtitle', "Agent Mode"))); dom.append(container, $('.chat-welcome-view-indicator', undefined, localize('experimental', "EXPERIMENTAL"))); diff --git a/src/vs/workbench/contrib/chat/common/chat.ts b/src/vs/workbench/contrib/chat/common/chat.ts new file mode 100644 index 000000000000..7e2b00c0c8f8 --- /dev/null +++ b/src/vs/workbench/contrib/chat/common/chat.ts @@ -0,0 +1,16 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ChatMode } from './constants.js'; + +export function checkModeOption(mode: ChatMode, option: boolean | ((mode: ChatMode) => boolean) | undefined): boolean | undefined { + if (option === undefined) { + return undefined; + } + if (typeof option === 'function') { + return option(mode); + } + return option; +} diff --git a/src/vs/workbench/contrib/chat/common/chatAgents.ts b/src/vs/workbench/contrib/chat/common/chatAgents.ts index 633010ae07e5..ce8309c498af 100644 --- a/src/vs/workbench/contrib/chat/common/chatAgents.ts +++ b/src/vs/workbench/contrib/chat/common/chatAgents.ts @@ -25,8 +25,9 @@ import { asJson, IRequestService } from '../../../../platform/request/common/req import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; import { ChatContextKeys } from './chatContextKeys.js'; import { IChatProgressHistoryResponseContent, IChatRequestVariableData, ISerializableChatAgentData } from './chatModel.js'; -import { IRawChatCommandContribution, RawChatParticipantLocation } from './chatParticipantContribTypes.js'; +import { IRawChatCommandContribution } from './chatParticipantContribTypes.js'; import { IChatFollowup, IChatLocationData, IChatProgress, IChatResponseErrorDetails, IChatTaskDto } from './chatService.js'; +import { ChatAgentLocation, ChatMode } from './constants.js'; //#region agent service, commands etc @@ -36,27 +37,6 @@ export interface IChatAgentHistoryEntry { result: IChatAgentResult; } -export enum ChatAgentLocation { - Panel = 'panel', - Terminal = 'terminal', - Notebook = 'notebook', - Editor = 'editor', - EditingSession = 'editing-session', -} - -export namespace ChatAgentLocation { - export function fromRaw(value: RawChatParticipantLocation | string): ChatAgentLocation { - switch (value) { - case 'panel': return ChatAgentLocation.Panel; - case 'terminal': return ChatAgentLocation.Terminal; - case 'notebook': return ChatAgentLocation.Notebook; - case 'editor': return ChatAgentLocation.Editor; - case 'editing-session': return ChatAgentLocation.EditingSession; - } - return ChatAgentLocation.Panel; - } -} - export interface IChatAgentData { id: string; name: string; @@ -145,6 +125,7 @@ export interface IChatAgentMetadata { followupPlaceholder?: string; isSticky?: boolean; requester?: IChatRequesterInformation; + welcomeMessageContent?: IChatWelcomeMessageContent; } @@ -163,6 +144,7 @@ export interface IChatAgentRequest { acceptedConfirmationData?: any[]; rejectedConfirmationData?: any[]; userSelectedModelId?: string; + userSelectedTools?: string[]; } export interface IChatQuestion { @@ -206,9 +188,7 @@ export interface IChatAgentService { * undefined when an agent was removed */ readonly onDidChangeAgents: Event; - readonly onDidChangeToolsAgentModeEnabled: Event; - readonly toolsAgentModeEnabled: boolean; - toggleToolsAgentMode(enabled?: boolean): void; + readonly hasToolsAgent: boolean; registerAgent(id: string, data: IChatAgentData): IDisposable; registerAgentImplementation(id: string, agent: IChatAgentImplementation): IDisposable; registerDynamicAgent(data: IChatAgentData, agentImpl: IChatAgentImplementation): IDisposable; @@ -231,7 +211,7 @@ export interface IChatAgentService { /** * Get the default agent (only if activated) */ - getDefaultAgent(location: ChatAgentLocation): IChatAgent | undefined; + getDefaultAgent(location: ChatAgentLocation, mode?: ChatMode): IChatAgent | undefined; /** * Get the default agent data that has been contributed (may not be activated yet) @@ -241,8 +221,6 @@ export interface IChatAgentService { updateAgent(id: string, updateMetadata: IChatAgentMetadata): void; } -const ChatToolsAgentModeStorageKey = 'chat.toolsAgentMode'; - export class ChatAgentService extends Disposable implements IChatAgentService { public static readonly AGENT_LEADER = '@'; @@ -254,21 +232,16 @@ export class ChatAgentService extends Disposable implements IChatAgentService { private readonly _onDidChangeAgents = new Emitter(); readonly onDidChangeAgents: Event = this._onDidChangeAgents.event; - private readonly _onDidChangeToolsAgentModeEnabled = new Emitter(); - readonly onDidChangeToolsAgentModeEnabled: Event = this._onDidChangeToolsAgentModeEnabled.event; - private readonly _agentsContextKeys = new Set(); private readonly _hasDefaultAgent: IContextKey; private readonly _defaultAgentRegistered: IContextKey; private readonly _editingAgentRegistered: IContextKey; - private readonly _agentModeContextKey: IContextKey; private readonly _hasToolsAgentContextKey: IContextKey; private _chatParticipantDetectionProviders = new Map(); constructor( @IContextKeyService private readonly contextKeyService: IContextKeyService, - @IStorageService private readonly storageService: IStorageService, ) { super(); this._hasDefaultAgent = ChatContextKeys.enabled.bindTo(this.contextKeyService); @@ -280,12 +253,7 @@ export class ChatAgentService extends Disposable implements IChatAgentService { } })); - this._agentModeContextKey = ChatContextKeys.Editing.agentMode.bindTo(contextKeyService); this._hasToolsAgentContextKey = ChatContextKeys.Editing.hasToolsAgent.bindTo(contextKeyService); - this._agentModeContextKey.set( - this.storageService.getBoolean(ChatToolsAgentModeStorageKey, StorageScope.WORKSPACE, false)); - this._register( - this.storageService.onWillSaveState(() => this.storageService.store(ChatToolsAgentModeStorageKey, this._agentModeContextKey.get(), StorageScope.WORKSPACE, StorageTarget.USER))); } registerAgent(id: string, data: IChatAgentData): IDisposable { @@ -412,9 +380,13 @@ export class ChatAgentService extends Disposable implements IChatAgentService { this._onDidChangeAgents.fire(new MergedChatAgent(agent.data, agent.impl)); } - getDefaultAgent(location: ChatAgentLocation): IChatAgent | undefined { + getDefaultAgent(location: ChatAgentLocation, mode?: ChatMode): IChatAgent | undefined { + if (mode === ChatMode.Edit || mode === ChatMode.Agent) { + location = ChatAgentLocation.EditingSession; + } + return findLast(this.getActivatedAgents(), a => { - if (location === ChatAgentLocation.EditingSession && this.toolsAgentModeEnabled !== !!a.isToolsAgent) { + if ((mode === ChatMode.Agent) !== !!a.isToolsAgent) { return false; } @@ -422,14 +394,8 @@ export class ChatAgentService extends Disposable implements IChatAgentService { }); } - public get toolsAgentModeEnabled(): boolean { - return !!this._hasToolsAgentContextKey.get() && !!this._agentModeContextKey.get(); - } - - toggleToolsAgentMode(enabled?: boolean): void { - this._agentModeContextKey.set(enabled ?? !this._agentModeContextKey.get()); - this._onDidChangeToolsAgentModeEnabled.fire(); - this._onDidChangeAgents.fire(this.getDefaultAgent(ChatAgentLocation.EditingSession)); + public get hasToolsAgent(): boolean { + return !!this._hasToolsAgentContextKey.get(); } getContributedDefaultAgent(location: ChatAgentLocation): IChatAgentData | undefined { diff --git a/src/vs/workbench/contrib/chat/common/chatContextKeys.ts b/src/vs/workbench/contrib/chat/common/chatContextKeys.ts index 591bbdcad92c..19b59301b571 100644 --- a/src/vs/workbench/contrib/chat/common/chatContextKeys.ts +++ b/src/vs/workbench/contrib/chat/common/chatContextKeys.ts @@ -7,7 +7,7 @@ import { localize } from '../../../../nls.js'; import { ContextKeyExpr, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; import { IsWebContext } from '../../../../platform/contextkey/common/contextkeys.js'; import { RemoteNameContext } from '../../../common/contextkeys.js'; -import { ChatAgentLocation } from './chatAgents.js'; +import { ChatAgentLocation, ChatConfiguration, ChatMode } from './constants.js'; export namespace ChatContextKeys { export const responseVote = new RawContextKey('chatSessionResponseVote', '', { type: 'string', description: localize('interactiveSessionResponseVote', "When the response has been voted up, is set to 'up'. When voted down, is set to 'down'. Otherwise an empty string.") }); @@ -30,7 +30,9 @@ export namespace ChatContextKeys { export const inputHasFocus = new RawContextKey('chatInputHasFocus', false, { type: 'boolean', description: localize('interactiveInputHasFocus', "True when the chat input has focus.") }); export const inChatInput = new RawContextKey('inChatInput', false, { type: 'boolean', description: localize('inInteractiveInput', "True when focus is in the chat input, false otherwise.") }); export const inChatSession = new RawContextKey('inChat', false, { type: 'boolean', description: localize('inChat', "True when focus is in the chat widget, false otherwise.") }); + export const inUnifiedChat = new RawContextKey('inUnifiedChat', false, { type: 'boolean', description: localize('inUnifiedChat', "True when focus is in the unified chat widget, false otherwise.") }); export const instructionsAttached = new RawContextKey('chatInstructionsAttached', false, { type: 'boolean', description: localize('chatInstructionsAttachedContextDescription', "True when the chat has a prompt instructions attached.") }); + export const chatMode = new RawContextKey('chatMode', ChatMode.Chat, { type: 'string', description: localize('chatMode', "The current chat mode.") }); export const supported = ContextKeyExpr.or(IsWebContext.toNegated(), RemoteNameContext.notEqualsTo('')); // supported on desktop and in web only with a remote connection export const enabled = new RawContextKey('chatIsEnabled', false, { type: 'boolean', description: localize('chatIsEnabled', "True when chat is enabled because a default chat participant is activated with an implementation.") }); @@ -49,30 +51,31 @@ export namespace ChatContextKeys { export const languageModelsAreUserSelectable = new RawContextKey('chatModelsAreUserSelectable', false, { type: 'boolean', description: localize('chatModelsAreUserSelectable', "True when the chat model can be selected manually by the user.") }); export const Setup = { - - // State - signedOut: new RawContextKey('chatSetupSignedOut', false, true), // True when user is signed out. hidden: new RawContextKey('chatSetupHidden', false, true), // True when chat setup is explicitly hidden. installed: new RawContextKey('chatSetupInstalled', false, true), // True when the chat extension is installed. + fromDialog: ContextKeyExpr.has('config.chat.experimental.setupFromDialog'), + }; - // Plans + export const Entitlement = { + signedOut: new RawContextKey('chatSetupSignedOut', false, true), // True when user is signed out. canSignUp: new RawContextKey('chatPlanCanSignUp', false, true), // True when user can sign up to be a chat limited user. limited: new RawContextKey('chatPlanLimited', false, true), // True when user is a chat limited user. pro: new RawContextKey('chatPlanPro', false, true) // True when user is a chat pro user. }; - export const SetupViewKeys = new Set([ChatContextKeys.Setup.hidden.key, ChatContextKeys.Setup.installed.key, ChatContextKeys.Setup.signedOut.key, ChatContextKeys.Setup.canSignUp.key]); + export const SetupViewKeys = new Set([ChatContextKeys.Setup.hidden.key, ChatContextKeys.Setup.installed.key, ChatContextKeys.Entitlement.signedOut.key, ChatContextKeys.Entitlement.canSignUp.key, ...Setup.fromDialog.keys()]); export const SetupViewCondition = ContextKeyExpr.or( ContextKeyExpr.and( ChatContextKeys.Setup.hidden.negate(), - ChatContextKeys.Setup.installed.negate() + ChatContextKeys.Setup.installed.negate(), + Setup.fromDialog.negate() ), ContextKeyExpr.and( - ChatContextKeys.Setup.canSignUp, + ChatContextKeys.Entitlement.canSignUp, ChatContextKeys.Setup.installed ), ContextKeyExpr.and( - ChatContextKeys.Setup.signedOut, + ChatContextKeys.Entitlement.signedOut, ChatContextKeys.Setup.installed ) )!; @@ -82,7 +85,29 @@ export namespace ChatContextKeys { export const Editing = { hasToolsAgent: new RawContextKey('chatHasToolsAgent', false, { type: 'boolean', description: localize('chatEditingHasToolsAgent', "True when a tools agent is registered.") }), - agentMode: new RawContextKey('chatAgentMode', false, { type: 'boolean', description: localize('chatEditingAgentMode', "True when edits is in agent mode.") }), agentModeDisallowed: new RawContextKey('chatAgentModeDisallowed', undefined, { type: 'boolean', description: localize('chatAgentModeDisallowed', "True when agent mode is not allowed.") }), // experiment-driven disablement + hasToolConfirmation: new RawContextKey('chatHasToolConfirmation', false, { type: 'boolean', description: localize('chatEditingHasToolConfirmation', "True when a tool confirmation is present.") }), + }; + + export const Tools = { + + toolsCount: new RawContextKey('toolsCount', 0, { type: 'number', description: localize('toolsCount', "The count of tools available in the chat.") }) }; } + +export namespace ChatContextKeyExprs { + export const unifiedChatEnabled = ContextKeyExpr.has(`config.${ChatConfiguration.UnifiedChatView}`); + + export const inEditsOrUnified = ContextKeyExpr.or( + ChatContextKeys.location.isEqualTo(ChatAgentLocation.EditingSession), + ChatContextKeys.inUnifiedChat); + + export const inNonUnifiedPanel = ContextKeyExpr.and( + ChatContextKeys.location.isEqualTo(ChatAgentLocation.Panel), + ChatContextKeys.inUnifiedChat.negate()); + + export const inEditingMode = ContextKeyExpr.or( + ChatContextKeys.chatMode.isEqualTo(ChatMode.Edit), + ChatContextKeys.chatMode.isEqualTo(ChatMode.Agent), + ); +} diff --git a/src/vs/workbench/contrib/chat/common/chatEditingService.ts b/src/vs/workbench/contrib/chat/common/chatEditingService.ts index 0f4d8ff0dc96..0de480c9d886 100644 --- a/src/vs/workbench/contrib/chat/common/chatEditingService.ts +++ b/src/vs/workbench/contrib/chat/common/chatEditingService.ts @@ -6,7 +6,6 @@ import { CancellationToken } from '../../../../base/common/cancellation.js'; import { Event } from '../../../../base/common/event.js'; import { IDisposable } from '../../../../base/common/lifecycle.js'; -import { ResourceMap } from '../../../../base/common/map.js'; import { IObservable, IReader, ITransaction } from '../../../../base/common/observable.js'; import { URI } from '../../../../base/common/uri.js'; import { TextEdit } from '../../../../editor/common/languages.js'; @@ -68,11 +67,11 @@ export interface IChatRelatedFilesProvider { export interface WorkingSetDisplayMetadata { state: WorkingSetEntryState; description?: string; - isMarkedReadonly?: boolean; } export interface IStreamingEdits { pushText(edits: TextEdit[]): void; + pushNotebookCellText(cell: URI, edits: TextEdit[]): void; pushNotebook(edits: ICellEditOperation[]): void; /** Marks edits as done, idempotent */ complete(): void; @@ -87,11 +86,8 @@ export interface IChatEditingSession extends IDisposable { readonly onDidDispose: Event; readonly state: IObservable; readonly entries: IObservable; - readonly workingSet: ResourceMap; - addFileToWorkingSet(uri: URI, description?: string, kind?: WorkingSetEntryState.Suggested): void; show(): Promise; remove(reason: WorkingSetEntryRemovalReason, ...uris: URI[]): void; - markIsReadonly(uri: URI, isReadonly?: boolean): void; accept(...uris: URI[]): Promise; reject(...uris: URI[]): Promise; getEntry(uri: URI): IModifiedFileEntry | undefined; @@ -156,9 +152,8 @@ export const enum WorkingSetEntryState { Accepted, Rejected, Transient, // TODO@joyceerhl remove this - Attached, + Attached, // TODO@joyceerhl remove this Sent, // TODO@joyceerhl remove this - Suggested, } export const enum ChatEditingSessionChangeType { @@ -261,7 +256,6 @@ export const enum ChatEditingSessionState { export const CHAT_EDITING_MULTI_DIFF_SOURCE_RESOLVER_SCHEME = 'chat-editing-multi-diff-source'; export const chatEditingWidgetFileStateContextKey = new RawContextKey('chatEditingWidgetFileState', undefined, localize('chatEditingWidgetFileState', "The current state of the file in the chat editing widget")); -export const chatEditingWidgetFileReadonlyContextKey = new RawContextKey('chatEditingWidgetFileReadonly', undefined, localize('chatEditingWidgetFileReadonly', "Whether the file has been marked as read-only in the chat editing widget")); export const chatEditingAgentSupportsReadonlyReferencesContextKey = new RawContextKey('chatEditingAgentSupportsReadonlyReferences', undefined, localize('chatEditingAgentSupportsReadonlyReferences', "Whether the chat editing agent supports readonly references (temporary)")); export const decidedChatEditingResourceContextKey = new RawContextKey('decidedChatEditingResource', []); export const chatEditingResourceContextKey = new RawContextKey('chatEditingResource', undefined); diff --git a/src/vs/workbench/contrib/chat/common/chatEntitlementService.ts b/src/vs/workbench/contrib/chat/common/chatEntitlementService.ts new file mode 100644 index 000000000000..ab6ffdb0324a --- /dev/null +++ b/src/vs/workbench/contrib/chat/common/chatEntitlementService.ts @@ -0,0 +1,882 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import product from '../../../../platform/product/common/product.js'; +import { Barrier } from '../../../../base/common/async.js'; +import { CancellationToken, CancellationTokenSource } from '../../../../base/common/cancellation.js'; +import { Emitter, Event } from '../../../../base/common/event.js'; +import { Lazy } from '../../../../base/common/lazy.js'; +import { Disposable } from '../../../../base/common/lifecycle.js'; +import { IRequestContext } from '../../../../base/parts/request/common/request.js'; +import { localize } from '../../../../nls.js'; +import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; +import { IContextKey, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; +import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js'; +import { ExtensionIdentifier } from '../../../../platform/extensions/common/extensions.js'; +import { createDecorator, IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; +import { ILogService } from '../../../../platform/log/common/log.js'; +import { IProductService } from '../../../../platform/product/common/productService.js'; +import { asText, IRequestService } from '../../../../platform/request/common/request.js'; +import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; +import { ITelemetryService, TelemetryLevel } from '../../../../platform/telemetry/common/telemetry.js'; +import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; +import { AuthenticationSession, IAuthenticationExtensionsService, IAuthenticationService } from '../../../services/authentication/common/authentication.js'; +import { IWorkbenchExtensionEnablementService } from '../../../services/extensionManagement/common/extensionManagement.js'; +import { IExtension, IExtensionsWorkbenchService } from '../../extensions/common/extensions.js'; +import { ChatContextKeys } from './chatContextKeys.js'; +import { IOpenerService } from '../../../../platform/opener/common/opener.js'; +import { URI } from '../../../../base/common/uri.js'; +import Severity from '../../../../base/common/severity.js'; +import { IWorkbenchEnvironmentService } from '../../../services/environment/common/environmentService.js'; +import { isWeb } from '../../../../base/common/platform.js'; + +export const IChatEntitlementService = createDecorator('chatEntitlementService'); + +export enum ChatEntitlement { + /** Signed out */ + Unknown = 1, + /** Signed in but not yet resolved */ + Unresolved, + /** Signed in and entitled to Limited */ + Available, + /** Signed in but not entitled to Limited */ + Unavailable, + /** Signed-up to Limited */ + Limited, + /** Signed-up to Pro */ + Pro +} + +export enum ChatSentiment { + /** Out of the box value */ + Standard = 1, + /** Explicitly disabled/hidden by user */ + Disabled = 2, + /** Extensions installed */ + Installed = 3 +} + +export interface IChatQuotas { + readonly chatQuotaExceeded: boolean; + readonly completionsQuotaExceeded: boolean; + readonly quotaResetDate: Date | undefined; + + readonly chatTotal?: number; + readonly completionsTotal?: number; + + readonly chatRemaining?: number; + readonly completionsRemaining?: number; +} + +export interface IChatEntitlementService { + + _serviceBrand: undefined; + + readonly onDidChangeEntitlement: Event; + + readonly entitlement: ChatEntitlement; + + readonly onDidChangeQuotaExceeded: Event; + readonly onDidChangeQuotaRemaining: Event; + + readonly quotas: IChatQuotas; + + update(token: CancellationToken): Promise; + + readonly onDidChangeSentiment: Event; + + readonly sentiment: ChatSentiment; +} + +//#region Service Implementation + +const defaultChat = { + extensionId: product.defaultChatAgent?.extensionId ?? '', + chatExtensionId: product.defaultChatAgent?.chatExtensionId ?? '', + upgradePlanUrl: product.defaultChatAgent?.upgradePlanUrl ?? '', + providerId: product.defaultChatAgent?.providerId ?? '', + enterpriseProviderId: product.defaultChatAgent?.enterpriseProviderId ?? '', + providerScopes: product.defaultChatAgent?.providerScopes ?? [[]], + entitlementUrl: product.defaultChatAgent?.entitlementUrl ?? '', + entitlementSignupLimitedUrl: product.defaultChatAgent?.entitlementSignupLimitedUrl ?? '', + completionsAdvancedSetting: product.defaultChatAgent?.completionsAdvancedSetting ?? '', + chatQuotaExceededContext: product.defaultChatAgent?.chatQuotaExceededContext ?? '', + completionsQuotaExceededContext: product.defaultChatAgent?.completionsQuotaExceededContext ?? '' +}; + +interface IChatQuotasAccessor { + clearQuotas(): void; + acceptQuotas(quotas: IChatQuotas): void; +} + +export class ChatEntitlementService extends Disposable implements IChatEntitlementService { + + declare _serviceBrand: undefined; + + readonly context: Lazy | undefined; + readonly requests: Lazy | undefined; + + constructor( + @IInstantiationService instantiationService: IInstantiationService, + @IProductService productService: IProductService, + @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, + @IContextKeyService private readonly contextKeyService: IContextKeyService, + ) { + super(); + + this.chatQuotaExceededContextKey = ChatContextKeys.chatQuotaExceeded.bindTo(this.contextKeyService); + this.completionsQuotaExceededContextKey = ChatContextKeys.completionsQuotaExceeded.bindTo(this.contextKeyService); + + this.onDidChangeEntitlement = Event.map( + Event.filter( + this.contextKeyService.onDidChangeContext, e => e.affectsSome(new Set([ + ChatContextKeys.Entitlement.pro.key, + ChatContextKeys.Entitlement.limited.key, + ChatContextKeys.Entitlement.canSignUp.key, + ChatContextKeys.Entitlement.signedOut.key + ])), this._store + ), () => { }, this._store + ); + + this.onDidChangeSentiment = Event.map( + Event.filter( + this.contextKeyService.onDidChangeContext, e => e.affectsSome(new Set([ + ChatContextKeys.Setup.hidden.key, + ChatContextKeys.Setup.installed.key + ])), this._store + ), () => { }, this._store + ); + + if ( + !productService.defaultChatAgent || // needs product config + (isWeb && !environmentService.remoteAuthority) // only enabled locally or a remote backend + ) { + ChatContextKeys.Setup.hidden.bindTo(this.contextKeyService).set(true); // hide copilot UI + return; + } + + const context = this.context = new Lazy(() => this._register(instantiationService.createInstance(ChatEntitlementContext))); + this.requests = new Lazy(() => this._register(instantiationService.createInstance(ChatEntitlementRequests, context.value, { + clearQuotas: () => this.clearQuotas(), + acceptQuotas: quotas => this.acceptQuotas(quotas) + }))); + + this.registerListeners(); + } + + //#region --- Entitlements + + readonly onDidChangeEntitlement: Event; + + get entitlement(): ChatEntitlement { + if (this.contextKeyService.getContextKeyValue(ChatContextKeys.Entitlement.pro.key) === true) { + return ChatEntitlement.Pro; + } else if (this.contextKeyService.getContextKeyValue(ChatContextKeys.Entitlement.limited.key) === true) { + return ChatEntitlement.Limited; + } else if (this.contextKeyService.getContextKeyValue(ChatContextKeys.Entitlement.canSignUp.key) === true) { + return ChatEntitlement.Available; + } else if (this.contextKeyService.getContextKeyValue(ChatContextKeys.Entitlement.signedOut.key) === true) { + return ChatEntitlement.Unknown; + } + + return ChatEntitlement.Unresolved; + } + + //#endregion + + //#region --- Quotas + + private readonly _onDidChangeQuotaExceeded = this._register(new Emitter()); + readonly onDidChangeQuotaExceeded = this._onDidChangeQuotaExceeded.event; + + private readonly _onDidChangeQuotaRemaining = this._register(new Emitter()); + readonly onDidChangeQuotaRemaining = this._onDidChangeQuotaRemaining.event; + + private _quotas: IChatQuotas = { chatQuotaExceeded: false, completionsQuotaExceeded: false, quotaResetDate: undefined }; + get quotas() { return this._quotas; } + + private readonly chatQuotaExceededContextKey: IContextKey; + private readonly completionsQuotaExceededContextKey: IContextKey; + + private ExtensionQuotaContextKeys = { + chatQuotaExceeded: defaultChat.chatQuotaExceededContext, + completionsQuotaExceeded: defaultChat.completionsQuotaExceededContext, + }; + + private registerListeners(): void { + const chatQuotaExceededSet = new Set([this.ExtensionQuotaContextKeys.chatQuotaExceeded]); + const completionsQuotaExceededSet = new Set([this.ExtensionQuotaContextKeys.completionsQuotaExceeded]); + + this._register(this.contextKeyService.onDidChangeContext(e => { + let changed = false; + if (e.affectsSome(chatQuotaExceededSet)) { + const newChatQuotaExceeded = this.contextKeyService.getContextKeyValue(this.ExtensionQuotaContextKeys.chatQuotaExceeded); + if (typeof newChatQuotaExceeded === 'boolean' && newChatQuotaExceeded !== this._quotas.chatQuotaExceeded) { + this._quotas = { + ...this._quotas, + chatQuotaExceeded: newChatQuotaExceeded, + }; + changed = true; + } + } + + if (e.affectsSome(completionsQuotaExceededSet)) { + const newCompletionsQuotaExceeded = this.contextKeyService.getContextKeyValue(this.ExtensionQuotaContextKeys.completionsQuotaExceeded); + if (typeof newCompletionsQuotaExceeded === 'boolean' && newCompletionsQuotaExceeded !== this._quotas.completionsQuotaExceeded) { + this._quotas = { + ...this._quotas, + completionsQuotaExceeded: newCompletionsQuotaExceeded, + }; + changed = true; + } + } + + if (changed) { + this.updateContextKeys(); + this._onDidChangeQuotaExceeded.fire(); + } + })); + } + + acceptQuotas(quotas: IChatQuotas): void { + const oldQuota = this._quotas; + this._quotas = quotas; + this.updateContextKeys(); + + if ( + oldQuota.chatQuotaExceeded !== this._quotas.chatQuotaExceeded || + oldQuota.completionsQuotaExceeded !== this._quotas.completionsQuotaExceeded + ) { + this._onDidChangeQuotaExceeded.fire(); + } + + if ( + oldQuota.chatRemaining !== this._quotas.chatRemaining || + oldQuota.completionsRemaining !== this._quotas.completionsRemaining + ) { + this._onDidChangeQuotaRemaining.fire(); + } + } + + clearQuotas(): void { + if (this.quotas.chatQuotaExceeded || this.quotas.completionsQuotaExceeded) { + this.acceptQuotas({ chatQuotaExceeded: false, completionsQuotaExceeded: false, quotaResetDate: undefined }); + } + } + + private updateContextKeys(): void { + this.chatQuotaExceededContextKey.set(this._quotas.chatQuotaExceeded); + this.completionsQuotaExceededContextKey.set(this._quotas.completionsQuotaExceeded); + } + + //#endregion + + //#region --- Sentiment + + private readonly _onDidChangeSentiment = this._register(new Emitter()); + readonly onDidChangeSentiment = this._onDidChangeSentiment.event; + + get sentiment(): ChatSentiment { + if (this.contextKeyService.getContextKeyValue(ChatContextKeys.Setup.installed.key) === true) { + return ChatSentiment.Installed; + } else if (this.contextKeyService.getContextKeyValue(ChatContextKeys.Setup.hidden.key) === true) { + return ChatSentiment.Disabled; + } + + return ChatSentiment.Standard; + } + + //#endregion + + async update(token: CancellationToken): Promise { + await this.requests?.value.forceResolveEntitlement(undefined, token); + } +} + +//#endregion + +//#region Chat Entitlement Request Service + +type EntitlementClassification = { + tid: { classification: 'EndUserPseudonymizedInformation'; purpose: 'BusinessInsight'; comment: 'The anonymized analytics id returned by the service'; endpoint: 'GoogleAnalyticsId' }; + entitlement: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Flag indicating the chat entitlement state' }; + quotaChat: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The number of chat completions available to the user' }; + quotaCompletions: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The number of chat completions available to the user' }; + quotaResetDate: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The date the quota will reset' }; + owner: 'bpasero'; + comment: 'Reporting chat entitlements'; +}; + +type EntitlementEvent = { + entitlement: ChatEntitlement; + tid: string; + quotaChat: number | undefined; + quotaCompletions: number | undefined; + quotaResetDate: string | undefined; +}; + +interface IEntitlementsResponse { + readonly access_type_sku: string; + readonly assigned_date: string; + readonly can_signup_for_limited: boolean; + readonly chat_enabled: boolean; + readonly analytics_tracking_id: string; + readonly limited_user_quotas?: { + readonly chat: number; + readonly completions: number; + }; + readonly monthly_quotas?: { + readonly chat: number; + readonly completions: number; + }; + readonly limited_user_reset_date: string; +} + +interface IEntitlements { + readonly entitlement: ChatEntitlement; + readonly quotas?: IQuotas; +} + +interface IQuotas { + readonly chatTotal?: number; + readonly completionsTotal?: number; + + readonly chatRemaining?: number; + readonly completionsRemaining?: number; + + readonly resetDate?: string; +} + +export class ChatEntitlementRequests extends Disposable { + + static providerId(configurationService: IConfigurationService): string { + if (configurationService.getValue(`${defaultChat.completionsAdvancedSetting}.authProvider`) === defaultChat.enterpriseProviderId) { + return defaultChat.enterpriseProviderId; + } + + return defaultChat.providerId; + } + + private state: IEntitlements; + + private pendingResolveCts = new CancellationTokenSource(); + private didResolveEntitlements = false; + + constructor( + private readonly context: ChatEntitlementContext, + private readonly chatQuotasAccessor: IChatQuotasAccessor, + @ITelemetryService private readonly telemetryService: ITelemetryService, + @IAuthenticationService private readonly authenticationService: IAuthenticationService, + @ILogService private readonly logService: ILogService, + @IRequestService private readonly requestService: IRequestService, + @IDialogService private readonly dialogService: IDialogService, + @IOpenerService private readonly openerService: IOpenerService, + @IConfigurationService private readonly configurationService: IConfigurationService, + @IAuthenticationExtensionsService private readonly authenticationExtensionsService: IAuthenticationExtensionsService, + ) { + super(); + + this.state = { entitlement: this.context.state.entitlement }; + + this.registerListeners(); + + this.resolve(); + } + + private registerListeners(): void { + this._register(this.authenticationService.onDidChangeDeclaredProviders(() => this.resolve())); + + this._register(this.authenticationService.onDidChangeSessions(e => { + if (e.providerId === ChatEntitlementRequests.providerId(this.configurationService)) { + this.resolve(); + } + })); + + this._register(this.authenticationService.onDidRegisterAuthenticationProvider(e => { + if (e.id === ChatEntitlementRequests.providerId(this.configurationService)) { + this.resolve(); + } + })); + + this._register(this.authenticationService.onDidUnregisterAuthenticationProvider(e => { + if (e.id === ChatEntitlementRequests.providerId(this.configurationService)) { + this.resolve(); + } + })); + + this._register(this.context.onDidChange(() => { + if (!this.context.state.installed || this.context.state.entitlement === ChatEntitlement.Unknown) { + // When the extension is not installed or the user is not entitled + // make sure to clear quotas so that any indicators are also gone + this.state = { entitlement: this.state.entitlement, quotas: undefined }; + this.chatQuotasAccessor.clearQuotas(); + } + })); + } + + private async resolve(): Promise { + this.pendingResolveCts.dispose(true); + const cts = this.pendingResolveCts = new CancellationTokenSource(); + + const session = await this.findMatchingProviderSession(cts.token); + if (cts.token.isCancellationRequested) { + return; + } + + // Immediately signal whether we have a session or not + let state: IEntitlements | undefined = undefined; + if (session) { + // Do not overwrite any state we have already + if (this.state.entitlement === ChatEntitlement.Unknown) { + state = { entitlement: ChatEntitlement.Unresolved }; + } + } else { + this.didResolveEntitlements = false; // reset so that we resolve entitlements fresh when signed in again + state = { entitlement: ChatEntitlement.Unknown }; + } + if (state) { + this.update(state); + } + + if (session && !this.didResolveEntitlements) { + // Afterwards resolve entitlement with a network request + // but only unless it was not already resolved before. + await this.resolveEntitlement(session, cts.token); + } + } + + private async findMatchingProviderSession(token: CancellationToken): Promise { + const sessions = await this.doGetSessions(ChatEntitlementRequests.providerId(this.configurationService)); + if (token.isCancellationRequested) { + return undefined; + } + + for (const session of sessions) { + for (const scopes of defaultChat.providerScopes) { + if (this.scopesMatch(session.scopes, scopes)) { + return session; + } + } + } + + return undefined; + } + + private async doGetSessions(providerId: string): Promise { + try { + return await this.authenticationService.getSessions(providerId); + } catch (error) { + // ignore - errors can throw if a provider is not registered + } + + return []; + } + + private scopesMatch(scopes: ReadonlyArray, expectedScopes: string[]): boolean { + return scopes.length === expectedScopes.length && expectedScopes.every(scope => scopes.includes(scope)); + } + + private async resolveEntitlement(session: AuthenticationSession, token: CancellationToken): Promise { + const entitlements = await this.doResolveEntitlement(session, token); + if (typeof entitlements?.entitlement === 'number' && !token.isCancellationRequested) { + this.didResolveEntitlements = true; + this.update(entitlements); + } + + return entitlements; + } + + private async doResolveEntitlement(session: AuthenticationSession, token: CancellationToken): Promise { + if (ChatEntitlementRequests.providerId(this.configurationService) === defaultChat.enterpriseProviderId) { + this.logService.trace('[chat entitlement]: enterprise provider, assuming Pro'); + return { entitlement: ChatEntitlement.Pro }; + } + + if (token.isCancellationRequested) { + return undefined; + } + + const response = await this.request(defaultChat.entitlementUrl, 'GET', undefined, session, token); + if (token.isCancellationRequested) { + return undefined; + } + + if (!response) { + this.logService.trace('[chat entitlement]: no response'); + return { entitlement: ChatEntitlement.Unresolved }; + } + + if (response.res.statusCode && response.res.statusCode !== 200) { + this.logService.trace(`[chat entitlement]: unexpected status code ${response.res.statusCode}`); + return { entitlement: ChatEntitlement.Unresolved }; + } + + let responseText: string | null = null; + try { + responseText = await asText(response); + } catch (error) { + // ignore - handled below + } + if (token.isCancellationRequested) { + return undefined; + } + + if (!responseText) { + this.logService.trace('[chat entitlement]: response has no content'); + return { entitlement: ChatEntitlement.Unresolved }; + } + + let entitlementsResponse: IEntitlementsResponse; + try { + entitlementsResponse = JSON.parse(responseText); + this.logService.trace(`[chat entitlement]: parsed result is ${JSON.stringify(entitlementsResponse)}`); + } catch (err) { + this.logService.trace(`[chat entitlement]: error parsing response (${err})`); + return { entitlement: ChatEntitlement.Unresolved }; + } + + let entitlement: ChatEntitlement; + if (entitlementsResponse.access_type_sku === 'free_limited_copilot') { + entitlement = ChatEntitlement.Limited; + } else if (entitlementsResponse.can_signup_for_limited) { + entitlement = ChatEntitlement.Available; + } else if (entitlementsResponse.chat_enabled) { + entitlement = ChatEntitlement.Pro; + } else { + entitlement = ChatEntitlement.Unavailable; + } + + const chatRemaining = entitlementsResponse.limited_user_quotas?.chat; + const completionsRemaining = entitlementsResponse.limited_user_quotas?.completions; + + const entitlements: IEntitlements = { + entitlement, + quotas: { + chatTotal: entitlementsResponse.monthly_quotas?.chat, + completionsTotal: entitlementsResponse.monthly_quotas?.completions, + chatRemaining: typeof chatRemaining === 'number' ? Math.max(0, chatRemaining) : undefined, + completionsRemaining: typeof completionsRemaining === 'number' ? Math.max(0, completionsRemaining) : undefined, + resetDate: entitlementsResponse.limited_user_reset_date + } + }; + + this.logService.trace(`[chat entitlement]: resolved to ${entitlements.entitlement}, quotas: ${JSON.stringify(entitlements.quotas)}`); + this.telemetryService.publicLog2('chatInstallEntitlement', { + entitlement: entitlements.entitlement, + tid: entitlementsResponse.analytics_tracking_id, + quotaChat: entitlementsResponse.limited_user_quotas?.chat, + quotaCompletions: entitlementsResponse.limited_user_quotas?.completions, + quotaResetDate: entitlementsResponse.limited_user_reset_date + }); + + return entitlements; + } + + private async request(url: string, type: 'GET', body: undefined, session: AuthenticationSession, token: CancellationToken): Promise; + private async request(url: string, type: 'POST', body: object, session: AuthenticationSession, token: CancellationToken): Promise; + private async request(url: string, type: 'GET' | 'POST', body: object | undefined, session: AuthenticationSession, token: CancellationToken): Promise { + try { + return await this.requestService.request({ + type, + url, + data: type === 'POST' ? JSON.stringify(body) : undefined, + disableCache: true, + headers: { + 'Authorization': `Bearer ${session.accessToken}` + } + }, token); + } catch (error) { + if (!token.isCancellationRequested) { + this.logService.error(`[chat entitlement] request: error ${error}`); + } + + return undefined; + } + } + + private update(state: IEntitlements): void { + this.state = state; + + this.context.update({ entitlement: this.state.entitlement }); + + if (state.quotas) { + this.chatQuotasAccessor.acceptQuotas({ + chatQuotaExceeded: typeof state.quotas.chatRemaining === 'number' ? state.quotas.chatRemaining <= 0 : false, + completionsQuotaExceeded: typeof state.quotas.completionsRemaining === 'number' ? state.quotas.completionsRemaining <= 0 : false, + quotaResetDate: state.quotas.resetDate ? new Date(state.quotas.resetDate) : undefined, + chatTotal: state.quotas.chatTotal, + completionsTotal: state.quotas.completionsTotal, + chatRemaining: state.quotas.chatRemaining, + completionsRemaining: state.quotas.completionsRemaining + }); + } + } + + async forceResolveEntitlement(session: AuthenticationSession | undefined, token = CancellationToken.None): Promise { + if (!session) { + session = await this.findMatchingProviderSession(token); + } + + if (!session) { + return undefined; + } + + return this.resolveEntitlement(session, token); + } + + async signUpLimited(session: AuthenticationSession): Promise { + const body = { + restricted_telemetry: this.telemetryService.telemetryLevel === TelemetryLevel.NONE ? 'disabled' : 'enabled', + public_code_suggestions: 'enabled' + }; + + const response = await this.request(defaultChat.entitlementSignupLimitedUrl, 'POST', body, session, CancellationToken.None); + if (!response) { + const retry = await this.onUnknownSignUpError(localize('signUpNoResponseError', "No response received."), '[chat entitlement] sign-up: no response'); + return retry ? this.signUpLimited(session) : { errorCode: 1 }; + } + + if (response.res.statusCode && response.res.statusCode !== 200) { + if (response.res.statusCode === 422) { + try { + const responseText = await asText(response); + if (responseText) { + const responseError: { message: string } = JSON.parse(responseText); + if (typeof responseError.message === 'string' && responseError.message) { + this.onUnprocessableSignUpError(`[chat entitlement] sign-up: unprocessable entity (${responseError.message})`, responseError.message); + return { errorCode: response.res.statusCode }; + } + } + } catch (error) { + // ignore - handled below + } + } + const retry = await this.onUnknownSignUpError(localize('signUpUnexpectedStatusError', "Unexpected status code {0}.", response.res.statusCode), `[chat entitlement] sign-up: unexpected status code ${response.res.statusCode}`); + return retry ? this.signUpLimited(session) : { errorCode: response.res.statusCode }; + } + + let responseText: string | null = null; + try { + responseText = await asText(response); + } catch (error) { + // ignore - handled below + } + + if (!responseText) { + const retry = await this.onUnknownSignUpError(localize('signUpNoResponseContentsError', "Response has no contents."), '[chat entitlement] sign-up: response has no content'); + return retry ? this.signUpLimited(session) : { errorCode: 2 }; + } + + let parsedResult: { subscribed: boolean } | undefined = undefined; + try { + parsedResult = JSON.parse(responseText); + this.logService.trace(`[chat entitlement] sign-up: response is ${responseText}`); + } catch (err) { + const retry = await this.onUnknownSignUpError(localize('signUpInvalidResponseError', "Invalid response contents."), `[chat entitlement] sign-up: error parsing response (${err})`); + return retry ? this.signUpLimited(session) : { errorCode: 3 }; + } + + // We have made it this far, so the user either did sign-up or was signed-up already. + // That is, because the endpoint throws in all other case according to Patrick. + this.update({ entitlement: ChatEntitlement.Limited }); + + return Boolean(parsedResult?.subscribed); + } + + private async onUnknownSignUpError(detail: string, logMessage: string): Promise { + this.logService.error(logMessage); + + const { confirmed } = await this.dialogService.confirm({ + type: Severity.Error, + message: localize('unknownSignUpError', "An error occurred while signing up for Copilot Free. Would you like to try again?"), + detail, + primaryButton: localize('retry', "Retry") + }); + + return confirmed; + } + + private onUnprocessableSignUpError(logMessage: string, logDetails: string): void { + this.logService.error(logMessage); + + this.dialogService.prompt({ + type: Severity.Error, + message: localize('unprocessableSignUpError', "An error occurred while signing up for Copilot Free."), + detail: logDetails, + buttons: [ + { + label: localize('ok', "OK"), + run: () => { /* noop */ } + }, + { + label: localize('learnMore', "Learn More"), + run: () => this.openerService.open(URI.parse(defaultChat.upgradePlanUrl)) + } + ] + }); + } + + async signIn() { + const providerId = ChatEntitlementRequests.providerId(this.configurationService); + const session = await this.authenticationService.createSession(providerId, defaultChat.providerScopes[0]); + + this.authenticationExtensionsService.updateAccountPreference(defaultChat.extensionId, providerId, session.account); + this.authenticationExtensionsService.updateAccountPreference(defaultChat.chatExtensionId, providerId, session.account); + + const entitlements = await this.forceResolveEntitlement(session); + + return { session, entitlements }; + } + + override dispose(): void { + this.pendingResolveCts.dispose(true); + + super.dispose(); + } +} + +//#endregion + +//#region Context + +export interface IChatEntitlementContextState { + entitlement: ChatEntitlement; + hidden?: boolean; + installed?: boolean; + registered?: boolean; +} + +export class ChatEntitlementContext extends Disposable { + + private static readonly CHAT_ENTITLEMENT_CONTEXT_STORAGE_KEY = 'chat.setupContext'; + + private readonly canSignUpContextKey: IContextKey; + private readonly signedOutContextKey: IContextKey; + private readonly limitedContextKey: IContextKey; + private readonly proContextKey: IContextKey; + private readonly hiddenContext: IContextKey; + private readonly installedContext: IContextKey; + + private _state: IChatEntitlementContextState; + private suspendedState: IChatEntitlementContextState | undefined = undefined; + get state(): IChatEntitlementContextState { + return this.suspendedState ?? this._state; + } + + private readonly _onDidChange = this._register(new Emitter()); + readonly onDidChange = this._onDidChange.event; + + private updateBarrier: Barrier | undefined = undefined; + + constructor( + @IContextKeyService contextKeyService: IContextKeyService, + @IStorageService private readonly storageService: IStorageService, + @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, + @IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService, + @ILogService private readonly logService: ILogService, + @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, + ) { + super(); + + this.canSignUpContextKey = ChatContextKeys.Entitlement.canSignUp.bindTo(contextKeyService); + this.signedOutContextKey = ChatContextKeys.Entitlement.signedOut.bindTo(contextKeyService); + this.limitedContextKey = ChatContextKeys.Entitlement.limited.bindTo(contextKeyService); + this.proContextKey = ChatContextKeys.Entitlement.pro.bindTo(contextKeyService); + this.hiddenContext = ChatContextKeys.Setup.hidden.bindTo(contextKeyService); + this.installedContext = ChatContextKeys.Setup.installed.bindTo(contextKeyService); + + this._state = this.storageService.getObject(ChatEntitlementContext.CHAT_ENTITLEMENT_CONTEXT_STORAGE_KEY, StorageScope.PROFILE) ?? { entitlement: ChatEntitlement.Unknown }; + + this.checkExtensionInstallation(); + this.updateContextSync(); + } + + private async checkExtensionInstallation(): Promise { + + // Await extensions to be ready to be queried + await this.extensionsWorkbenchService.queryLocal(); + + // Listen to change and process extensions once + this._register(Event.runAndSubscribe(this.extensionsWorkbenchService.onChange, e => { + if (e && !ExtensionIdentifier.equals(e.identifier.id, defaultChat.extensionId)) { + return; // unrelated event + } + + const defaultChatExtension = this.extensionsWorkbenchService.local.find(value => ExtensionIdentifier.equals(value.identifier.id, defaultChat.extensionId)); + this.update({ installed: !!defaultChatExtension?.local && this.extensionEnablementService.isEnabled(defaultChatExtension.local) }); + })); + } + + update(context: { installed: boolean }): Promise; + update(context: { hidden: boolean }): Promise; + update(context: { entitlement: ChatEntitlement }): Promise; + update(context: { installed?: boolean; hidden?: boolean; entitlement?: ChatEntitlement }): Promise { + this.logService.trace(`[chat entitlement context] update(): ${JSON.stringify(context)}`); + + if (typeof context.installed === 'boolean') { + this._state.installed = context.installed; + + if (context.installed) { + context.hidden = false; // allows to fallback if the extension is uninstalled + } + } + + if (typeof context.hidden === 'boolean') { + this._state.hidden = context.hidden; + } + + if (typeof context.entitlement === 'number') { + this._state.entitlement = context.entitlement; + + if (this._state.entitlement === ChatEntitlement.Limited || this._state.entitlement === ChatEntitlement.Pro) { + this._state.registered = true; + } else if (this._state.entitlement === ChatEntitlement.Available) { + this._state.registered = false; // only reset when signed-in user can sign-up for limited + } + } + + this.storageService.store(ChatEntitlementContext.CHAT_ENTITLEMENT_CONTEXT_STORAGE_KEY, this._state, StorageScope.PROFILE, StorageTarget.MACHINE); + + return this.updateContext(); + } + + private async updateContext(): Promise { + await this.updateBarrier?.wait(); + + this.updateContextSync(); + } + + private updateContextSync(): void { + this.logService.trace(`[chat entitlement context] updateContext(): ${JSON.stringify(this._state)}`); + + if (!this._state.hidden && !this._state.installed) { + // this is ugly but fixes flicker from a previous chat install + this.storageService.remove('chat.welcomeMessageContent.panel', StorageScope.APPLICATION); + this.storageService.remove('interactive.sessions', this.workspaceContextService.getWorkspace().folders.length ? StorageScope.WORKSPACE : StorageScope.APPLICATION); + } + + this.signedOutContextKey.set(this._state.entitlement === ChatEntitlement.Unknown); + this.canSignUpContextKey.set(this._state.entitlement === ChatEntitlement.Available); + this.limitedContextKey.set(this._state.entitlement === ChatEntitlement.Limited); + this.proContextKey.set(this._state.entitlement === ChatEntitlement.Pro); + this.hiddenContext.set(!!this._state.hidden); + this.installedContext.set(!!this._state.installed); + + this._onDidChange.fire(); + } + + suspend(): void { + this.suspendedState = { ...this._state }; + this.updateBarrier = new Barrier(); + } + + resume(): void { + this.suspendedState = undefined; + this.updateBarrier?.open(); + this.updateBarrier = undefined; + } +} + +//#endregion diff --git a/src/vs/workbench/contrib/chat/common/chatModel.ts b/src/vs/workbench/contrib/chat/common/chatModel.ts index dec4623cd07f..9b276befbe82 100644 --- a/src/vs/workbench/contrib/chat/common/chatModel.ts +++ b/src/vs/workbench/contrib/chat/common/chatModel.ts @@ -5,6 +5,7 @@ import { asArray } from '../../../../base/common/arrays.js'; import { DeferredPromise } from '../../../../base/common/async.js'; +import { Codicon } from '../../../../base/common/codicons.js'; import { Emitter, Event } from '../../../../base/common/event.js'; import { IMarkdownString, MarkdownString, isMarkdownString } from '../../../../base/common/htmlContent.js'; import { Disposable, IDisposable } from '../../../../base/common/lifecycle.js'; @@ -21,18 +22,19 @@ import { IRange } from '../../../../editor/common/core/range.js'; import { Location, SymbolKind, TextEdit } from '../../../../editor/common/languages.js'; import { localize } from '../../../../nls.js'; import { ILogService } from '../../../../platform/log/common/log.js'; -import { ICellEditOperation } from '../../notebook/common/notebookCommon.js'; -import { ChatAgentLocation, IChatAgentCommand, IChatAgentData, IChatAgentResult, IChatAgentService, IChatWelcomeMessageContent, reviveSerializedAgent } from './chatAgents.js'; +import { IMarker, MarkerSeverity } from '../../../../platform/markers/common/markers.js'; +import { CellUri, ICellEditOperation } from '../../notebook/common/notebookCommon.js'; +import { IChatAgentCommand, IChatAgentData, IChatAgentResult, IChatAgentService, reviveSerializedAgent } from './chatAgents.js'; import { ChatRequestTextPart, IParsedChatRequest, reviveParsedChatRequest } from './chatParserTypes.js'; import { ChatAgentVoteDirection, ChatAgentVoteDownReason, IChatAgentMarkdownContentWithVulnerability, IChatCodeCitation, IChatCommandButton, IChatConfirmation, IChatContentInlineReference, IChatContentReference, IChatFollowup, IChatLocationData, IChatMarkdownContent, IChatNotebookEdit, IChatProgress, IChatProgressMessage, IChatResponseCodeblockUriPart, IChatResponseProgressFileTreeData, IChatTask, IChatTextEdit, IChatToolInvocation, IChatToolInvocationSerialized, IChatTreeData, IChatUndoStop, IChatUsedContext, IChatWarningMessage, isIUsedContext } from './chatService.js'; import { IChatRequestVariableValue } from './chatVariables.js'; +import { ChatAgentLocation } from './constants.js'; export interface IBaseChatRequestVariableEntry { id: string; fullName?: string; icon?: ThemeIcon; name: string; - isMarkedReadonly?: boolean; modelDescription?: string; range?: IOffsetRange; value: IChatRequestVariableValue; @@ -87,7 +89,80 @@ export interface ILinkVariableEntry extends Omit { + readonly kind: 'image'; + readonly isPasted?: boolean; + readonly isURL?: boolean; +} + +export interface IDiagnosticVariableEntryFilterData { + readonly owner?: string; + readonly problemMessage?: string; + readonly filterUri?: URI; + readonly filterSeverity?: MarkerSeverity; + readonly filterRange?: IRange; +} + +export namespace IDiagnosticVariableEntryFilterData { + export const icon = Codicon.error; + + export function fromMarker(marker: IMarker): IDiagnosticVariableEntryFilterData { + return { + filterUri: marker.resource, + owner: marker.owner, + problemMessage: marker.message, + filterRange: { startLineNumber: marker.startLineNumber, endLineNumber: marker.endLineNumber, startColumn: marker.startColumn, endColumn: marker.endColumn } + }; + } + + export function toEntry(data: IDiagnosticVariableEntryFilterData): IDiagnosticVariableEntry { + return { + id: id(data), + name: label(data), + icon, + value: data, + kind: 'diagnostic' as const, + range: data.filterRange ? new OffsetRange(data.filterRange.startLineNumber, data.filterRange.endLineNumber) : undefined, + ...data, + }; + } + + export function id(data: IDiagnosticVariableEntryFilterData) { + return [data.filterUri, data.owner, data.filterSeverity, data.filterRange?.startLineNumber].join(':'); + } + + export function label(data: IDiagnosticVariableEntryFilterData) { + const enum TrimThreshold { + MaxChars = 30, + MaxSpaceLookback = 10, + } + if (data.problemMessage) { + if (data.problemMessage.length < TrimThreshold.MaxChars) { + return data.problemMessage; + } + + // Trim the message, on a space if it would not lose too much + // data (MaxSpaceLookback) or just blindly otherwise. + const lastSpace = data.problemMessage.lastIndexOf(' ', TrimThreshold.MaxChars); + if (lastSpace === -1 || lastSpace + TrimThreshold.MaxSpaceLookback < TrimThreshold.MaxChars) { + return data.problemMessage.substring(0, TrimThreshold.MaxChars) + '…'; + } + return data.problemMessage.substring(0, lastSpace) + '…'; + } + let labelStr = localize('chat.attachment.problems.all', "All Problems"); + if (data.filterUri) { + labelStr = localize('chat.attachment.problems.inFile', "Problems in {0}", basename(data.filterUri)); + } + + return labelStr; + } +} + +export interface IDiagnosticVariableEntry extends Omit, IDiagnosticVariableEntryFilterData { + readonly kind: 'diagnostic'; +} + +export type IChatRequestVariableEntry = IChatRequestImplicitVariableEntry | IChatRequestPasteVariableEntry | ISymbolVariableEntry | ICommandResultVariableEntry | ILinkVariableEntry | IBaseChatRequestVariableEntry | IDiagnosticVariableEntry | IImageVariableEntry; export function isImplicitVariableEntry(obj: IChatRequestVariableEntry): obj is IChatRequestImplicitVariableEntry { return obj.kind === 'implicit'; @@ -101,6 +176,14 @@ export function isLinkVariableEntry(obj: IChatRequestVariableEntry): obj is ILin return obj.kind === 'link'; } +export function isImageVariableEntry(obj: IChatRequestVariableEntry): obj is IImageVariableEntry { + return obj.kind === 'image'; +} + +export function isDiagnosticsVariableEntry(obj: IChatRequestVariableEntry): obj is IDiagnosticVariableEntry { + return obj.kind === 'diagnostic'; +} + export function isChatRequestVariableEntry(obj: unknown): obj is IChatRequestVariableEntry { const entry = obj as IChatRequestVariableEntry; return typeof entry === 'object' && @@ -143,9 +226,19 @@ export interface IChatTextEditGroup { done: boolean | undefined; } +export function isCellTextEditOperation(value: unknown): value is ICellTextEditOperation { + const candidate = value as ICellTextEditOperation; + return !!candidate && !!candidate.edit && !!candidate.uri && URI.isUri(candidate.uri); +} + +export interface ICellTextEditOperation { + edit: TextEdit; + uri: URI; +} + export interface IChatNotebookEditGroup { uri: URI; - edits: ICellEditOperation[][]; + edits: (ICellTextEditOperation | ICellEditOperation)[]; state?: IChatTextEditGroupState; kind: 'notebookEditGroup'; done: boolean | undefined; @@ -484,13 +577,19 @@ export class Response extends AbstractResponse implements IDisposable { } this._updateRepr(quiet); } else if (progress.kind === 'textEdit' || progress.kind === 'notebookEdit') { + // If the progress.uri is a cell Uri, its possible its part of the inline chat. + // Old approach of notebook inline chat would not start and end with notebook Uri, so we need to check for old approach. + const useOldApproachForInlineNotebook = progress.uri.scheme === Schemas.vscodeNotebookCell && !this._responseParts.find(part => part.kind === 'notebookEditGroup'); // merge edits for the same file no matter when they come in - const groupKind = progress.kind === 'textEdit' ? 'textEditGroup' : 'notebookEditGroup'; + const notebookUri = useOldApproachForInlineNotebook ? undefined : CellUri.parse(progress.uri)?.notebook; + const uri = notebookUri ?? progress.uri; let found = false; + const groupKind = progress.kind === 'textEdit' && !notebookUri ? 'textEditGroup' : 'notebookEditGroup'; + const edits: any = groupKind === 'textEditGroup' ? progress.edits : progress.edits.map(edit => TextEdit.isTextEdit(edit) ? { uri: progress.uri, edit } : edit); for (let i = 0; !found && i < this._responseParts.length; i++) { const candidate = this._responseParts[i]; - if (candidate.kind === groupKind && !candidate.done && isEqual(candidate.uri, progress.uri)) { - candidate.edits.push(progress.edits as any); + if (candidate.kind === groupKind && !candidate.done && isEqual(candidate.uri, uri)) { + candidate.edits.push(edits); candidate.done = progress.done; found = true; } @@ -498,8 +597,8 @@ export class Response extends AbstractResponse implements IDisposable { if (!found) { this._responseParts.push({ kind: groupKind, - uri: progress.uri, - edits: [progress.edits as any], + uri, + edits: groupKind === 'textEditGroup' ? [edits] : edits, done: progress.done }); } @@ -849,7 +948,6 @@ export interface IChatModel { readonly initState: ChatModelInitState; readonly initialLocation: ChatAgentLocation; readonly title: string; - readonly welcomeMessage: IChatWelcomeMessageContent | undefined; readonly sampleQuestions: IChatFollowup[] | undefined; readonly requestInProgress: boolean; readonly requestPausibility: ChatPauseState; @@ -1016,6 +1114,7 @@ export type IChatChangeEvent = | IChatSetAgentEvent | IChatMoveEvent | IChatSetHiddenEvent + | IChatCompletedRequestEvent ; export interface IChatAddRequestEvent { @@ -1028,6 +1127,11 @@ export interface IChatChangedRequestEvent { request: IChatRequestModel; } +export interface IChatCompletedRequestEvent { + kind: 'completedRequest'; + request: IChatRequestModel; +} + export interface IChatAddResponseEvent { kind: 'addResponse'; response: IChatResponseModel; @@ -1103,11 +1207,6 @@ export class ChatModel extends Disposable implements IChatModel { private _initState: ChatModelInitState = ChatModelInitState.Created; private _isInitializedDeferred = new DeferredPromise(); - private _welcomeMessage: IChatWelcomeMessageContent | undefined; - get welcomeMessage(): IChatWelcomeMessageContent | undefined { - return this._welcomeMessage; - } - private _sampleQuestions: IChatFollowup[] | undefined; get sampleQuestions(): IChatFollowup[] | undefined { return this._sampleQuestions; @@ -1329,14 +1428,13 @@ export class ChatModel extends Disposable implements IChatModel { this._isInitializedDeferred = new DeferredPromise(); } - initialize(welcomeMessage?: IChatWelcomeMessageContent, sampleQuestions?: IChatFollowup[]): void { + initialize(sampleQuestions?: IChatFollowup[]): void { if (this.initState !== ChatModelInitState.Initializing) { // Must call startInitialize before initialize, and only call it once throw new Error(`ChatModel is in the wrong state for initialize: ${ChatModelInitState[this.initState]}`); } this._initState = ChatModelInitState.Initialized; - this._welcomeMessage = welcomeMessage; this._sampleQuestions = sampleQuestions; this._isInitializedDeferred.complete(); @@ -1487,6 +1585,7 @@ export class ChatModel extends Disposable implements IChatModel { } request.response.complete(); + this._onDidChange.fire({ kind: 'completedRequest', request }); } setFollowups(request: ChatRequestModel, followups: IChatFollowup[] | undefined): void { diff --git a/src/vs/workbench/contrib/chat/common/chatParserTypes.ts b/src/vs/workbench/contrib/chat/common/chatParserTypes.ts index 0dff77c6e12e..2542fa4ca674 100644 --- a/src/vs/workbench/contrib/chat/common/chatParserTypes.ts +++ b/src/vs/workbench/contrib/chat/common/chatParserTypes.ts @@ -7,9 +7,11 @@ import { revive } from '../../../../base/common/marshalling.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; import { IOffsetRange, OffsetRange } from '../../../../editor/common/core/offsetRange.js'; import { IRange } from '../../../../editor/common/core/range.js'; -import { ChatAgentLocation, IChatAgentCommand, IChatAgentData, IChatAgentService, reviveSerializedAgent } from './chatAgents.js'; +import { IChatAgentCommand, IChatAgentData, IChatAgentService, reviveSerializedAgent } from './chatAgents.js'; +import { IChatRequestVariableEntry, IDiagnosticVariableEntryFilterData } from './chatModel.js'; import { IChatSlashData } from './chatSlashCommands.js'; -import { IChatRequestVariableValue } from './chatVariables.js'; +import { IChatRequestProblemsVariable, IChatRequestVariableValue } from './chatVariables.js'; +import { ChatAgentLocation } from './constants.js'; import { IToolData } from './languageModelToolsService.js'; // These are in a separate file to avoid circular dependencies with the dependencies of the parser @@ -84,6 +86,10 @@ export class ChatRequestToolPart implements IParsedChatRequestPart { get promptText(): string { return this.text; } + + toVariableEntry(): IChatRequestVariableEntry { + return { id: this.toolId, name: this.toolName, range: this.range, value: undefined, isTool: true, icon: ThemeIcon.isThemeIcon(this.icon) ? this.icon : undefined, fullName: this.displayName }; + } } /** @@ -143,7 +149,7 @@ export class ChatRequestSlashCommandPart implements IParsedChatRequestPart { export class ChatRequestDynamicVariablePart implements IParsedChatRequestPart { static readonly Kind = 'dynamic'; readonly kind = ChatRequestDynamicVariablePart.Kind; - constructor(readonly range: OffsetRange, readonly editorRange: IRange, readonly text: string, readonly id: string, readonly modelDescription: string | undefined, readonly data: IChatRequestVariableValue, readonly fullName?: string, readonly icon?: ThemeIcon, readonly isFile?: boolean) { } + constructor(readonly range: OffsetRange, readonly editorRange: IRange, readonly text: string, readonly id: string, readonly modelDescription: string | undefined, readonly data: IChatRequestVariableValue, readonly fullName?: string, readonly icon?: ThemeIcon, readonly isFile?: boolean, readonly isDirectory?: boolean) { } get referenceText(): string { return this.text.replace(chatVariableLeader, ''); @@ -152,6 +158,14 @@ export class ChatRequestDynamicVariablePart implements IParsedChatRequestPart { get promptText(): string { return this.text; } + + toVariableEntry(): IChatRequestVariableEntry { + if (this.id === 'vscode.problems') { + return IDiagnosticVariableEntryFilterData.toEntry((this.data as IChatRequestProblemsVariable).filter); + } + + return { id: this.id, name: this.referenceText, range: this.range, value: this.data, fullName: this.fullName, icon: this.icon, isFile: this.isFile, isDirectory: this.isDirectory }; + } } export function reviveParsedChatRequest(serialized: IParsedChatRequest): IParsedChatRequest { @@ -212,7 +226,8 @@ export function reviveParsedChatRequest(serialized: IParsedChatRequest): IParsed revive((part as ChatRequestDynamicVariablePart).data), (part as ChatRequestDynamicVariablePart).fullName, (part as ChatRequestDynamicVariablePart).icon, - (part as ChatRequestDynamicVariablePart).isFile + (part as ChatRequestDynamicVariablePart).isFile, + (part as ChatRequestDynamicVariablePart).isDirectory ); } else { throw new Error(`Unknown chat request part: ${part.kind}`); diff --git a/src/vs/workbench/contrib/chat/common/chatParticipantContribTypes.ts b/src/vs/workbench/contrib/chat/common/chatParticipantContribTypes.ts index 401211b3c4f1..92549dc2480d 100644 --- a/src/vs/workbench/contrib/chat/common/chatParticipantContribTypes.ts +++ b/src/vs/workbench/contrib/chat/common/chatParticipantContribTypes.ts @@ -3,6 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { RawChatParticipantLocation } from './constants.js'; + export interface IRawChatCommandContribution { name: string; description: string; @@ -13,8 +15,6 @@ export interface IRawChatCommandContribution { disambiguation?: { category: string; categoryName?: string /** Deprecated */; description: string; examples: string[] }[]; } -export type RawChatParticipantLocation = 'panel' | 'terminal' | 'notebook' | 'editing-session'; - export interface IRawChatParticipantContribution { id: string; name: string; diff --git a/src/vs/workbench/contrib/chat/common/chatProgressTypes/chatToolInvocation.ts b/src/vs/workbench/contrib/chat/common/chatProgressTypes/chatToolInvocation.ts index 8bbc84f11d25..8a601045354b 100644 --- a/src/vs/workbench/contrib/chat/common/chatProgressTypes/chatToolInvocation.ts +++ b/src/vs/workbench/contrib/chat/common/chatProgressTypes/chatToolInvocation.ts @@ -6,7 +6,7 @@ import { DeferredPromise } from '../../../../../base/common/async.js'; import { IMarkdownString } from '../../../../../base/common/htmlContent.js'; import { localize } from '../../../../../nls.js'; -import { IChatToolInvocation, IChatToolInvocationSerialized } from '../chatService.js'; +import { IChatTerminalToolInvocationData, IChatToolInvocation, IChatToolInvocationSerialized } from '../chatService.js'; import { IPreparedToolInvocation, IToolConfirmationMessages, IToolData, IToolResult } from '../languageModelToolsService.js'; export class ChatToolInvocation implements IChatToolInvocation { @@ -39,18 +39,19 @@ export class ChatToolInvocation implements IChatToolInvocation { public readonly invocationMessage: string | IMarkdownString; public pastTenseMessage: string | IMarkdownString | undefined; - public readonly tooltip: string | IMarkdownString | undefined; private _confirmationMessages: IToolConfirmationMessages | undefined; public readonly presentation: IPreparedToolInvocation['presentation']; + public readonly toolSpecificData?: IChatTerminalToolInvocationData; + constructor(preparedInvocation: IPreparedToolInvocation | undefined, toolData: IToolData) { const defaultMessage = localize('toolInvocationMessage', "Using {0}", `"${toolData.displayName}"`); const invocationMessage = preparedInvocation?.invocationMessage ?? defaultMessage; this.invocationMessage = invocationMessage; this.pastTenseMessage = preparedInvocation?.pastTenseMessage; - this.tooltip = preparedInvocation?.tooltip; this._confirmationMessages = preparedInvocation?.confirmationMessages; this.presentation = preparedInvocation?.presentation; + this.toolSpecificData = preparedInvocation?.toolSpecificData; if (!this._confirmationMessages) { // No confirmation needed @@ -87,10 +88,10 @@ export class ChatToolInvocation implements IChatToolInvocation { presentation: this.presentation, invocationMessage: this.invocationMessage, pastTenseMessage: this.pastTenseMessage, - tooltip: this.tooltip, isConfirmed: this._isConfirmed, isComplete: this._isComplete, - resultDetails: this._resultDetails + resultDetails: this._resultDetails, + toolSpecificData: this.toolSpecificData, }; } } diff --git a/src/vs/workbench/contrib/chat/common/chatRequestParser.ts b/src/vs/workbench/contrib/chat/common/chatRequestParser.ts index b710d18bd310..251e1fb291d7 100644 --- a/src/vs/workbench/contrib/chat/common/chatRequestParser.ts +++ b/src/vs/workbench/contrib/chat/common/chatRequestParser.ts @@ -6,10 +6,11 @@ import { OffsetRange } from '../../../../editor/common/core/offsetRange.js'; import { IPosition, Position } from '../../../../editor/common/core/position.js'; import { Range } from '../../../../editor/common/core/range.js'; -import { ChatAgentLocation, IChatAgentData, IChatAgentService } from './chatAgents.js'; +import { IChatAgentData, IChatAgentService } from './chatAgents.js'; import { ChatRequestAgentPart, ChatRequestAgentSubcommandPart, ChatRequestDynamicVariablePart, ChatRequestSlashCommandPart, ChatRequestTextPart, ChatRequestToolPart, IParsedChatRequest, IParsedChatRequestPart, chatAgentLeader, chatSubcommandLeader, chatVariableLeader } from './chatParserTypes.js'; import { IChatSlashCommandService } from './chatSlashCommands.js'; import { IChatVariablesService, IDynamicVariable } from './chatVariables.js'; +import { ChatAgentLocation, ChatMode } from './constants.js'; import { ILanguageModelToolsService } from './languageModelToolsService.js'; const agentReg = /^@([\w_\-\.]+)(?=(\s|$|\b))/i; // An @-agent @@ -19,6 +20,7 @@ const slashReg = /\/([\w_\-]+)(?=(\s|$|\b))/i; // A / command export interface IChatParserContext { /** Used only as a disambiguator, when the query references an agent that has a duplicate with the same name. */ selectedAgent?: IChatAgentData; + mode?: ChatMode; } export class ChatRequestParser { @@ -45,7 +47,7 @@ export class ChatRequestParser { } else if (char === chatAgentLeader) { newPart = this.tryToParseAgent(message.slice(i), message, i, new Position(lineNumber, column), parts, location, context); } else if (char === chatSubcommandLeader) { - newPart = this.tryToParseSlashCommand(message.slice(i), message, i, new Position(lineNumber, column), parts, location); + newPart = this.tryToParseSlashCommand(message.slice(i), message, i, new Position(lineNumber, column), parts, location, context); } if (!newPart) { @@ -94,7 +96,7 @@ export class ChatRequestParser { private tryToParseAgent(message: string, fullMessage: string, offset: number, position: IPosition, parts: Array, location: ChatAgentLocation, context: IChatParserContext | undefined): ChatRequestAgentPart | undefined { const nextAgentMatch = message.match(agentReg); - if (!nextAgentMatch) { + if (!nextAgentMatch || context?.mode !== undefined && context.mode !== ChatMode.Chat) { return; } @@ -157,7 +159,7 @@ export class ChatRequestParser { return; } - private tryToParseSlashCommand(remainingMessage: string, fullMessage: string, offset: number, position: IPosition, parts: ReadonlyArray, location: ChatAgentLocation): ChatRequestSlashCommandPart | ChatRequestAgentSubcommandPart | undefined { + private tryToParseSlashCommand(remainingMessage: string, fullMessage: string, offset: number, position: IPosition, parts: ReadonlyArray, location: ChatAgentLocation, context?: IChatParserContext): ChatRequestSlashCommandPart | ChatRequestAgentSubcommandPart | undefined { const nextSlashMatch = remainingMessage.match(slashReg); if (!nextSlashMatch) { return; @@ -199,7 +201,7 @@ export class ChatRequestParser { return new ChatRequestSlashCommandPart(slashRange, slashEditorRange, slashCommand); } else { // check for with default agent for this location - const defaultAgent = this.agentService.getDefaultAgent(location); + const defaultAgent = this.agentService.getDefaultAgent(location, context?.mode); const subCommand = defaultAgent?.slashCommands.find(c => c.name === command); if (subCommand) { // Valid default agent subcommand @@ -219,7 +221,7 @@ export class ChatRequestParser { const length = refAtThisPosition.range.endColumn - refAtThisPosition.range.startColumn; const text = message.substring(0, length); const range = new OffsetRange(offset, offset + length); - return new ChatRequestDynamicVariablePart(range, refAtThisPosition.range, text, refAtThisPosition.id, refAtThisPosition.modelDescription, refAtThisPosition.data, refAtThisPosition.fullName, refAtThisPosition.icon, refAtThisPosition.isFile); + return new ChatRequestDynamicVariablePart(range, refAtThisPosition.range, text, refAtThisPosition.id, refAtThisPosition.modelDescription, refAtThisPosition.data, refAtThisPosition.fullName, refAtThisPosition.icon, refAtThisPosition.isFile, refAtThisPosition.isDirectory); } return; diff --git a/src/vs/workbench/contrib/chat/common/chatService.ts b/src/vs/workbench/contrib/chat/common/chatService.ts index b2246a10052a..de11ce552d02 100644 --- a/src/vs/workbench/contrib/chat/common/chatService.ts +++ b/src/vs/workbench/contrib/chat/common/chatService.ts @@ -16,11 +16,12 @@ import { FileType } from '../../../../platform/files/common/files.js'; import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; import { ICellEditOperation } from '../../notebook/common/notebookCommon.js'; import { IWorkspaceSymbol } from '../../search/common/search.js'; -import { ChatAgentLocation, IChatAgentCommand, IChatAgentData, IChatAgentResult } from './chatAgents.js'; +import { IChatAgentCommand, IChatAgentData, IChatAgentResult } from './chatAgents.js'; import { ChatModel, IChatModel, IChatRequestModel, IChatRequestVariableData, IChatRequestVariableEntry, IChatResponseModel, IExportableChatData, ISerializableChatData } from './chatModel.js'; import { IParsedChatRequest } from './chatParserTypes.js'; import { IChatParserContext } from './chatRequestParser.js'; import { IChatRequestVariableValue } from './chatVariables.js'; +import { ChatAgentLocation, ChatMode } from './constants.js'; import { IPreparedToolInvocation, IToolConfirmationMessages, IToolResult } from './languageModelToolsService.js'; export interface IChatRequest { @@ -28,12 +29,19 @@ export interface IChatRequest { variables: Record; } +export enum ChatErrorLevel { + Info = 0, + Warning = 1, + Error = 2 +} + export interface IChatResponseErrorDetails { message: string; responseIsIncomplete?: boolean; responseIsFiltered?: boolean; responseIsRedacted?: boolean; isQuotaExceeded?: boolean; + level?: ChatErrorLevel; } export interface IChatResponseProgressFileTreeData { @@ -203,8 +211,15 @@ export interface IChatConfirmation { kind: 'confirmation'; } +export interface IChatTerminalToolInvocationData { + kind: 'terminal'; + command: string; + language: string; +} + export interface IChatToolInvocation { presentation: IPreparedToolInvocation['presentation']; + toolSpecificData?: IChatTerminalToolInvocationData; /** Presence of this property says that confirmation is required */ confirmationMessages?: IToolConfirmationMessages; confirmed: DeferredPromise; @@ -212,7 +227,6 @@ export interface IChatToolInvocation { isConfirmed: boolean | undefined; invocationMessage: string | IMarkdownString; pastTenseMessage: string | IMarkdownString | undefined; - tooltip: string | IMarkdownString | undefined; resultDetails: IToolResult['toolResultDetails']; isCompletePromise: Promise; @@ -226,10 +240,10 @@ export interface IChatToolInvocation { */ export interface IChatToolInvocationSerialized { presentation: IPreparedToolInvocation['presentation']; + toolSpecificData?: IChatTerminalToolInvocationData; invocationMessage: string | IMarkdownString; pastTenseMessage: string | IMarkdownString | undefined; resultDetails: IToolResult['toolResultDetails']; - tooltip: string | IMarkdownString | undefined; isConfirmed: boolean | undefined; isComplete: boolean; kind: 'toolInvocationSerialized'; @@ -397,6 +411,8 @@ export interface IChatProviderInfo { export interface IChatTransferredSessionData { sessionId: string; inputValue: string; + location: ChatAgentLocation; + mode: ChatMode; } export interface IChatSendRequestResponseState { @@ -429,7 +445,9 @@ export interface IChatTerminalLocationData { export type IChatLocationData = IChatEditorLocationData | IChatNotebookLocationData | IChatTerminalLocationData; export interface IChatSendRequestOptions { + mode?: ChatMode; userSelectedModelId?: string; + userSelectedTools?: string[]; location?: ChatAgentLocation; locationData?: IChatLocationData; parserContext?: IChatParserContext; @@ -460,6 +478,8 @@ export interface IChatService { _serviceBrand: undefined; transferredSessionData: IChatTransferredSessionData | undefined; + onDidSubmitRequest: Event<{ chatSessionId: string }>; + isEnabled(location: ChatAgentLocation): boolean; hasSessions(): boolean; startSession(location: ChatAgentLocation, token: CancellationToken): ChatModel; @@ -488,6 +508,9 @@ export interface IChatService { onDidDisposeSession: Event<{ sessionId: string; reason: 'initializationFailed' | 'cleared' }>; transferChatSession(transferredSessionData: IChatTransferredSessionData, toWorkspace: URI): void; + + readonly unifiedViewEnabled: boolean; + isEditingLocation(location: ChatAgentLocation): boolean; } export const KEYWORD_ACTIVIATION_SETTING_ID = 'accessibility.voice.keywordActivation'; diff --git a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts index 6e5e83b27a0c..42f2d1d63867 100644 --- a/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts +++ b/src/vs/workbench/contrib/chat/common/chatServiceImpl.ts @@ -5,6 +5,7 @@ import { DeferredPromise } from '../../../../base/common/async.js'; import { CancellationToken, CancellationTokenSource } from '../../../../base/common/cancellation.js'; +import { memoize } from '../../../../base/common/decorators.js'; import { toErrorMessage } from '../../../../base/common/errorMessage.js'; import { ErrorNoTelemetry } from '../../../../base/common/errors.js'; import { Emitter, Event } from '../../../../base/common/event.js'; @@ -25,7 +26,7 @@ import { ITelemetryService } from '../../../../platform/telemetry/common/telemet import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; import { IWorkbenchAssignmentService } from '../../../services/assignment/common/assignmentService.js'; import { IExtensionService } from '../../../services/extensions/common/extensions.js'; -import { ChatAgentLocation, IChatAgent, IChatAgentCommand, IChatAgentData, IChatAgentHistoryEntry, IChatAgentRequest, IChatAgentResult, IChatAgentService } from './chatAgents.js'; +import { IChatAgent, IChatAgentCommand, IChatAgentData, IChatAgentHistoryEntry, IChatAgentRequest, IChatAgentResult, IChatAgentService } from './chatAgents.js'; import { ChatModel, ChatRequestModel, ChatRequestRemovalReason, IChatModel, IChatRequestModel, IChatRequestVariableData, IChatResponseModel, IExportableChatData, ISerializableChatData, ISerializableChatDataIn, ISerializableChatsData, normalizeSerializableChatData, toChatHistoryContent, updateRanges } from './chatModel.js'; import { ChatRequestAgentPart, ChatRequestAgentSubcommandPart, ChatRequestSlashCommandPart, IParsedChatRequest, chatAgentLeader, chatSubcommandLeader, getPromptText } from './chatParserTypes.js'; import { ChatRequestParser } from './chatRequestParser.js'; @@ -33,6 +34,7 @@ import { IChatCompleteResponse, IChatDetail, IChatFollowup, IChatProgress, IChat import { ChatServiceTelemetry } from './chatServiceTelemetry.js'; import { IChatSlashCommandService } from './chatSlashCommands.js'; import { IChatVariablesService } from './chatVariables.js'; +import { ChatAgentLocation, ChatConfiguration, ChatMode } from './constants.js'; import { ChatMessageRole, IChatMessage } from './languageModels.js'; import { ILanguageModelToolsService } from './languageModelToolsService.js'; @@ -44,6 +46,8 @@ interface IChatTransfer { timestampInMilliseconds: number; chat: ISerializableChatData; inputValue: string; + location: ChatAgentLocation; + mode: ChatMode; } const SESSION_TRANSFER_EXPIRATION_IN_MILLISECONDS = 1000 * 60; @@ -120,6 +124,9 @@ export class ChatService extends Disposable implements IChatService { return this._transferredSessionData; } + private readonly _onDidSubmitRequest = this._register(new Emitter<{ chatSessionId: string }>()); + public readonly onDidSubmitRequest: Event<{ chatSessionId: string }> = this._onDidSubmitRequest.event; + private readonly _onDidPerformUserAction = this._register(new Emitter()); public readonly onDidPerformUserAction: Event = this._onDidPerformUserAction.event; @@ -129,6 +136,11 @@ export class ChatService extends Disposable implements IChatService { private readonly _sessionFollowupCancelTokens = this._register(new DisposableMap()); private readonly _chatServiceTelemetry: ChatServiceTelemetry; + @memoize + public get unifiedViewEnabled(): boolean { + return !!this.configurationService.getValue(ChatConfiguration.UnifiedChatView); + } + constructor( @IStorageService private readonly storageService: IStorageService, @ILogService private readonly logService: ILogService, @@ -162,7 +174,12 @@ export class ChatService extends Disposable implements IChatService { if (transferredChat) { this.trace('constructor', `Transferred session ${transferredChat.sessionId}`); this._persistedSessions[transferredChat.sessionId] = transferredChat; - this._transferredSessionData = { sessionId: transferredChat.sessionId, inputValue: transferredData.inputValue }; + this._transferredSessionData = { + sessionId: transferredChat.sessionId, + inputValue: transferredData.inputValue, + location: transferredData.location, + mode: transferredData.mode, + }; } this._register(storageService.onWillSaveState(() => this.saveState())); @@ -187,7 +204,26 @@ export class ChatService extends Disposable implements IChatService { .filter(session => !this._sessionModels.has(session.sessionId)) .filter(session => session.requests.length)); allSessions.sort((a, b) => (b.creationDate ?? 0) - (a.creationDate ?? 0)); + + // Only keep one persisted edit session, the latest one. This would be the current one if it's live. + // No way to know whether it's currently live or if it has been cleared and there is no current session. + // But ensure that we don't store multiple edit sessions. + let hasPersistedEditSession = false; + allSessions = allSessions.filter(s => { + if (s.initialLocation === ChatAgentLocation.EditingSession) { + if (hasPersistedEditSession) { + return false; + } else { + hasPersistedEditSession = true; + return true; + } + } + + return true; + }); + allSessions = allSessions.slice(0, maxPersistedSessions); + if (allSessions.length) { this.trace('onWillSaveState', `Persisting ${allSessions.length} sessions`); } @@ -413,9 +449,8 @@ export class ChatService extends Disposable implements IChatService { throw new ErrorNoTelemetry('No default agent registered'); } - const welcomeMessage = await defaultAgent.provideWelcomeMessage?.(token) ?? undefined; const sampleQuestions = await defaultAgent.provideSampleQuestions?.(model.initialLocation, token) ?? undefined; - model.initialize(welcomeMessage, sampleQuestions); + model.initialize(sampleQuestions); } catch (err) { this.trace('startSession', `initializeSession failed: ${err}`); model.setInitializationError(err); @@ -440,11 +475,16 @@ export class ChatService extends Disposable implements IChatService { return undefined; } - if (sessionId === this.transferredSessionData?.sessionId) { + const session = this._startSession(sessionData, sessionData.initialLocation ?? ChatAgentLocation.Panel, CancellationToken.None); + + const isTransferred = this.transferredSessionData?.sessionId === sessionId; + if (isTransferred) { + // TODO + // this.chatAgentService.toggleToolsAgentMode(this.transferredSessionData.toolsAgentModeEnabled); this._transferredSessionData = undefined; } - return this._startSession(sessionData, sessionData.initialLocation ?? ChatAgentLocation.Panel, CancellationToken.None); + return session; } loadSessionFromContent(data: IExportableChatData | ISerializableChatData): IChatModel | undefined { @@ -468,7 +508,7 @@ export class ChatService extends Disposable implements IChatService { const location = options?.location ?? model.initialLocation; const attempt = options?.attempt ?? 0; const enableCommandDetection = !options?.noCommandDetection; - const defaultAgent = this.chatAgentService.getDefaultAgent(location)!; + const defaultAgent = this.chatAgentService.getDefaultAgent(location, options?.mode)!; model.removeRequest(request.id, ChatRequestRemovalReason.Resend); @@ -521,7 +561,7 @@ export class ChatService extends Disposable implements IChatService { const location = options?.location ?? model.initialLocation; const attempt = options?.attempt ?? 0; - const defaultAgent = this.chatAgentService.getDefaultAgent(location)!; + const defaultAgent = this.chatAgentService.getDefaultAgent(location, options?.mode)!; const parsedRequest = this.parseChatRequest(sessionId, request, location, options); const agent = parsedRequest.parts.find((r): r is ChatRequestAgentPart => r instanceof ChatRequestAgentPart)?.agent ?? defaultAgent; @@ -542,7 +582,7 @@ export class ChatService extends Disposable implements IChatService { if (!agent) { throw new Error(`Unknown agent: ${options.agentId}`); } - parserContext = { selectedAgent: agent }; + parserContext = { selectedAgent: agent, mode: options.mode }; const commandPart = options.slashCommand ? ` ${chatSubcommandLeader}${options.slashCommand}` : ''; request = `${chatAgentLeader}${agent.name}${commandPart} ${request}`; } @@ -664,11 +704,12 @@ export class ChatService extends Disposable implements IChatService { locationData: request.locationData, acceptedConfirmationData: options?.acceptedConfirmationData, rejectedConfirmationData: options?.rejectedConfirmationData, - userSelectedModelId: options?.userSelectedModelId + userSelectedModelId: options?.userSelectedModelId, + userSelectedTools: options?.userSelectedTools } satisfies IChatAgentRequest; }; - if (this.configurationService.getValue('chat.detectParticipant.enabled') !== false && this.chatAgentService.hasChatParticipantDetectionProviders() && !agentPart && !commandPart && !agentSlashCommandPart && enableCommandDetection) { + if (this.configurationService.getValue('chat.detectParticipant.enabled') !== false && this.chatAgentService.hasChatParticipantDetectionProviders() && !agentPart && !commandPart && !agentSlashCommandPart && enableCommandDetection && options?.mode !== ChatMode.Agent && options?.mode !== ChatMode.Edit) { // We have no agent or command to scope history with, pass the full history to the participant detection provider const defaultAgentHistory = this.getHistoryEntriesFromModel(requests, model.sessionId, location, defaultAgent.id); @@ -806,6 +847,7 @@ export class ChatService extends Disposable implements IChatService { rawResponsePromise.finally(() => { this._pendingRequests.deleteAndDispose(model.sessionId); }); + this._onDidSubmitRequest.fire({ chatSessionId: model.sessionId }); return { responseCreatedPromise: responseCreated.p, responseCompletePromise: rawResponsePromise, @@ -857,13 +899,13 @@ export class ChatService extends Disposable implements IChatService { private getHistoryEntriesFromModel(requests: IChatRequestModel[], sessionId: string, location: ChatAgentLocation, forAgentId: string): IChatAgentHistoryEntry[] { const history: IChatAgentHistoryEntry[] = []; + const agent = this.chatAgentService.getAgent(forAgentId); for (const request of requests) { if (!request.response) { continue; } - const defaultAgentId = this.chatAgentService.getDefaultAgent(location)?.id; - if (forAgentId !== request.response.agent?.id && forAgentId !== defaultAgentId) { + if (forAgentId !== request.response.agent?.id && !agent?.isDefault) { // An agent only gets to see requests that were sent to this agent. // The default agent (the undefined case) gets to see all of them. continue; @@ -996,11 +1038,17 @@ export class ChatService extends Disposable implements IChatService { timestampInMilliseconds: Date.now(), toWorkspace: toWorkspace, inputValue: transferredSessionData.inputValue, + location: transferredSessionData.location, + mode: transferredSessionData.mode, }); this.storageService.store(globalChatKey, JSON.stringify(existingRaw), StorageScope.PROFILE, StorageTarget.MACHINE); this.trace('transferChatSession', `Transferred session ${model.sessionId} to workspace ${toWorkspace.toString()}`); } + + isEditingLocation(location: ChatAgentLocation): boolean { + return location === ChatAgentLocation.EditingSession || this.unifiedViewEnabled; + } } function getCodeBlocks(text: string): string[] { diff --git a/src/vs/workbench/contrib/chat/common/chatServiceTelemetry.ts b/src/vs/workbench/contrib/chat/common/chatServiceTelemetry.ts index 2cd48f413936..14da30a74bab 100644 --- a/src/vs/workbench/contrib/chat/common/chatServiceTelemetry.ts +++ b/src/vs/workbench/contrib/chat/common/chatServiceTelemetry.ts @@ -3,9 +3,8 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { CommandsRegistry } from '../../../../platform/commands/common/commands.js'; import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; -import { IChatUserActionEvent, ChatAgentVoteDirection, ChatCopyKind } from './chatService.js'; +import { ChatAgentVoteDirection, ChatCopyKind, IChatUserActionEvent } from './chatService.js'; type ChatVoteEvent = { direction: 'up' | 'down'; @@ -69,20 +68,6 @@ type ChatApplyClassification = { comment: 'Provides insight into the usage of Chat features.'; }; -type ChatCommandEvent = { - commandId: string; - agentId: string; - command: string | undefined; -}; - -type ChatCommandClassification = { - commandId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The id of the command that was executed.' }; - agentId: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The ID of the related chat agent.' }; - command: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The name of the related slash command.' }; - owner: 'roblourens'; - comment: 'Provides insight into the usage of Chat features.'; -}; - type ChatFollowupEvent = { agentId: string; command: string | undefined; @@ -156,15 +141,6 @@ export class ChatServiceTelemetry { command: action.command, editsProposed: !!action.action.editsProposed, }); - } else if (action.action.kind === 'command') { - // TODO not currently called - const command = CommandsRegistry.getCommand(action.action.commandButton.command.id); - const commandId = command ? action.action.commandButton.command.id : 'INVALID'; - this.telemetryService.publicLog2('interactiveSessionCommand', { - commandId, - agentId: action.agentId ?? '', - command: action.command, - }); } else if (action.action.kind === 'runInTerminal') { this.telemetryService.publicLog2('interactiveSessionRunInTerminal', { languageId: action.action.languageId ?? '', diff --git a/src/vs/workbench/contrib/chat/common/chatSlashCommands.ts b/src/vs/workbench/contrib/chat/common/chatSlashCommands.ts index 20ec2affa73d..e258f8f5c629 100644 --- a/src/vs/workbench/contrib/chat/common/chatSlashCommands.ts +++ b/src/vs/workbench/contrib/chat/common/chatSlashCommands.ts @@ -11,7 +11,7 @@ import { IProgress } from '../../../../platform/progress/common/progress.js'; import { IChatMessage } from './languageModels.js'; import { IChatFollowup, IChatProgress, IChatResponseProgressFileTreeData } from './chatService.js'; import { IExtensionService } from '../../../services/extensions/common/extensions.js'; -import { ChatAgentLocation } from './chatAgents.js'; +import { ChatAgentLocation } from './constants.js'; //#region slash service, commands etc diff --git a/src/vs/workbench/contrib/chat/common/chatTransferService.ts b/src/vs/workbench/contrib/chat/common/chatTransferService.ts new file mode 100644 index 000000000000..22a2eb31aa85 --- /dev/null +++ b/src/vs/workbench/contrib/chat/common/chatTransferService.ts @@ -0,0 +1,36 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ +import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; +import { IStorageService } from '../../../../platform/storage/common/storage.js'; +import { IFileService } from '../../../../platform/files/common/files.js'; +import { IWorkspaceTrustManagementService } from '../../../../platform/workspace/common/workspaceTrust.js'; +import { isChatTransferredWorkspace, areWorkspaceFoldersEmpty } from '../../../services/workspaces/common/workspaceUtils.js'; +import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; + +export const IChatTransferService = createDecorator('chatTransferService'); + +export interface IChatTransferService { + readonly _serviceBrand: undefined; + + checkAndSetWorkspaceTrust(): Promise; +} + +export class ChatTransferService implements IChatTransferService { + _serviceBrand: undefined; + + constructor( + @IWorkspaceContextService private readonly workspaceService: IWorkspaceContextService, + @IStorageService private readonly storageService: IStorageService, + @IFileService private readonly fileService: IFileService, + @IWorkspaceTrustManagementService private readonly workspaceTrustManagementService: IWorkspaceTrustManagementService + ) { } + + async checkAndSetWorkspaceTrust(): Promise { + const workspace = this.workspaceService.getWorkspace(); + if (isChatTransferredWorkspace(workspace, this.storageService) && await areWorkspaceFoldersEmpty(workspace, this.fileService)) { + await this.workspaceTrustManagementService.setWorkspaceTrust(true); + } + } +} diff --git a/src/vs/workbench/contrib/chat/common/chatVariables.ts b/src/vs/workbench/contrib/chat/common/chatVariables.ts index de02d68cf919..809d5637c533 100644 --- a/src/vs/workbench/contrib/chat/common/chatVariables.ts +++ b/src/vs/workbench/contrib/chat/common/chatVariables.ts @@ -9,10 +9,10 @@ import { URI } from '../../../../base/common/uri.js'; import { IRange } from '../../../../editor/common/core/range.js'; import { Location } from '../../../../editor/common/languages.js'; import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; -import { ChatAgentLocation } from './chatAgents.js'; -import { IChatModel, IChatRequestVariableData, IChatRequestVariableEntry } from './chatModel.js'; +import { IChatModel, IChatRequestVariableData, IChatRequestVariableEntry, IDiagnosticVariableEntryFilterData } from './chatModel.js'; import { IParsedChatRequest } from './chatParserTypes.js'; import { IChatContentReference, IChatProgressMessage } from './chatService.js'; +import { ChatAgentLocation } from './constants.js'; export interface IChatVariableData { id: string; @@ -24,7 +24,15 @@ export interface IChatVariableData { canTakeArgument?: boolean; } -export type IChatRequestVariableValue = string | URI | Location | unknown | Uint8Array; +export interface IChatRequestProblemsVariable { + id: 'vscode.problems'; + filter: IDiagnosticVariableEntryFilterData; +} + +export const isIChatRequestProblemsVariable = (obj: unknown): obj is IChatRequestProblemsVariable => + typeof obj === 'object' && obj !== null && 'id' in obj && (obj as IChatRequestProblemsVariable).id === 'vscode.problems'; + +export type IChatRequestVariableValue = string | URI | Location | unknown | Uint8Array | IChatRequestProblemsVariable; export type IChatVariableResolverProgress = | IChatContentReference @@ -55,5 +63,6 @@ export interface IDynamicVariable { prefix?: string; modelDescription?: string; isFile?: boolean; + isDirectory?: boolean; data: IChatRequestVariableValue; } diff --git a/src/vs/workbench/contrib/chat/common/chatViewModel.ts b/src/vs/workbench/contrib/chat/common/chatViewModel.ts index 88d973ecc41d..063bd4b0a7f5 100644 --- a/src/vs/workbench/contrib/chat/common/chatViewModel.ts +++ b/src/vs/workbench/contrib/chat/common/chatViewModel.ts @@ -588,9 +588,13 @@ export class ChatResponseViewModel extends Disposable implements IChatResponseVi const now = Date.now(); const wordCount = countWords(_model.entireResponse.getMarkdown()); - if (wordCount > 0 && wordCount === this._contentUpdateTimings.lastWordCount) { + if (wordCount === this._contentUpdateTimings.lastWordCount) { this.trace('onDidChange', `Update- no new words`); } else { + if (this._contentUpdateTimings.lastWordCount === 0) { + this._contentUpdateTimings.lastUpdateTime = now; + } + const timeDiff = Math.min(now - this._contentUpdateTimings.lastUpdateTime, 1000); const newTotalTime = Math.max(this._contentUpdateTimings.totalTime + timeDiff, 250); const impliedWordLoadRate = wordCount / (newTotalTime / 1000); diff --git a/src/vs/workbench/contrib/chat/common/chatWidgetHistoryService.ts b/src/vs/workbench/contrib/chat/common/chatWidgetHistoryService.ts index d2ab31d58ce3..4522c0b7bd33 100644 --- a/src/vs/workbench/contrib/chat/common/chatWidgetHistoryService.ts +++ b/src/vs/workbench/contrib/chat/common/chatWidgetHistoryService.ts @@ -8,10 +8,10 @@ import { URI } from '../../../../base/common/uri.js'; import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; import { Memento } from '../../../common/memento.js'; -import { ChatAgentLocation } from './chatAgents.js'; import { WorkingSetEntryState } from './chatEditingService.js'; import { IChatRequestVariableEntry } from './chatModel.js'; import { CHAT_PROVIDER_ID } from './chatParticipantContribTypes.js'; +import { ChatAgentLocation, ChatMode } from './constants.js'; export interface IChatHistoryEntry { text: string; @@ -23,6 +23,7 @@ export interface IChatInputState { [key: string]: any; chatContextAttachments?: ReadonlyArray; chatWorkingSet?: ReadonlyArray<{ uri: URI; state: WorkingSetEntryState }>; + chatMode?: ChatMode; } export const IChatWidgetHistoryService = createDecorator('IChatWidgetHistoryService'); diff --git a/src/vs/workbench/contrib/chat/common/codeBlockModelCollection.ts b/src/vs/workbench/contrib/chat/common/codeBlockModelCollection.ts index 7dd9973b03b3..59c605b64815 100644 --- a/src/vs/workbench/contrib/chat/common/codeBlockModelCollection.ts +++ b/src/vs/workbench/contrib/chat/common/codeBlockModelCollection.ts @@ -43,6 +43,7 @@ export class CodeBlockModelCollection extends Disposable { private readonly maxModelCount = 100; constructor( + private readonly tag: string | undefined, @ILanguageService private readonly languageService: ILanguageService, @ITextModelService private readonly textModelService: ITextModelService, ) { @@ -201,10 +202,11 @@ export class CodeBlockModelCollection extends Disposable { private getCodeBlockUri(sessionId: string, chat: IChatRequestViewModel | IChatResponseViewModel, index: number): URI { const metadata = this.getUriMetaData(chat); + const indexPart = this.tag ? `${this.tag}-${index}` : `${index}`; return URI.from({ scheme: Schemas.vscodeChatCodeBlock, authority: sessionId, - path: `/${chat.id}/${index}`, + path: `/${chat.id}/${indexPart}`, fragment: metadata ? JSON.stringify(metadata) : undefined, }); } diff --git a/src/vs/workbench/contrib/chat/common/constants.ts b/src/vs/workbench/contrib/chat/common/constants.ts new file mode 100644 index 000000000000..9c42f7bc3041 --- /dev/null +++ b/src/vs/workbench/contrib/chat/common/constants.ts @@ -0,0 +1,37 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export enum ChatConfiguration { + UnifiedChatView = 'chat.experimental.unifiedChatView', +} + +export enum ChatMode { + Chat = 'chat', + Edit = 'edit', + Agent = 'agent' +} + +export type RawChatParticipantLocation = 'panel' | 'terminal' | 'notebook' | 'editing-session'; + +export enum ChatAgentLocation { + Panel = 'panel', + Terminal = 'terminal', + Notebook = 'notebook', + Editor = 'editor', + EditingSession = 'editing-session', +} + +export namespace ChatAgentLocation { + export function fromRaw(value: RawChatParticipantLocation | string): ChatAgentLocation { + switch (value) { + case 'panel': return ChatAgentLocation.Panel; + case 'terminal': return ChatAgentLocation.Terminal; + case 'notebook': return ChatAgentLocation.Notebook; + case 'editor': return ChatAgentLocation.Editor; + case 'editing-session': return ChatAgentLocation.EditingSession; + } + return ChatAgentLocation.Panel; + } +} diff --git a/src/vs/workbench/contrib/chat/common/languageModelToolsService.ts b/src/vs/workbench/contrib/chat/common/languageModelToolsService.ts index 878810b26f9d..24869cbc2b61 100644 --- a/src/vs/workbench/contrib/chat/common/languageModelToolsService.ts +++ b/src/vs/workbench/contrib/chat/common/languageModelToolsService.ts @@ -14,6 +14,7 @@ import { ContextKeyExpression } from '../../../../platform/contextkey/common/con import { ExtensionIdentifier } from '../../../../platform/extensions/common/extensions.js'; import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; import { Location } from '../../../../editor/common/languages.js'; +import { IChatTerminalToolInvocationData } from './chatService.js'; export interface IToolData { id: string; @@ -36,6 +37,8 @@ export interface IToolInvocation { tokenBudget?: number; context: IToolInvocationContext | undefined; chatRequestId?: string; + chatInteractionId?: string; + toolSpecificData?: IChatTerminalToolInvocationData; } export interface IToolInvocationContext { @@ -65,14 +68,15 @@ export interface IToolResultTextPart { export interface IToolConfirmationMessages { title: string; message: string | IMarkdownString; + toolInput?: object; } export interface IPreparedToolInvocation { invocationMessage?: string | IMarkdownString; pastTenseMessage?: string | IMarkdownString; - tooltip?: string | IMarkdownString; confirmationMessages?: IToolConfirmationMessages; presentation?: 'hidden' | undefined; + toolSpecificData?: IChatTerminalToolInvocationData; } export interface IToolImpl { diff --git a/src/vs/workbench/contrib/chat/common/languageModels.ts b/src/vs/workbench/contrib/chat/common/languageModels.ts index 8424946dac5c..3db81d2941c1 100644 --- a/src/vs/workbench/contrib/chat/common/languageModels.ts +++ b/src/vs/workbench/contrib/chat/common/languageModels.ts @@ -89,6 +89,7 @@ export interface ILanguageModelChatMetadata { readonly capabilities?: { readonly vision?: boolean; readonly toolCalling?: boolean; + readonly agentMode?: boolean; }; } diff --git a/src/vs/workbench/contrib/chat/common/promptFileReferenceErrors.ts b/src/vs/workbench/contrib/chat/common/promptFileReferenceErrors.ts index 1538f9e23164..e19588df4e6a 100644 --- a/src/vs/workbench/contrib/chat/common/promptFileReferenceErrors.ts +++ b/src/vs/workbench/contrib/chat/common/promptFileReferenceErrors.ts @@ -4,11 +4,13 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from '../../../../base/common/uri.js'; +import { basename } from '../../../../base/common/path.js'; +import { assert, assertNever } from '../../../../base/common/assert.js'; /** * Base prompt parsing error class. */ -export abstract class ParseError extends Error { +abstract class ParseError extends Error { /** * Error type name. */ @@ -41,41 +43,41 @@ export abstract class ParseError extends Error { } /** - * A generic error for failing to resolve prompt contents stream. + * Base resolve error class used when file reference resolution fails. */ -export class FailedToResolveContentsStream extends ParseError { - public override errorType = 'FailedToResolveContentsStream'; +export abstract class ResolveError extends ParseError { + public abstract override errorType: string; constructor( public readonly uri: URI, - public readonly originalError: unknown, - message: string = `Failed to resolve prompt contents stream for '${uri.toString()}': ${originalError}.`, + message?: string, + options?: ErrorOptions, ) { - super(message); + super(message, options); } } - /** - * Base resolve error class used when file reference resolution fails. + * A generic error for failing to resolve prompt contents stream. */ -export abstract class ResolveError extends ParseError { - public abstract override errorType: string; +export class FailedToResolveContentsStream extends ResolveError { + public override errorType = 'FailedToResolveContentsStream'; constructor( - public readonly uri: URI, - message?: string, - options?: ErrorOptions, + uri: URI, + public readonly originalError: unknown, + message: string = `Failed to resolve prompt contents stream for '${uri.toString()}': ${originalError}.`, ) { - super(message, options); + super(uri, message); } } + /** * Error that reflects the case when attempt to open target file fails. */ -export class FileOpenFailed extends FailedToResolveContentsStream { - public override errorType = 'FileOpenError'; +export class OpenFailed extends FailedToResolveContentsStream { + public override errorType = 'OpenError'; constructor( uri: URI, @@ -84,11 +86,17 @@ export class FileOpenFailed extends FailedToResolveContentsStream { super( uri, originalError, - `Failed to open file '${uri.toString()}': ${originalError}.`, + `Failed to open '${uri.fsPath}': ${originalError}.`, ); } } +/** + * Character use to join filenames/paths in a chain of references that + * lead to recursion. + */ +const DEFAULT_RECURSIVE_PATH_JOIN_CHAR = ' -> '; + /** * Error that reflects the case when attempt resolve nested file * references failes due to a recursive reference, e.g., @@ -106,23 +114,67 @@ export class FileOpenFailed extends FailedToResolveContentsStream { export class RecursiveReference extends ResolveError { public override errorType = 'RecursiveReferenceError'; + /** + * Cached default string representation of the recursive path. + */ + private defaultPathStringCache: string | undefined; + constructor( uri: URI, public readonly recursivePath: string[], ) { - const references = recursivePath.join(' -> '); + // sanity check - a recursive path must always have at least + // two items in the list, otherwise it is not a recursive loop + assert( + recursivePath.length >= 2, + `Recursive path must contain at least two paths, got '${recursivePath.length}'.`, + ); super( - uri, - `Recursive references found: ${references}.`, + uri, 'Recursive references found.', ); } + public override get message(): string { + return `${super.message} ${this.getRecursivePathString('fullpath')}`; + } + /** * Returns a string representation of the recursive path. */ - public get recursivePathString(): string { - return this.recursivePath.join(' -> '); + public getRecursivePathString( + filename: 'basename' | 'fullpath', + pathJoinCharacter: string = DEFAULT_RECURSIVE_PATH_JOIN_CHAR, + ): string { + const isDefault = (filename === 'fullpath') && + (pathJoinCharacter === DEFAULT_RECURSIVE_PATH_JOIN_CHAR); + + if (isDefault && (this.defaultPathStringCache !== undefined)) { + return this.defaultPathStringCache; + } + + const result = this.recursivePath + .map((path) => { + if (filename === 'fullpath') { + return `'${path}'`; + } + + if (filename === 'basename') { + return `'${basename(path)}'`; + } + + assertNever( + filename, + `Unknown filename format '${filename}'.`, + ); + }) + .join(pathJoinCharacter); + + if (isDefault) { + this.defaultPathStringCache = result; + } + + return result; } /** @@ -138,7 +190,22 @@ export class RecursiveReference extends ResolveError { return false; } - return this.recursivePathString === other.recursivePathString; + // performance optimization - compare number of paths in the + // recursive path chains first to avoid comparison of all strings + if (this.recursivePath.length !== other.recursivePath.length) { + return false; + } + + const myRecursivePath = this.getRecursivePathString('fullpath'); + const theirRecursivePath = other.getRecursivePathString('fullpath'); + + // performance optimization - if the path lengths don't match, + // no need to compare entire strings as they must be different + if (myRecursivePath.length !== theirRecursivePath.length) { + return false; + } + + return myRecursivePath === theirRecursivePath; } /** @@ -150,11 +217,30 @@ export class RecursiveReference extends ResolveError { } /** - * Error that reflects the case when resource URI does not point to - * a prompt snippet file, hence was not attempted to be resolved. + * Error for the case when a resource URI doesn't point to a prompt file. + */ +export class NotPromptFile extends ResolveError { + public override errorType = 'NotPromptFileError'; + + constructor( + uri: URI, + message: string = '', + ) { + + const suffix = message ? `: ${message}` : ''; + + super( + uri, + `Resource at ${uri.path} is not a prompt file${suffix}`, + ); + } +} + +/** + * Error for the case when a resource URI points to a folder. */ -export class NonPromptSnippetFile extends ResolveError { - public override errorType = 'NonPromptSnippetFileError'; +export class FolderReference extends NotPromptFile { + public override errorType = 'FolderReferenceError'; constructor( uri: URI, @@ -165,7 +251,7 @@ export class NonPromptSnippetFile extends ResolveError { super( uri, - `Resource at ${uri.path} is not a prompt snippet file${suffix}`, + `Entity at '${uri.path}' is a folder${suffix}`, ); } } diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/codecs/chatPromptDecoder.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/codecs/chatPromptDecoder.ts index 06d8e1620ec6..b1bd3de2b923 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/codecs/chatPromptDecoder.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/codecs/chatPromptDecoder.ts @@ -3,166 +3,21 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { FileReference } from './tokens/fileReference.js'; +import { PromptToken } from './tokens/promptToken.js'; import { VSBuffer } from '../../../../../../base/common/buffer.js'; -import { Range } from '../../../../../../editor/common/core/range.js'; +import { assertNever } from '../../../../../../base/common/assert.js'; import { ReadableStream } from '../../../../../../base/common/stream.js'; import { BaseDecoder } from '../../../../../../base/common/codecs/baseDecoder.js'; -import { Tab } from '../../../../../../editor/common/codecs/simpleCodec/tokens/tab.js'; -import { Word } from '../../../../../../editor/common/codecs/simpleCodec/tokens/word.js'; +import { PromptVariable, PromptVariableWithData } from './tokens/promptVariable.js'; import { Hash } from '../../../../../../editor/common/codecs/simpleCodec/tokens/hash.js'; -import { Space } from '../../../../../../editor/common/codecs/simpleCodec/tokens/space.js'; -import { Colon } from '../../../../../../editor/common/codecs/simpleCodec/tokens/colon.js'; -import { NewLine } from '../../../../../../editor/common/codecs/linesCodec/tokens/newLine.js'; -import { FormFeed } from '../../../../../../editor/common/codecs/simpleCodec/tokens/formFeed.js'; -import { VerticalTab } from '../../../../../../editor/common/codecs/simpleCodec/tokens/verticalTab.js'; import { MarkdownLink } from '../../../../../../editor/common/codecs/markdownCodec/tokens/markdownLink.js'; -import { CarriageReturn } from '../../../../../../editor/common/codecs/linesCodec/tokens/carriageReturn.js'; -import { ParserBase, TAcceptTokenResult } from '../../../../../../editor/common/codecs/simpleCodec/parserBase.js'; +import { PartialPromptVariableName, PartialPromptVariableWithData } from './parsers/promptVariableParser.js'; import { MarkdownDecoder, TMarkdownToken } from '../../../../../../editor/common/codecs/markdownCodec/markdownDecoder.js'; /** * Tokens produced by this decoder. */ -export type TChatPromptToken = MarkdownLink | FileReference; - -/** - * The Parser responsible for processing a `prompt variable name` syntax from - * a sequence of tokens (e.g., `#variable:`). - * - * The parsing process starts with single `#` token, then can accept `file` word, - * followed by the `:` token, resulting in the tokens sequence equivalent to - * the `#file:` text sequence. In this successful case, the parser transitions into - * the {@linkcode PartialPromptFileReference} parser to continue the parsing process. - */ -class PartialPromptVariableName extends ParserBase { - constructor(token: Hash) { - super([token]); - } - - public accept(token: TMarkdownToken): TAcceptTokenResult { - // given we currently hold the `#` token, if we receive a `file` word, - // we can successfully proceed to the next token in the sequence - if (token instanceof Word) { - if (token.text === 'file') { - this.currentTokens.push(token); - - return { - result: 'success', - nextParser: this, - wasTokenConsumed: true, - }; - } - - return { - result: 'failure', - wasTokenConsumed: false, - }; - } - - // if we receive the `:` token, we can successfully proceed to the next - // token in the sequence `only if` the previous token was a `file` word - // therefore for currently tokens sequence equivalent to the `#file` text - if (token instanceof Colon) { - const lastToken = this.currentTokens[this.currentTokens.length - 1]; - - if (lastToken instanceof Word) { - this.currentTokens.push(token); - - return { - result: 'success', - nextParser: new PartialPromptFileReference(this.currentTokens), - wasTokenConsumed: true, - }; - } - } - - // all other cases are failures and we don't consume the offending token - return { - result: 'failure', - wasTokenConsumed: false, - }; - } -} - -/** - * List of characters that stop a prompt variable sequence. - */ -const PROMPT_FILE_REFERENCE_STOP_CHARACTERS: readonly string[] = [Space, Tab, CarriageReturn, NewLine, VerticalTab, FormFeed] - .map((token) => { return token.symbol; }); - -/** - * Parser responsible for processing the `file reference` syntax part from - * a sequence of tokens (e.g., #variable:`./some/file/path.md`). - * - * The parsing process starts with the sequence of `#`, `file`, and `:` tokens, - * then can accept a sequence of tokens until one of the tokens defined in - * the {@linkcode PROMPT_FILE_REFERENCE_STOP_CHARACTERS} list is encountered. - * This sequence of tokens is treated as a `file path` part of the `#file:` variable, - * and in the successful case, the parser transitions into the {@linkcode FileReference} - * token which signifies the end of the file reference text parsing process. - */ -class PartialPromptFileReference extends ParserBase { - /** - * Set of tokens that were accumulated so far. - */ - private readonly fileReferenceTokens: (Hash | Word | Colon)[]; - - constructor(tokens: (Hash | Word | Colon)[]) { - super([]); - - this.fileReferenceTokens = tokens; - } - - /** - * List of tokens that were accumulated so far. - */ - public override get tokens(): readonly (Hash | Word | Colon)[] { - return [...this.fileReferenceTokens, ...this.currentTokens]; - } - - /** - * Return the `FileReference` instance created from the current object. - */ - public asFileReference(): FileReference { - // use only tokens in the `currentTokens` list to - // create the path component of the file reference - const path = this.currentTokens - .map((token) => { return token.text; }) - .join(''); - - const firstToken = this.tokens[0]; - - const range = new Range( - firstToken.range.startLineNumber, - firstToken.range.startColumn, - firstToken.range.startLineNumber, - firstToken.range.startColumn + FileReference.TOKEN_START.length + path.length, - ); - - return new FileReference(range, path); - } - - public accept(token: TMarkdownToken): TAcceptTokenResult { - // any of stop characters is are breaking a prompt variable sequence - if (PROMPT_FILE_REFERENCE_STOP_CHARACTERS.includes(token.text)) { - return { - result: 'success', - wasTokenConsumed: false, - nextParser: this.asFileReference(), - }; - } - - // any other token can be included in the sequence so accumulate - // it and continue with using the current parser instance - this.currentTokens.push(token); - return { - result: 'success', - wasTokenConsumed: true, - nextParser: this, - }; - } -} +export type TChatPromptToken = MarkdownLink | PromptVariable | PromptVariableWithData; /** * Decoder for the common chatbot prompt message syntax. @@ -170,11 +25,11 @@ class PartialPromptFileReference extends ParserBase { /** - * Currently active parser object that is used to parse a well-known equence of - * tokens, for instance, a `file reference` that consists of `hash`, `word`, and - * `colon` tokens sequence plus following file path part. + * Currently active parser object that is used to parse a well-known sequence of + * tokens, for instance, a `#file:/path/to/file.md` link that consists of `hash`, + * `word`, and `colon` tokens sequence plus the `file path` part that follows. */ - private current?: PartialPromptVariableName; + private current?: PartialPromptVariableName | PartialPromptVariableWithData; constructor( stream: ReadableStream, @@ -185,7 +40,7 @@ export class ChatPromptDecoder extends BaseDecoder { return token.symbol; }); + +/** + * List of characters that cannot be in a variable name (excluding the {@link STOP_CHARACTERS}). + */ +export const INVALID_NAME_CHARACTERS: readonly string[] = [Hash, Colon, ExclamationMark, LeftAngleBracket, RightAngleBracket, LeftBracket, RightBracket] + .map((token) => { return token.symbol; }); + +/** + * The parser responsible for parsing a `prompt variable name`. + * E.g., `#selection` or `#workspace` variable. If the `:` character follows + * the variable name, the parser transitions to {@link PartialPromptVariableWithData} + * that is also able to parse the `data` part of the variable. E.g., the `#file` part + * of the `#file:/path/to/something.md` sequence. + */ +export class PartialPromptVariableName extends ParserBase { + constructor(token: Hash) { + super([token]); + } + + @assertNotConsumed + public accept(token: TSimpleToken): TAcceptTokenResult { + // if a `stop` character is encountered, finish the parsing process + if (STOP_CHARACTERS.includes(token.text)) { + try { + // if it is possible to convert current parser to `PromptVariable`, return success result + return { + result: 'success', + nextParser: this.asPromptVariable(), + wasTokenConsumed: false, + }; + } catch (error) { + // otherwise fail + return { + result: 'failure', + wasTokenConsumed: false, + }; + } finally { + // in any case this is an end of the parsing process + this.isConsumed = true; + } + } + + // if a `:` character is encountered, we might transition to {@link PartialPromptVariableWithData} + if (token instanceof Colon) { + this.isConsumed = true; + + // if there is only one token before the `:` character, it must be the starting + // `#` symbol, therefore fail because there is no variable name present + if (this.currentTokens.length <= 1) { + return { + result: 'failure', + wasTokenConsumed: false, + }; + } + + // otherwise, if there are more characters after `#` available, + // we have a variable name, so we can transition to {@link PromptVariableWithData} + return { + result: 'success', + nextParser: new PartialPromptVariableWithData([...this.currentTokens, token]), + wasTokenConsumed: true, + }; + } + + // variables cannot have {@link INVALID_NAME_CHARACTERS} in their names + if (INVALID_NAME_CHARACTERS.includes(token.text)) { + this.isConsumed = true; + + return { + result: 'failure', + wasTokenConsumed: false, + }; + } + + // otherwise, a valid name character, so add it to the list of + // the current tokens and continue the parsing process + this.currentTokens.push(token); + + return { + result: 'success', + nextParser: this, + wasTokenConsumed: true, + }; + } + + /** + * Try to convert current parser instance into a fully-parsed {@link PromptVariable} token. + * + * @throws if sequence of tokens received so far do not constitute a valid prompt variable, + * for instance, if there is only `1` starting `#` token is available. + */ + public asPromptVariable(): PromptVariable { + // if there is only one token before the stop character + // must be the starting `#` one), then fail + assert( + this.currentTokens.length > 1, + 'Cannot create a prompt variable out of incomplete token sequence.', + ); + + const firstToken = this.currentTokens[0]; + const lastToken = this.currentTokens[this.currentTokens.length - 1]; + + // render the characters above into strings, excluding the starting `#` character + const variableNameTokens = this.currentTokens.slice(1); + const variableName = variableNameTokens.map(pick('text')).join(''); + + return new PromptVariable( + new Range( + firstToken.range.startLineNumber, + firstToken.range.startColumn, + lastToken.range.endLineNumber, + lastToken.range.endColumn, + ), + variableName, + ); + } +} + +/** + * The parser responsible for parsing a `prompt variable name` with `data`. + * E.g., the `/path/to/something.md` part of the `#file:/path/to/something.md` sequence. + */ +export class PartialPromptVariableWithData extends ParserBase { + + constructor(tokens: readonly TSimpleToken[]) { + const firstToken = tokens[0]; + const lastToken = tokens[tokens.length - 1]; + + // sanity checks of our expectations about the tokens list + assert( + tokens.length > 2, + `Tokens list must contain at least 3 items, got '${tokens.length}'.`, + ); + assert( + firstToken instanceof Hash, + `The first token must be a '#', got '${firstToken} '.`, + ); + assert( + lastToken instanceof Colon, + `The last token must be a ':', got '${lastToken} '.`, + ); + + super([...tokens]); + } + + @assertNotConsumed + public accept(token: TSimpleToken): TAcceptTokenResult { + // if a `stop` character is encountered, finish the parsing process + if (STOP_CHARACTERS.includes(token.text)) { + // in any case, success of failure below, this is an end of the parsing process + this.isConsumed = true; + + const firstToken = this.currentTokens[0]; + const lastToken = this.currentTokens[this.currentTokens.length - 1]; + + // tokens representing variable name without the `#` character at the start and + // the `:` data separator character at the end + const variableNameTokens = this.currentTokens.slice(1, this.startTokensCount - 1); + // tokens representing variable data without the `:` separator character at the start + const variableDataTokens = this.currentTokens.slice(this.startTokensCount); + // compute the full range of the variable token + const fullRange = new Range( + firstToken.range.startLineNumber, + firstToken.range.startColumn, + lastToken.range.endLineNumber, + lastToken.range.endColumn, + ); + + // render the characters above into strings + const variableName = variableNameTokens.map(pick('text')).join(''); + const variableData = variableDataTokens.map(pick('text')).join(''); + + return { + result: 'success', + nextParser: new PromptVariableWithData( + fullRange, + variableName, + variableData, + ), + wasTokenConsumed: false, + }; + } + + // otherwise, token is a valid data character - the data can contain almost any character, + // including `:` and `#`, hence add it to the list of the current tokens and continue + this.currentTokens.push(token); + + return { + result: 'success', + nextParser: this, + wasTokenConsumed: true, + }; + } + + /** + * Try to convert current parser instance into a fully-parsed {@link asPromptVariableWithData} token. + */ + public asPromptVariableWithData(): PromptVariableWithData { + // tokens representing variable name without the `#` character at the start and + // the `:` data separator character at the end + const variableNameTokens = this.currentTokens.slice(1, this.startTokensCount - 1); + // tokens representing variable data without the `:` separator character at the start + const variableDataTokens = this.currentTokens.slice(this.startTokensCount); + + // render the characters above into strings + const variableName = variableNameTokens.map(pick('text')).join(''); + const variableData = variableDataTokens.map(pick('text')).join(''); + + const firstToken = this.currentTokens[0]; + const lastToken = this.currentTokens[this.currentTokens.length - 1]; + + return new PromptVariableWithData( + new Range( + firstToken.range.startLineNumber, + firstToken.range.startColumn, + lastToken.range.endLineNumber, + lastToken.range.endColumn, + ), + variableName, + variableData, + ); + } +} diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/codecs/tokens/fileReference.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/codecs/tokens/fileReference.ts index 5efc84d9a6cc..fd04befbc575 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/codecs/tokens/fileReference.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/codecs/tokens/fileReference.ts @@ -3,118 +3,60 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ + +import { PromptVariableWithData } from './promptVariable.js'; import { assert } from '../../../../../../../base/common/assert.js'; import { IRange, Range } from '../../../../../../../editor/common/core/range.js'; import { BaseToken } from '../../../../../../../editor/common/codecs/baseToken.js'; -import { Word } from '../../../../../../../editor/common/codecs/simpleCodec/tokens/word.js'; /** - * Start sequence for a file reference token in a prompt. + * Name of the variable. */ -const TOKEN_START: string = '#file:'; +const VARIABLE_NAME: string = 'file'; /** * Object represents a file reference token inside a chatbot prompt. */ -export class FileReference extends BaseToken { - /** - * Start sequence for a file reference token in a prompt. - */ - public static readonly TOKEN_START = TOKEN_START; - +export class FileReference extends PromptVariableWithData { constructor( range: Range, public readonly path: string, ) { - super(range); - } - - /** - * Get full text of the file reference token. - */ - public get text(): string { - return `${TOKEN_START}${this.path}`; + super(range, VARIABLE_NAME, path); } /** - * Create a file reference token out of a generic `Word`. - * @throws if the word does not conform to the expected format or if - * the reference is an invalid `URI`. + * Create a {@link FileReference} from a {@link PromptVariableWithData} instance. + * @throws if variable name is not equal to {@link VARIABLE_NAME}. */ - public static fromWord(word: Word): FileReference { - const { text } = word; - - assert( - text.startsWith(TOKEN_START), - `The reference must start with "${TOKEN_START}", got ${text}.`, - ); - - const maybeReference = text.split(TOKEN_START); - + public static from(variable: PromptVariableWithData) { assert( - maybeReference.length === 2, - `The expected reference format is "${TOKEN_START}:filesystem-path", got ${text}.`, + variable.name === VARIABLE_NAME, + `Variable name must be '${VARIABLE_NAME}', got '${variable.name}'.`, ); - const [first, second] = maybeReference; - - assert( - first === '', - `The reference must start with "${TOKEN_START}", got ${first}.`, + return new FileReference( + variable.range, + variable.data, ); - - assert( - // Note! this accounts for both cases when second is `undefined` or `empty` - // and we don't care about rest of the "falsy" cases here - !!second, - `The reference path must be defined, got ${second}.`, - ); - - const reference = new FileReference( - word.range, - second, - ); - - return reference; } /** * Check if this token is equal to another one. */ public override equals(other: T): boolean { - if (!super.sameRange(other.range)) { - return false; - } - - if (!(other instanceof FileReference)) { + if ((other instanceof FileReference) === false) { return false; } - return this.text === other.text; + return super.equals(other); } /** - * Get the range of the `link part` of the token (e.g., + * Get the range of the `link` part of the token (e.g., * the `/path/to/file.md` part of `#file:/path/to/file.md`). */ public get linkRange(): IRange | undefined { - if (this.path.length === 0) { - return undefined; - } - - const { range } = this; - return new Range( - range.startLineNumber, - range.startColumn + TOKEN_START.length, - range.endLineNumber, - range.endColumn, - ); - } - - /** - * Return a string representation of the token. - */ - public override toString(): string { - return `file-ref("${this.text}")${this.range}`; + return super.dataRange; } } diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookChatEditContext.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/codecs/tokens/promptToken.ts similarity index 51% rename from src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookChatEditContext.ts rename to src/vs/workbench/contrib/chat/common/promptSyntax/codecs/tokens/promptToken.ts index ff706d889be3..38a9d69d5ac3 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookChatEditContext.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/codecs/tokens/promptToken.ts @@ -3,7 +3,9 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { localize } from '../../../../../../nls.js'; -import { RawContextKey } from '../../../../../../platform/contextkey/common/contextkey.js'; +import { BaseToken } from '../../../../../../../editor/common/codecs/baseToken.js'; -export const ctxNotebookHasEditorModification = new RawContextKey('chat.hasNotebookEditorModifications', undefined, localize('chat.hasNotebookEditorModifications', "The current Notebook editor contains chat modifications")); +/** + * Common base token that all chatbot `prompt` tokens should inherit from. + */ +export abstract class PromptToken extends BaseToken { } diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/codecs/tokens/promptVariable.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/codecs/tokens/promptVariable.ts new file mode 100644 index 000000000000..449926faa3e2 --- /dev/null +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/codecs/tokens/promptVariable.ts @@ -0,0 +1,152 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { PromptToken } from './promptToken.js'; +import { assert } from '../../../../../../../base/common/assert.js'; +import { IRange, Range } from '../../../../../../../editor/common/core/range.js'; +import { BaseToken } from '../../../../../../../editor/common/codecs/baseToken.js'; +import { INVALID_NAME_CHARACTERS, STOP_CHARACTERS } from '../parsers/promptVariableParser.js'; + +/** + * All prompt variables start with `#` character. + */ +const START_CHARACTER: string = '#'; + +/** + * Character that separates name of a prompt variable from its data. + */ +const DATA_SEPARATOR: string = ':'; + +/** + * Represents a `#variable` token in a prompt text. + */ +export class PromptVariable extends PromptToken { + constructor( + range: Range, + /** + * The name of a prompt variable, excluding the `#` character at the start. + */ + public readonly name: string, + ) { + // sanity check of characters used in the provided variable name + for (const character of name) { + assert( + (INVALID_NAME_CHARACTERS.includes(character) === false) && + (STOP_CHARACTERS.includes(character) === false), + `Variable 'name' cannot contain character '${character}', got '${name}'.`, + ); + } + + super(range); + } + + /** + * Get full text of the token. + */ + public get text(): string { + return `${START_CHARACTER}${this.name}`; + } + + /** + * Check if this token is equal to another one. + */ + public override equals(other: T): boolean { + if (!super.sameRange(other.range)) { + return false; + } + + if ((other instanceof PromptVariable) === false) { + return false; + } + + if (this.text.length !== other.text.length) { + return false; + } + + return this.text === other.text; + } + + /** + * Return a string representation of the token. + */ + public override toString(): string { + return `${this.text}${this.range}`; + } +} + +/** + * Represents a {@link PromptVariable} with additional data token in a prompt text. + * (e.g., `#variable:/path/to/file.md`) + */ +export class PromptVariableWithData extends PromptVariable { + constructor( + fullRange: Range, + /** + * The name of the variable, excluding the starting `#` character. + */ + name: string, + + /** + * The data of the variable, excluding the starting {@link DATA_SEPARATOR} character. + */ + public readonly data: string, + ) { + super(fullRange, name); + + // sanity check of characters used in the provided variable data + for (const character of data) { + assert( + (STOP_CHARACTERS.includes(character) === false), + `Variable 'data' cannot contain character '${character}', got '${data}'.`, + ); + } + } + + /** + * Get full text of the token. + */ + public override get text(): string { + return `${START_CHARACTER}${this.name}${DATA_SEPARATOR}${this.data}`; + } + + /** + * Check if this token is equal to another one. + */ + public override equals(other: T): boolean { + if ((other instanceof PromptVariableWithData) === false) { + return false; + } + + return super.equals(other); + } + + /** + * Range of the `data` part of the variable. + */ + public get dataRange(): IRange | undefined { + const { range } = this; + + // calculate the start column number of the `data` part of the variable + const dataStartColumn = range.startColumn + + START_CHARACTER.length + this.name.length + + DATA_SEPARATOR.length; + + // create `range` of the `data` part of the variable + const result = new Range( + range.startLineNumber, + dataStartColumn, + range.endLineNumber, + range.endColumn, + ); + + // if the resulting range is empty, return `undefined` + // because there is no `data` part present in the variable + if (result.isEmpty()) { + return undefined; + } + + return result; + } +} diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/filePromptContentsProvider.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/filePromptContentsProvider.ts index 9bda1b1acf33..4f35897df0a9 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/filePromptContentsProvider.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/filePromptContentsProvider.ts @@ -11,7 +11,7 @@ import { CancellationError } from '../../../../../../base/common/errors.js'; import { PromptContentsProviderBase } from './promptContentsProviderBase.js'; import { VSBufferReadableStream } from '../../../../../../base/common/buffer.js'; import { CancellationToken } from '../../../../../../base/common/cancellation.js'; -import { FileOpenFailed, NonPromptSnippetFile } from '../../promptFileReferenceErrors.js'; +import { OpenFailed, NotPromptFile, ResolveError, FolderReference } from '../../promptFileReferenceErrors.js'; import { FileChangesEvent, FileChangeType, IFileService } from '../../../../../../platform/files/common/files.js'; /** @@ -64,14 +64,33 @@ export class FilePromptContentProvider extends PromptContentsProviderBase()); + private readonly onContentChangedEmitter = this._register(new Emitter()); /** * Event that fires when the prompt contents change. The event is either * a `VSBufferReadableStream` stream with changed contents or an instance of - * the `ParseError` class representing a parsing failure case. + * the `ResolveError` class representing a parsing failure case. * * `Note!` this field is meant to be used by the external consumers of the prompt * contents provider that the classes that extend this abstract class. @@ -112,7 +112,7 @@ export abstract class PromptContentsProviderBase< this.onContentChangedEmitter.fire(stream); }) .catch((error) => { - if (error instanceof ParseError) { + if (error instanceof ResolveError) { this.onContentChangedEmitter.fire(error); return; diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/types.d.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/types.d.ts index 5d03ef2848ce..3ff8c569b6dc 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/types.d.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/contentProviders/types.d.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from '../../../../../../base/common/uri.js'; -import { ParseError } from '../../promptFileReferenceErrors.js'; +import { ResolveError } from '../../promptFileReferenceErrors.js'; import { IDisposable } from '../../../../../../base/common/lifecycle.js'; import { VSBufferReadableStream } from '../../../../../../base/common/buffer.js'; @@ -27,9 +27,9 @@ export interface IPromptContentsProvider extends IDisposable { /** * Event that fires when the prompt contents change. The event is either a * {@linkcode VSBufferReadableStream} stream with changed contents or - * an instance of the {@linkcode ParseError} error. + * an instance of the {@linkcode ResolveError} error. */ onContentChanged( - callback: (streamOrError: VSBufferReadableStream | ParseError) => void, + callback: (streamOrError: VSBufferReadableStream | ResolveError) => void, ): IDisposable; } diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/languageFeatures/promptLinkDiagnosticsProvider.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/languageFeatures/promptLinkDiagnosticsProvider.ts new file mode 100644 index 000000000000..7d46c72dbbe0 --- /dev/null +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/languageFeatures/promptLinkDiagnosticsProvider.ts @@ -0,0 +1,224 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IPromptsService } from '../service/types.js'; +import { IPromptFileReference } from '../parsers/types.js'; +import { assert } from '../../../../../../base/common/assert.js'; +import { NotPromptFile } from '../../promptFileReferenceErrors.js'; +import { ITextModel } from '../../../../../../editor/common/model.js'; +import { assertDefined } from '../../../../../../base/common/types.js'; +import { Disposable } from '../../../../../../base/common/lifecycle.js'; +import { IEditor } from '../../../../../../editor/common/editorCommon.js'; +import { ObjectCache } from '../../../../../../base/common/objectCache.js'; +import { TextModelPromptParser } from '../parsers/textModelPromptParser.js'; +import { Registry } from '../../../../../../platform/registry/common/platform.js'; +import { PromptsConfig } from '../../../../../../platform/prompts/common/config.js'; +import { isPromptFile } from '../../../../../../platform/prompts/common/constants.js'; +import { LifecyclePhase } from '../../../../../services/lifecycle/common/lifecycle.js'; +import { IEditorService } from '../../../../../services/editor/common/editorService.js'; +import { ObservableDisposable } from '../../../../../../base/common/observableDisposable.js'; +import { IWorkbenchContributionsRegistry, Extensions } from '../../../../../common/contributions.js'; +import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; +import { IConfigurationService } from '../../../../../../platform/configuration/common/configuration.js'; +import { IMarkerData, IMarkerService, MarkerSeverity } from '../../../../../../platform/markers/common/markers.js'; + +/** + * Unique ID of the markers provider class. + */ +const MARKERS_OWNER_ID = 'reusable-prompts-syntax'; + +/** + * Prompt links diagnostics provider for a single text model. + */ +class PromptLinkDiagnosticsProvider extends ObservableDisposable { + /** + * Reference to the current prompt syntax parser instance. + */ + private readonly parser: TextModelPromptParser; + + constructor( + private readonly editor: ITextModel, + @IMarkerService private readonly markerService: IMarkerService, + @IPromptsService private readonly promptsService: IPromptsService, + ) { + super(); + + this.parser = this.promptsService + .getSyntaxParserFor(this.editor) + .onUpdate(this.updateMarkers.bind(this)) + .onDispose(this.dispose.bind(this)) + .start(); + + // initialize markers + this.updateMarkers(); + } + + /** + * Update diagnostic markers for the current editor. + */ + private async updateMarkers() { + // ensure that parsing process is settled + await this.parser.allSettled(); + + // clean up all previously added markers + this.markerService.remove(MARKERS_OWNER_ID, [this.editor.uri]); + + const markers: IMarkerData[] = []; + for (const link of this.parser.references) { + const { topError, linkRange } = link; + + if (!topError || !linkRange) { + continue; + } + + const { originalError } = topError; + + // the `NotPromptFile` error is allowed because we allow users + // to include non-prompt file links in the prompt files + // note! this check also handles the `FolderReference` error + if (originalError instanceof NotPromptFile) { + continue; + } + + markers.push(toMarker(link)); + } + + this.markerService.changeOne( + MARKERS_OWNER_ID, + this.editor.uri, + markers, + ); + } +} + +/** + * Convert a prompt link with an issue to a marker data. + * + * @throws + * - if there is no link issue (e.g., `topError` undefined) + * - if there is no link range to highlight (e.g., `linkRange` undefined) + * - if the original error is of `NotPromptFile` type - we don't want to + * show diagnostic markers for non-prompt file links in the prompts + */ +const toMarker = ( + link: IPromptFileReference, +): IMarkerData => { + const { topError, linkRange } = link; + + // a sanity check because this function must be + // used only if these link attributes are present + assertDefined( + topError, + 'Top error must to be defined.', + ); + assertDefined( + linkRange, + 'Link range must to be defined.', + ); + + const { originalError } = topError; + assert( + !(originalError instanceof NotPromptFile), + 'Error must not be of "not prompt file" type.', + ); + + // `error` severity for the link itself, `warning` for any of its children + const severity = (topError.errorSubject === 'root') + ? MarkerSeverity.Error + : MarkerSeverity.Warning; + + return { + message: topError.localizedMessage, + severity, + ...linkRange, + }; +}; + +/** + * The class that manages creation and disposal of {@link PromptLinkDiagnosticsProvider} + * classes for each specific editor text model. + */ +export class PromptLinkDiagnosticsInstanceManager extends Disposable { + /** + * Currently available {@link PromptLinkDiagnosticsProvider} instances. + */ + private readonly providers: ObjectCache; + + constructor( + @IEditorService editorService: IEditorService, + @IInstantiationService initService: IInstantiationService, + @IConfigurationService configService: IConfigurationService, + ) { + super(); + + // cache of prompt marker providers + this.providers = this._register( + new ObjectCache((editor: ITextModel) => { + const parser: PromptLinkDiagnosticsProvider = initService.createInstance( + PromptLinkDiagnosticsProvider, + editor, + ); + + // this is a sanity check and the contract of the object cache, + // we must return a non-disposed object from this factory function + parser.assertNotDisposed( + 'Created prompt parser must not be disposed.', + ); + + return parser; + }), + ); + + // if the feature is disabled, do not create any providers + if (!PromptsConfig.enabled(configService)) { + return; + } + + // subscribe to changes of the active editor + this._register(editorService.onDidActiveEditorChange(() => { + const { activeTextEditorControl } = editorService; + if (!activeTextEditorControl) { + return; + } + + this.handleNewEditor(activeTextEditorControl); + })); + + // handle existing visible text editors + editorService + .visibleTextEditorControls + .forEach(this.handleNewEditor.bind(this)); + } + + /** + * Initialize a new {@link PromptLinkDiagnosticsProvider} for the given editor. + */ + private handleNewEditor(editor: IEditor): this { + const model = editor.getModel(); + if (!model) { + return this; + } + + // we support only `text editors` for now so filter out `diff` ones + if ('modified' in model || 'model' in model) { + return this; + } + + // enable this only for prompt file editors + if (!isPromptFile(model.uri)) { + return this; + } + + // note! calling `get` also creates a provider if it does not exist; + // and the provider is auto-removed when the model is disposed + this.providers.get(model); + + return this; + } +} + +// register the provider as a workbench contribution +Registry.as(Extensions.Workbench) + .registerWorkbenchContribution(PromptLinkDiagnosticsInstanceManager, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/languageFeatures/promptLinkProvider.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/languageFeatures/promptLinkProvider.ts index 4d70254532e4..9b40572a7aff 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/languageFeatures/promptLinkProvider.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/languageFeatures/promptLinkProvider.ts @@ -9,21 +9,21 @@ import { assert } from '../../../../../../base/common/assert.js'; import { ITextModel } from '../../../../../../editor/common/model.js'; import { assertDefined } from '../../../../../../base/common/types.js'; import { Disposable } from '../../../../../../base/common/lifecycle.js'; -import { NonPromptSnippetFile } from '../../promptFileReferenceErrors.js'; import { CancellationError } from '../../../../../../base/common/errors.js'; import { CancellationToken } from '../../../../../../base/common/cancellation.js'; import { Registry } from '../../../../../../platform/registry/common/platform.js'; +import { FolderReference, NotPromptFile } from '../../promptFileReferenceErrors.js'; import { LifecyclePhase } from '../../../../../services/lifecycle/common/lifecycle.js'; import { ILink, ILinksList, LinkProvider } from '../../../../../../editor/common/languages.js'; +import { IWorkbenchContributionsRegistry, Extensions } from '../../../../../common/contributions.js'; import { ILanguageFeaturesService } from '../../../../../../editor/common/services/languageFeatures.js'; -import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions } from '../../../../../common/contributions.js'; /** * Provides link references for prompt files. */ export class PromptLinkProvider extends Disposable implements LinkProvider { constructor( - @IPromptsService private readonly promptSyntaxService: IPromptsService, + @IPromptsService private readonly promptsService: IPromptsService, @ILanguageFeaturesService private readonly languageService: ILanguageFeaturesService, ) { super(); @@ -43,7 +43,7 @@ export class PromptLinkProvider extends Disposable implements LinkProvider { new CancellationError(), ); - const parser = this.promptSyntaxService.getSyntaxParserFor(model); + const parser = this.promptsService.getSyntaxParserFor(model); assert( !parser.disposed, 'Prompt parser must not be disposed.', @@ -69,10 +69,15 @@ export class PromptLinkProvider extends Disposable implements LinkProvider { return true; } - return errorCondition instanceof NonPromptSnippetFile; + // don't provide links for folder references + if (errorCondition instanceof FolderReference) { + return false; + } + + return errorCondition instanceof NotPromptFile; }) .map((reference) => { - const { linkRange } = reference; + const { uri, linkRange } = reference; // must always be true because of the filter above assertDefined( @@ -80,10 +85,9 @@ export class PromptLinkProvider extends Disposable implements LinkProvider { 'Link range must be defined.', ); - return { range: linkRange, - url: reference.uri, + url: uri, }; }); @@ -94,5 +98,5 @@ export class PromptLinkProvider extends Disposable implements LinkProvider { } // register the provider as a workbench contribution -Registry.as(WorkbenchExtensions.Workbench) +Registry.as(Extensions.Workbench) .registerWorkbenchContribution(PromptLinkProvider, LifecyclePhase.Eventually); diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/languageFeatures/promptPathAutocompletion.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/languageFeatures/promptPathAutocompletion.ts index 7f026bdfff83..26f630845d57 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/languageFeatures/promptPathAutocompletion.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/languageFeatures/promptPathAutocompletion.ts @@ -17,14 +17,13 @@ import { LANGUAGE_SELECTOR } from '../constants.js'; import { IPromptsService } from '../service/types.js'; import { URI } from '../../../../../../base/common/uri.js'; -import { IPromptFileReference } from '../parsers/types.js'; -import { FileReference } from '../codecs/tokens/fileReference.js'; import { assertOneOf } from '../../../../../../base/common/types.js'; import { isWindows } from '../../../../../../base/common/platform.js'; import { ITextModel } from '../../../../../../editor/common/model.js'; import { Disposable } from '../../../../../../base/common/lifecycle.js'; import { CancellationError } from '../../../../../../base/common/errors.js'; import { Position } from '../../../../../../editor/common/core/position.js'; +import { IPromptFileReference, IPromptReference } from '../parsers/types.js'; import { dirname, extUri } from '../../../../../../base/common/resources.js'; import { assert, assertNever } from '../../../../../../base/common/assert.js'; import { IFileService } from '../../../../../../platform/files/common/files.js'; @@ -57,7 +56,7 @@ type TTriggerCharacter = ':' | '.' | '/'; * Finds a file reference that suites the provided `position`. */ const findFileReference = ( - references: readonly IPromptFileReference[], + references: readonly IPromptReference[], position: Position, ): IPromptFileReference | undefined => { for (const reference of references) { @@ -69,7 +68,7 @@ const findFileReference = ( } // this ensures that we handle only the `#file:` references for now - if (!reference.text.startsWith(FileReference.TOKEN_START)) { + if (reference.subtype !== 'prompt') { return undefined; } @@ -245,21 +244,20 @@ export class PromptPathAutocompletion extends Disposable implements CompletionIt // when character is `:`, there must be no link present yet // otherwise the `:` was used in the middle of the link hence // we don't want to provide suggestions for that - if (character === ':' && linkRange !== undefined) { + if ((character === ':') && (linkRange !== undefined)) { return []; } // otherwise when the `.` character is present, it is inside the link part // of the reference, hence we always expect the link range to be present - if (character === '.' && linkRange === undefined) { + if ((character === '.') && (linkRange === undefined)) { return []; } const suggestions = await this.getFolderSuggestions(fileFolderUri); - // replacement range of the suggestions - // when character is `.` we want to also replace it, because we add - // the `./` at the beginning of all the relative paths + // replacement range for suggestions; when character is `.`, we want to also + // replace it, because we add `./` at the beginning of all the relative paths const startColumnOffset = (character === '.') ? 1 : 0; const range = { ...fileReference.range, diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/basePromptParser.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/basePromptParser.ts index 4e6ce0657b93..86b1833c96c1 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/basePromptParser.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/basePromptParser.ts @@ -3,12 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { localize } from '../../../../../../nls.js'; +import { TopError } from './topError.js'; import { URI } from '../../../../../../base/common/uri.js'; import { ChatPromptCodec } from '../codecs/chatPromptCodec.js'; import { Emitter } from '../../../../../../base/common/event.js'; -import { assert } from '../../../../../../base/common/assert.js'; -import { IPromptFileReference, IResolveError } from './types.js'; import { FileReference } from '../codecs/tokens/fileReference.js'; import { ChatPromptDecoder } from '../codecs/chatPromptDecoder.js'; import { IRange } from '../../../../../../editor/common/core/range.js'; @@ -16,29 +14,22 @@ import { assertDefined } from '../../../../../../base/common/types.js'; import { IPromptContentsProvider } from '../contentProviders/types.js'; import { DeferredPromise } from '../../../../../../base/common/async.js'; import { ILogService } from '../../../../../../platform/log/common/log.js'; +import { PromptVariableWithData } from '../codecs/tokens/promptVariable.js'; import { basename, extUri } from '../../../../../../base/common/resources.js'; +import { assert, assertNever } from '../../../../../../base/common/assert.js'; import { VSBufferReadableStream } from '../../../../../../base/common/buffer.js'; import { isPromptFile } from '../../../../../../platform/prompts/common/constants.js'; import { ObservableDisposable } from '../../../../../../base/common/observableDisposable.js'; import { FilePromptContentProvider } from '../contentProviders/filePromptContentsProvider.js'; +import { IPromptFileReference, IPromptReference, IResolveError, ITopError } from './types.js'; import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; import { MarkdownLink } from '../../../../../../editor/common/codecs/markdownCodec/tokens/markdownLink.js'; -import { FileOpenFailed, NonPromptSnippetFile, RecursiveReference, ParseError, FailedToResolveContentsStream } from '../../promptFileReferenceErrors.js'; - -/** - * Well-known localized error messages. - */ -const errorMessages = { - recursion: localize('chatPromptInstructionsRecursiveReference', 'Recursive reference found'), - fileOpenFailed: localize('chatPromptInstructionsFileOpenFailed', 'Failed to open file'), - streamOpenFailed: localize('chatPromptInstructionsStreamOpenFailed', 'Failed to open contents stream'), - brokenChild: localize('chatPromptInstructionsBrokenReference', 'Contains a broken reference that will be ignored'), -}; +import { OpenFailed, NotPromptFile, RecursiveReference, FolderReference, ResolveError } from '../../promptFileReferenceErrors.js'; /** * Error conditions that may happen during the file reference resolution. */ -export type TErrorCondition = FileOpenFailed | RecursiveReference | NonPromptSnippetFile; +export type TErrorCondition = OpenFailed | RecursiveReference | FolderReference | NotPromptFile; /** * Base prompt parser class that provides a common interface for all @@ -65,13 +56,13 @@ export abstract class BasePromptParser extend return this; } - private _errorCondition?: ParseError; + private _errorCondition?: ResolveError; /** * If file reference resolution fails, this attribute will be set * to an error instance that describes the error condition. */ - public get errorCondition(): ParseError | undefined { + public get errorCondition(): ResolveError | undefined { return this._errorCondition; } @@ -157,7 +148,10 @@ export abstract class BasePromptParser extend if (seenReferences.includes(this.uri.path)) { seenReferences.push(this.uri.path); - this._errorCondition = new RecursiveReference(this.uri, seenReferences); + this._errorCondition = new RecursiveReference( + this.uri, + seenReferences, + ); this._onUpdate.fire(); this.firstParseResult.complete(); @@ -196,7 +190,7 @@ export abstract class BasePromptParser extend * references recursion. */ private onContentsChanged( - streamOrError: VSBufferReadableStream | ParseError, + streamOrError: VSBufferReadableStream | ResolveError, seenReferences: string[], ): void { // dispose and cleanup the previously received stream @@ -209,7 +203,7 @@ export abstract class BasePromptParser extend this.disposeReferences(); // if an error received, set up the error condition and stop - if (streamOrError instanceof ParseError) { + if (streamOrError instanceof ResolveError) { this._errorCondition = streamOrError; this._onUpdate.fire(); @@ -225,8 +219,12 @@ export abstract class BasePromptParser extend // when some tokens received, process and store the references this.stream.on('data', (token) => { - if (token instanceof FileReference) { - this.onReference(token, [...seenReferences]); + if (token instanceof PromptVariableWithData) { + try { + this.onReference(FileReference.from(token), [...seenReferences]); + } catch (error) { + // no-op + } } // note! the `isURL` is a simple check and needs to be improved to truly @@ -345,7 +343,7 @@ export abstract class BasePromptParser extend /** * Get a list of immediate child references of the prompt. */ - public get references(): readonly IPromptFileReference[] { + public get references(): readonly IPromptReference[] { return [...this._references]; } @@ -353,8 +351,8 @@ export abstract class BasePromptParser extend * Get a list of all references of the prompt, including * all possible nested references its children may have. */ - public get allReferences(): readonly IPromptFileReference[] { - const result: IPromptFileReference[] = []; + public get allReferences(): readonly IPromptReference[] { + const result: IPromptReference[] = []; for (const reference of this.references) { result.push(reference); @@ -370,13 +368,24 @@ export abstract class BasePromptParser extend /** * Get list of all valid references. */ - public get allValidReferences(): readonly IPromptFileReference[] { + public get allValidReferences(): readonly IPromptReference[] { return this.allReferences // filter out unresolved references .filter((reference) => { const { errorCondition } = reference; - return !errorCondition || (errorCondition instanceof NonPromptSnippetFile); + // include all references without errors + if (!errorCondition) { + return true; + } + + // filter out folder references from the list + if (errorCondition instanceof FolderReference) { + return false; + } + + // include non-prompt file references + return (errorCondition instanceof NotPromptFile); }); } @@ -388,38 +397,43 @@ export abstract class BasePromptParser extend .map(child => child.uri); } + /** + * Get list of errors for the direct links of the current reference. + */ + public get errors(): readonly ResolveError[] { + const childErrors: ResolveError[] = []; + + for (const reference of this.references) { + const { errorCondition } = reference; + + if (errorCondition && (!(errorCondition instanceof NotPromptFile))) { + childErrors.push(errorCondition); + } + } + + return childErrors; + } + /** * List of all errors that occurred while resolving the current * reference including all possible errors of nested children. */ - public get allErrors(): ParseError[] { - const result: ParseError[] = []; - - // collect error conditions of all child references - const childErrorConditions = this - // get entire reference tree - .allReferences - // filter out children without error conditions or - // the ones that are non-prompt snippet files - .filter((childReference) => { - const { errorCondition } = childReference; - - return errorCondition && !(errorCondition instanceof NonPromptSnippetFile); - }) - // map to error condition objects - .map((childReference): ParseError => { - const { errorCondition } = childReference; - - // `must` always be `true` because of the `filter` call above - assertDefined( - errorCondition, - `Error condition must be present for '${childReference.uri.path}'.`, - ); - - return errorCondition; - }); + public get allErrors(): readonly IResolveError[] { + const result: IResolveError[] = []; - result.push(...childErrorConditions); + for (const reference of this.references) { + const { errorCondition } = reference; + + if (errorCondition && (!(errorCondition instanceof NotPromptFile))) { + result.push({ + originalError: errorCondition, + parentUri: this.uri, + }); + } + + // recursively collect all possible errors of its children + result.push(...reference.allErrors); + } return result; } @@ -428,72 +442,48 @@ export abstract class BasePromptParser extend * The top most error of the current reference or any of its * possible child reference errors. */ - public get topError(): IResolveError | undefined { - // get all errors, including error of this object - const errors = []; + public get topError(): ITopError | undefined { if (this.errorCondition) { - errors.push(this.errorCondition); + return new TopError({ + errorSubject: 'root', + errorsCount: 1, + originalError: this.errorCondition, + }); } - errors.push(...this.allErrors); - // if no errors, nothing to do - if (errors.length === 0) { - return undefined; + const childErrors: ResolveError[] = [...this.errors]; + const nestedErrors: IResolveError[] = []; + for (const reference of this.references) { + nestedErrors.push(...reference.allErrors); } - - // if the first error is the error of the root reference, - // then return it as an `error` otherwise use `warning` - const [firstError, ...restErrors] = errors; - const isRootError = (firstError === this.errorCondition); - - // if a child error - the error is somewhere in the nested references tree, - // then use message prefix to highlight that this is not a root error - const prefix = (!isRootError) - ? `${errorMessages.brokenChild}: ` - : ''; - - const moreSuffix = restErrors.length > 0 - ? `\n-\n +${restErrors.length} more error${restErrors.length > 1 ? 's' : ''}` - : ''; - - const errorMessage = this.getErrorMessage(firstError); - return { - isRootError, - message: `${prefix}${errorMessage}${moreSuffix}`, - }; - } - - /** - * Get message for the provided error condition object. - * - * @param error Error object that extends {@link ParseError}. - * @returns Error message. - */ - protected getErrorMessage(error: TError): string { - if (error instanceof FileOpenFailed) { - return `${errorMessages.fileOpenFailed} '${error.uri.path}'.`; + if (childErrors.length === 0 && nestedErrors.length === 0) { + return undefined; } - if (error instanceof FailedToResolveContentsStream) { - return `${errorMessages.streamOpenFailed} '${error.uri.path}'.`; - } + const firstDirectChildError = childErrors[0]; + const firstNestedChildError = nestedErrors[0]; + const hasDirectChildError = (firstDirectChildError !== undefined); - // if a recursion, provide the entire recursion path so users - // can use it for the debugging purposes - if (error instanceof RecursiveReference) { - const { recursivePath } = error; + const firstChildError = (hasDirectChildError) + ? { + originalError: firstDirectChildError, + parentUri: this.uri, + } + : firstNestedChildError; - const recursivePathString = recursivePath - .map((path) => { - return basename(URI.file(path)); - }) - .join(' -> '); + const totalErrorsCount = childErrors.length + nestedErrors.length; - return `${errorMessages.recursion}:\n${recursivePathString}`; - } + const subject = (hasDirectChildError) + ? 'child' + : 'indirect-child'; - return error.message; + return new TopError({ + errorSubject: subject, + originalError: firstChildError.originalError, + parentUri: firstChildError.parentUri, + errorsCount: totalErrorsCount, + }); } /** @@ -535,7 +525,7 @@ export abstract class BasePromptParser extend /** * Prompt file reference object represents any file reference inside prompt - * text contents. For instanve the file variable(`#file:/path/to/file.md`) + * text contents. For instance the file variable(`#file:/path/to/file.md`) * or a markdown link(`[#file:file.md](/path/to/file.md)`). */ export class PromptFileReference extends BasePromptParser implements IPromptFileReference { @@ -564,7 +554,7 @@ export class PromptFileReference extends BasePromptParser { diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/topError.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/topError.ts new file mode 100644 index 000000000000..97584c8a917c --- /dev/null +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/topError.ts @@ -0,0 +1,102 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ITopError } from './types.js'; +import { localize } from '../../../../../../nls.js'; +import { assert } from '../../../../../../base/common/assert.js'; +import { assertDefined } from '../../../../../../base/common/types.js'; +import { OpenFailed, RecursiveReference, FailedToResolveContentsStream } from '../../promptFileReferenceErrors.js'; + +/** + * The top-most error of the reference tree. + */ +export class TopError implements ITopError { + public readonly originalError: ITopError['originalError']; + public readonly errorSubject: ITopError['errorSubject']; + public readonly errorsCount: ITopError['errorsCount']; + public readonly parentUri: ITopError['parentUri']; + + constructor( + readonly options: Omit, + ) { + this.originalError = options.originalError; + this.errorSubject = options.errorSubject; + this.errorsCount = options.errorsCount; + this.parentUri = options.parentUri; + } + + public get localizedMessage(): string { + const { originalError, parentUri, errorSubject: subject, errorsCount } = this; + + assert( + errorsCount >= 1, + `Error count must be at least 1, got '${errorsCount}'.`, + ); + + // a note about how many more link issues are there + const moreIssuesLabel = (errorsCount > 1) + ? localize('workbench.reusable-prompts.top-error.more-issues-label', "\n(+{0} more issues)", errorsCount - 1) + : ''; + + if (subject === 'root') { + if (originalError instanceof OpenFailed) { + return localize( + 'workbench.reusable-prompts.top-error.open-failed', + "Cannot open '{0}'.{1}", + originalError.uri.path, + moreIssuesLabel, + ); + } + + if (originalError instanceof FailedToResolveContentsStream) { + return localize( + 'workbench.reusable-prompts.top-error.cannot-read', + "Cannot read '{0}'.{1}", + originalError.uri.path, + moreIssuesLabel, + ); + } + + if (originalError instanceof RecursiveReference) { + return localize( + 'workbench.reusable-prompts.top-error.recursive-reference', + "Recursion to itself.", + ); + } + + return originalError.message + moreIssuesLabel; + } + + // a sanity check - because the error subject is not `root`, the parent must set + assertDefined( + parentUri, + 'Parent URI must be defined for error of non-root link.', + ); + + const errorMessageStart = (subject === 'child') + ? localize( + 'workbench.reusable-prompts.top-error.child.direct', + "Contains", + ) + : localize( + 'workbench.reusable-prompts.top-error.child.indirect', + "Indirectly referenced prompt '{0}' contains", + parentUri.path, + ); + + const linkIssueName = (originalError instanceof RecursiveReference) + ? localize('recursive', "recursive") + : localize('broken', "broken"); + + return localize( + 'workbench.reusable-prompts.top-error.child.final-message', + "{0} a {1} link to '{2}' that will be ignored.{3}", + errorMessageStart, + linkIssueName, + originalError.uri.path, + moreIssuesLabel, + ); + } +} diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/types.d.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/types.d.ts index 325e5c3ec451..04f07f388b8e 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/types.d.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/parsers/types.d.ts @@ -4,39 +4,64 @@ *--------------------------------------------------------------------------------------------*/ import { URI } from '../../../../../../base/common/uri.js'; -import { ParseError } from '../../promptFileReferenceErrors.js'; +import { ResolveError } from '../../promptFileReferenceErrors.js'; import { IDisposable } from '../../../../../../base/common/lifecycle.js'; import { IRange, Range } from '../../../../../../editor/common/core/range.js'; /** - * Interface for a resolve error. + * A resolve error with a parent prompt URI, if any. */ export interface IResolveError { /** - * Localized error message. + * Original error instance. */ - message: string; + readonly originalError: ResolveError; /** - * Whether this error is for the root reference - * object, or for one of its possible children. + * URI of the parent that references this error. */ - isRootError: boolean; + readonly parentUri?: URI; } /** - * List of all available prompt reference types. + * Top most error of the reference tree. */ -type PromptReferenceTypes = 'file'; +export interface ITopError extends IResolveError { + /** + * Where does the error belong to: + * + * - `root` - the error is the top most error of the entire tree + * - `child` - the error is a child of the root error + * - `indirect-child` - the error is a child of a child of the root error + */ + readonly errorSubject: 'root' | 'child' | 'indirect-child'; + + /** + * Total number of all errors in the references tree, including the error + * of the current reference and all possible errors of its children. + */ + readonly errorsCount: number; + + /** + * Localized error message. + */ + readonly localizedMessage: string; +} /** - * Interface for a generic prompt reference. + * Base interface for a generic prompt reference. */ -export interface IPromptReference extends IDisposable { +interface IPromptReferenceBase extends IDisposable { /** - * Type of the prompt reference. + * Type of the prompt reference. E.g., `file`, `http`, `image`, etc. */ - readonly type: PromptReferenceTypes; + readonly type: string; + + /** + * Subtype of the prompt reference. For instance a `file` reference + * can be a `markdown link` or a prompt `#file:` variable reference. + */ + readonly subtype: string; /** * URI component of the associated with this reference. @@ -85,19 +110,24 @@ export interface IPromptReference extends IDisposable { * * See also {@linkcode resolveFailed}. */ - readonly errorCondition: ParseError | undefined; + readonly errorCondition: ResolveError | undefined; + + /** + * Get list of errors for the direct links of the current reference. + */ + readonly errors: readonly ResolveError[]; /** * List of all errors that occurred while resolving the current * reference including all possible errors of nested children. */ - readonly allErrors: readonly ParseError[]; + readonly allErrors: readonly IResolveError[]; /** * The top most error of the current reference or any of its * possible child reference errors. */ - readonly topError: IResolveError | undefined; + readonly topError: ITopError | undefined; /** * Direct references of the current reference. @@ -138,9 +168,20 @@ export interface IPromptReference extends IDisposable { } /** - * The special case of the {@linkcode IPromptReference} that pertains + * The special case of the {@linkcode IPromptReferenceBase} that pertains * to a file resource on the disk. */ -export interface IPromptFileReference extends IPromptReference { +export interface IPromptFileReference extends IPromptReferenceBase { readonly type: 'file'; + + /** + * Subtype of a file reference, - either a prompt `#file` variable, + * or a `markdown link` (e.g., `[caption](/path/to/file.md)`). + */ + readonly subtype: 'prompt' | 'markdown'; } + +/** + * List of all known prompt reference types. + */ +export type IPromptReference = IPromptFileReference; diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.ts index 4f0de893ce3c..dc1ac08c94da 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/service/promptsService.ts @@ -82,11 +82,11 @@ export class PromptsService extends Disposable implements IPromptsService { } public async listPromptFiles(): Promise { - const globalLocations = [this.userDataService.currentProfile.promptsHome]; + const userLocations = [this.userDataService.currentProfile.promptsHome]; const prompts = await Promise.all([ - this.fileLocator.listFilesIn(globalLocations, []) - .then(withType('global')), + this.fileLocator.listFilesIn(userLocations, []) + .then(withType('user')), this.fileLocator.listFiles([]) .then(withType('local')), ]); @@ -100,11 +100,11 @@ export class PromptsService extends Disposable implements IPromptsService { // sanity check to make sure we don't miss a new prompt type // added in the future assert( - type === 'local' || type === 'global', + type === 'local' || type === 'user', `Unknown prompt type '${type}'.`, ); - const prompts = (type === 'global') + const prompts = (type === 'user') ? [this.userDataService.currentProfile.promptsHome] : this.fileLocator.getConfigBasedSourceFolders(); @@ -116,7 +116,7 @@ export class PromptsService extends Disposable implements IPromptsService { * Utility to add a provided prompt `type` to a prompt URI. */ const addType = ( - type: 'local' | 'global', + type: 'local' | 'user', ): (uri: URI) => IPromptPath => { return (uri) => { return { uri, type: type }; @@ -127,7 +127,7 @@ const addType = ( * Utility to add a provided prompt `type` to a list of prompt URIs. */ const withType = ( - type: 'local' | 'global', + type: 'local' | 'user', ): (uris: readonly URI[]) => (readonly IPromptPath[]) => { return (uris) => { return uris diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/service/types.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/service/types.ts index cf8576067f24..1f29a5811b5e 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/service/types.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/service/types.ts @@ -17,9 +17,9 @@ export const IPromptsService = createDecorator('IPromptsService /** * Supported prompt types. * - `local` means the prompt is a local file. -* - `global` means a "roamble" prompt file (similar to snippets). +* - `user` means a "roamble" prompt file (similar to snippets). */ -type TPromptsType = 'local' | 'global'; +type TPromptsType = 'local' | 'user'; /** * Represents a prompt path with its type. diff --git a/src/vs/workbench/contrib/chat/common/promptSyntax/utils/promptFilesLocator.ts b/src/vs/workbench/contrib/chat/common/promptSyntax/utils/promptFilesLocator.ts index 2d787402fbe1..348b9b628015 100644 --- a/src/vs/workbench/contrib/chat/common/promptSyntax/utils/promptFilesLocator.ts +++ b/src/vs/workbench/contrib/chat/common/promptSyntax/utils/promptFilesLocator.ts @@ -13,7 +13,7 @@ import { IWorkspaceContextService } from '../../../../../../platform/workspace/c import { IConfigurationService } from '../../../../../../platform/configuration/common/configuration.js'; /** - * Class to locate prompt files. + * Utility class to locate prompt files. */ export class PromptFilesLocator { constructor( diff --git a/src/vs/workbench/contrib/chat/common/tools/editFileTool.ts b/src/vs/workbench/contrib/chat/common/tools/editFileTool.ts index 655ee5a35fae..b6384cf0ad54 100644 --- a/src/vs/workbench/contrib/chat/common/tools/editFileTool.ts +++ b/src/vs/workbench/contrib/chat/common/tools/editFileTool.ts @@ -13,6 +13,8 @@ import { localize } from '../../../../../nls.js'; import { IWorkspaceContextService } from '../../../../../platform/workspace/common/workspace.js'; import { SaveReason } from '../../../../common/editor.js'; import { ITextFileService } from '../../../../services/textfile/common/textfiles.js'; +import { CellUri } from '../../../notebook/common/notebookCommon.js'; +import { INotebookService } from '../../../notebook/common/notebookService.js'; import { ICodeMapperService } from '../../common/chatCodeMapperService.js'; import { IChatEditingService } from '../../common/chatEditingService.js'; import { ChatModel } from '../../common/chatModel.js'; @@ -41,10 +43,10 @@ class Person { } `; -export const EditToolId = 'vscode_editFile'; +export const ExtensionEditToolId = 'vscode_editFile'; +export const InternalEditToolId = 'vscode_editFile_internal'; export const EditToolData: IToolData = { - id: EditToolId, - tags: ['vscode_editing'], + id: InternalEditToolId, displayName: localize('chat.tools.editFile', "Edit File"), modelDescription: `Edit a file in the workspace. Use this tool once per file that needs to be modified, even if there are multiple changes for a file. Generate the "explanation" property first. ${codeInstructions}`, inputSchema: { @@ -76,6 +78,7 @@ export class EditTool implements IToolImpl { @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, @ILanguageModelIgnoredFilesService private readonly ignoredFilesService: ILanguageModelIgnoredFilesService, @ITextFileService private readonly textFileService: ITextFileService, + @INotebookService private readonly notebookService: INotebookService, ) { } async invoke(invocation: IToolInvocation, countTokens: CountTokensCallback, token: CancellationToken): Promise { @@ -118,11 +121,21 @@ export class EditTool implements IToolImpl { kind: 'markdownContent', content: new MarkdownString(parameters.code + '\n````\n') }); - model.acceptResponseProgress(request, { - kind: 'textEdit', - edits: [], - uri - }); + const notebookUri = CellUri.parse(uri)?.notebook || uri; + // Signal start. + if (this.notebookService.hasSupportedNotebooks(notebookUri) && (this.notebookService.getNotebookTextModel(notebookUri))) { + model.acceptResponseProgress(request, { + kind: 'notebookEdit', + edits: [], + uri: notebookUri + }); + } else { + model.acceptResponseProgress(request, { + kind: 'textEdit', + edits: [], + uri + }); + } const editSession = this.chatEditingService.getEditingSession(model.sessionId); if (!editSession) { @@ -142,7 +155,12 @@ export class EditTool implements IToolImpl { }, }, token); - model.acceptResponseProgress(request, { kind: 'textEdit', uri, edits: [], done: true }); + // Signal end. + if (this.notebookService.hasSupportedNotebooks(notebookUri) && (this.notebookService.getNotebookTextModel(notebookUri))) { + model.acceptResponseProgress(request, { kind: 'notebookEdit', uri: notebookUri, edits: [], done: true }); + } else { + model.acceptResponseProgress(request, { kind: 'textEdit', uri, edits: [], done: true }); + } if (result?.errorMessage) { throw new Error(result.errorMessage); diff --git a/src/vs/workbench/contrib/chat/common/tools/languageModelToolsContribution.ts b/src/vs/workbench/contrib/chat/common/tools/languageModelToolsContribution.ts index e75fb61664af..d375686f5536 100644 --- a/src/vs/workbench/contrib/chat/common/tools/languageModelToolsContribution.ts +++ b/src/vs/workbench/contrib/chat/common/tools/languageModelToolsContribution.ts @@ -135,6 +135,8 @@ function toToolKey(extensionIdentifier: ExtensionIdentifier, toolName: string) { return `${extensionIdentifier.value}/${toolName}`; } +const CopilotAgentModeTag = 'vscode_editing'; + export class LanguageModelToolsExtensionPointHandler implements IWorkbenchContribution { static readonly ID = 'workbench.contrib.toolsExtensionPointHandler'; @@ -167,7 +169,14 @@ export class LanguageModelToolsExtensionPointHandler implements IWorkbenchContri continue; } - if (rawTool.tags?.some(tag => tag.startsWith('copilot_') || tag.startsWith('vscode_')) && !isProposedApiEnabled(extension.description, 'chatParticipantPrivate')) { + if (rawTool.tags?.includes(CopilotAgentModeTag)) { + if (!isProposedApiEnabled(extension.description, 'languageModelToolsForAgent') && !isProposedApiEnabled(extension.description, 'chatParticipantPrivate')) { + logService.error(`Extension '${extension.description.identifier.value}' CANNOT register tool with tag "${CopilotAgentModeTag}" without enabling 'languageModelToolsForAgent' proposal`); + continue; + } + } + + if (rawTool.tags?.some(tag => tag !== CopilotAgentModeTag && (tag.startsWith('copilot_') || tag.startsWith('vscode_'))) && !isProposedApiEnabled(extension.description, 'chatParticipantPrivate')) { logService.error(`Extension '${extension.description.identifier.value}' CANNOT register tool with tags starting with "vscode_" or "copilot_"`); continue; } diff --git a/src/vs/workbench/contrib/chat/common/voiceChatService.ts b/src/vs/workbench/contrib/chat/common/voiceChatService.ts index 71ca69aecb16..bcf91b3a5b5c 100644 --- a/src/vs/workbench/contrib/chat/common/voiceChatService.ts +++ b/src/vs/workbench/contrib/chat/common/voiceChatService.ts @@ -8,7 +8,7 @@ import { CancellationToken } from '../../../../base/common/cancellation.js'; import { Emitter, Event } from '../../../../base/common/event.js'; import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.js'; import { rtrim } from '../../../../base/common/strings.js'; -import { IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; +import { IContextKey, IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; import { IChatAgentService } from './chatAgents.js'; import { IChatModel } from './chatModel.js'; @@ -81,15 +81,17 @@ export class VoiceChatService extends Disposable implements IVoiceChatService { private static readonly CHAT_AGENT_ALIAS = new Map([['vscode', 'code']]); - private readonly voiceChatInProgress = VoiceChatInProgress.bindTo(this.contextKeyService); + private readonly voiceChatInProgress: IContextKey; private activeVoiceChatSessions = 0; constructor( @ISpeechService private readonly speechService: ISpeechService, @IChatAgentService private readonly chatAgentService: IChatAgentService, - @IContextKeyService private readonly contextKeyService: IContextKeyService + @IContextKeyService contextKeyService: IContextKeyService ) { super(); + + this.voiceChatInProgress = VoiceChatInProgress.bindTo(contextKeyService); } private createPhrases(model?: IChatModel): Map { diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts index 9a33d55ae7b8..a55199d97694 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/actions/voiceChatActions.ts @@ -36,7 +36,7 @@ import { AccessibilityVoiceSettingId, SpeechTimeoutDefault, accessibilityConfigu import { CHAT_CATEGORY } from '../../browser/actions/chatActions.js'; import { IChatExecuteActionContext } from '../../browser/actions/chatExecuteActions.js'; import { IChatWidget, IChatWidgetService, IQuickChatService, showChatView } from '../../browser/chat.js'; -import { ChatAgentLocation, IChatAgentService } from '../../common/chatAgents.js'; +import { IChatAgentService } from '../../common/chatAgents.js'; import { ChatContextKeys } from '../../common/chatContextKeys.js'; import { KEYWORD_ACTIVIATION_SETTING_ID } from '../../common/chatService.js'; import { ChatResponseViewModel, IChatResponseViewModel, isResponseVM } from '../../common/chatViewModel.js'; @@ -54,6 +54,7 @@ import { IViewsService } from '../../../../services/views/common/viewsService.js import { IChatResponseModel } from '../../common/chatModel.js'; import { IAccessibilityService } from '../../../../../platform/accessibility/common/accessibility.js'; import { renderStringAsPlaintext } from '../../../../../base/browser/markdownRenderer.js'; +import { ChatAgentLocation } from '../../common/constants.js'; //#region Speech to Text diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/chat.contribution.ts b/src/vs/workbench/contrib/chat/electron-sandbox/chat.contribution.ts index 0e888b7f8644..6c89bb022219 100644 --- a/src/vs/workbench/contrib/chat/electron-sandbox/chat.contribution.ts +++ b/src/vs/workbench/contrib/chat/electron-sandbox/chat.contribution.ts @@ -5,7 +5,27 @@ import { InlineVoiceChatAction, QuickVoiceChatAction, StartVoiceChatAction, VoiceChatInChatViewAction, StopListeningAction, StopListeningAndSubmitAction, KeywordActivationContribution, InstallSpeechProviderForVoiceChatAction, HoldToVoiceChatInChatViewAction, ReadChatResponseAloud, StopReadAloud, StopReadChatItemAloud } from './actions/voiceChatActions.js'; import { registerAction2 } from '../../../../platform/actions/common/actions.js'; -import { WorkbenchPhase, registerWorkbenchContribution2 } from '../../../common/contributions.js'; +import { IWorkbenchContribution, WorkbenchPhase, registerWorkbenchContribution2 } from '../../../common/contributions.js'; +import { Disposable } from '../../../../base/common/lifecycle.js'; +import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; +import { ILanguageModelToolsService } from '../common/languageModelToolsService.js'; +import { FetchWebPageTool, FetchWebPageToolData } from './tools/fetchPageTool.js'; + +class NativeBuiltinToolsContribution extends Disposable implements IWorkbenchContribution { + + static readonly ID = 'chat.nativeBuiltinTools'; + + constructor( + @ILanguageModelToolsService toolsService: ILanguageModelToolsService, + @IInstantiationService instantiationService: IInstantiationService, + ) { + super(); + + const editTool = instantiationService.createInstance(FetchWebPageTool); + this._register(toolsService.registerToolData(FetchWebPageToolData)); + this._register(toolsService.registerToolImplementation(FetchWebPageToolData.id, editTool)); + } +} registerAction2(StartVoiceChatAction); registerAction2(InstallSpeechProviderForVoiceChatAction); @@ -23,3 +43,4 @@ registerAction2(StopReadChatItemAloud); registerAction2(StopReadAloud); registerWorkbenchContribution2(KeywordActivationContribution.ID, KeywordActivationContribution, WorkbenchPhase.AfterRestored); +registerWorkbenchContribution2(NativeBuiltinToolsContribution.ID, NativeBuiltinToolsContribution, WorkbenchPhase.AfterRestored); diff --git a/src/vs/workbench/contrib/chat/electron-sandbox/tools/fetchPageTool.ts b/src/vs/workbench/contrib/chat/electron-sandbox/tools/fetchPageTool.ts new file mode 100644 index 000000000000..30418d3b03dc --- /dev/null +++ b/src/vs/workbench/contrib/chat/electron-sandbox/tools/fetchPageTool.ts @@ -0,0 +1,155 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { localize } from '../../../../../nls.js'; +import { CancellationToken } from '../../../../../base/common/cancellation.js'; +import { URI } from '../../../../../base/common/uri.js'; +import { IWebContentExtractorService } from '../../../../../platform/webContentExtractor/common/webContentExtractor.js'; +import { ITrustedDomainService } from '../../../url/browser/trustedDomainService.js'; +import { CountTokensCallback, IPreparedToolInvocation, IToolData, IToolImpl, IToolInvocation, IToolResult } from '../../common/languageModelToolsService.js'; +import { MarkdownString } from '../../../../../base/common/htmlContent.js'; + +export const InternalFetchWebPageToolId = 'vscode_fetchWebPage_internal'; +export const FetchWebPageToolData: IToolData = { + id: InternalFetchWebPageToolId, + displayName: 'Fetch Web Page', + tags: ['vscode_editing'], + modelDescription: localize('fetchWebPage.modelDescription', 'Fetches the main content from a web page. This tool is useful for summarizing or analyzing the content of a webpage.'), + userDescription: localize('fetchWebPage.userDescription', 'Fetch the main content from a web page. This tool is useful for summarizing or analyzing the content of a webpage.'), + inputSchema: { + type: 'object', + properties: { + urls: { + type: 'array', + items: { + type: 'string', + }, + description: localize('fetchWebPage.urlsDescription', 'An array of URLs to fetch content from.') + } + }, + required: ['urls'] + } +}; + +export class FetchWebPageTool implements IToolImpl { + private _alreadyApprovedDomains = new Set(); + + constructor( + @IWebContentExtractorService private readonly _readerModeService: IWebContentExtractorService, + @ITrustedDomainService private readonly _trustedDomainService: ITrustedDomainService, + ) { } + + async invoke(invocation: IToolInvocation, _countTokens: CountTokensCallback, _token: CancellationToken): Promise { + const { valid } = this._parseUris((invocation.parameters as { urls?: string[] }).urls); + if (!valid.length) { + return { + content: [{ kind: 'text', value: localize('fetchWebPage.noValidUrls', 'No valid URLs provided.') }] + }; + } + + for (const uri of valid) { + if (!this._trustedDomainService.isValid(uri)) { + this._alreadyApprovedDomains.add(uri.toString(true)); + } + } + + const result = await this._readerModeService.extract(valid); + // Right now there's a bug when returning multiple text content parts so we're merging into one. + // When that's fixed we can use the helper function _getPromptPartForWebPageContents. + const value = result.map((content, index) => localize( + 'fetchWebPage.promptPart', + 'Below is the main content extracted from the webpage ({0}). Please read and analyze this content to assist with any follow-up questions:\n\n{1}', + valid[index].toString(), + content + )).join('\n\n---\n\n'); + return { content: [{ kind: 'text', value }] }; + } + + async prepareToolInvocation(parameters: any, token: CancellationToken): Promise { + const { invalid, valid } = this._parseUris(parameters.urls); + const urlsNeedingConfirmation = valid.filter(url => !this._trustedDomainService.isValid(url) && !this._alreadyApprovedDomains.has(url.toString(true))); + + const pastTenseMessage = invalid.length + ? invalid.length > 1 + ? new MarkdownString( + localize( + 'fetchWebPage.pastTenseMessage.plural', + 'Fetched {0} web pages, but the following were invalid URLs:\n\n{1}\n\n', valid.length, invalid.map(url => `- ${url}`).join('\n') + )) + : new MarkdownString( + localize( + 'fetchWebPage.pastTenseMessage.singular', + 'Fetched web page, but the following was an invalid URL:\n\n{0}\n\n', invalid[0] + )) + : new MarkdownString(); + pastTenseMessage.appendMarkdown(valid.length > 1 + ? localize('fetchWebPage.pastTenseMessageResult.plural', 'Fetched {0} web pages', valid.length) + : localize('fetchWebPage.pastTenseMessageResult.singular', 'Fetched [web page]({0})', valid[0].toString()) + ); + + const result: IPreparedToolInvocation = { + invocationMessage: valid.length > 1 + ? new MarkdownString(localize('fetchWebPage.invocationMessage.plural', 'Fetching {0} web pages', valid.length)) + : new MarkdownString(localize('fetchWebPage.invocationMessage.singular', 'Fetching [web page]({0})', valid[0].toString())), + pastTenseMessage + }; + + if (urlsNeedingConfirmation.length) { + const confirmationTitle = urlsNeedingConfirmation.length > 1 + ? localize('fetchWebPage.confirmationTitle.plural', 'Fetch untrusted web pages?') + : localize('fetchWebPage.confirmationTitle.singular', 'Fetch untrusted web page?'); + + const managedTrustedDomainsCommand = 'workbench.action.manageTrustedDomain'; + const confirmationMessage = new MarkdownString( + urlsNeedingConfirmation.length > 1 + ? urlsNeedingConfirmation.map(uri => `- ${uri.toString()}`).join('\n') + : urlsNeedingConfirmation[0].toString(), + { + isTrusted: { enabledCommands: [managedTrustedDomainsCommand] }, + supportThemeIcons: true + } + ); + + confirmationMessage.appendMarkdown( + '\n\n$(info)' + localize( + 'fetchWebPage.confirmationMessageManageTrustedDomains', + 'You can [manage your trusted domains]({0}) to skip this confirmation in the future.', + `command:${managedTrustedDomainsCommand}` + ) + ); + + result.confirmationMessages = { title: confirmationTitle, message: confirmationMessage }; + } + + return result; + } + + private _parseUris(urls?: string[]): { invalid: string[]; valid: URI[] } { + const invalidUrls: string[] = []; + const validUrls: URI[] = []; + urls?.forEach(uri => { + try { + const uriObj = URI.parse(uri); + validUrls.push(uriObj); + } catch (e) { + invalidUrls.push(uri); + } + }); + + return { invalid: invalidUrls, valid: validUrls }; + } + + // private _getPromptPartForWebPageContents(webPageContents: string, uri: URI): IToolResultTextPart { + // return { + // kind: 'text', + // value: localize( + // 'fetchWebPage.promptPart', + // 'Below is the main content extracted from the webpage ({0}). Please read and analyze this content to assist with any follow-up questions:\n\n{1}', + // uri.toString(), + // webPageContents + // ) + // }; + // } +} diff --git a/src/vs/workbench/contrib/chat/test/browser/chatEditingModifiedNotebookEntry.test.ts b/src/vs/workbench/contrib/chat/test/browser/chatEditingModifiedNotebookEntry.test.ts new file mode 100644 index 000000000000..0ac36cbdb452 --- /dev/null +++ b/src/vs/workbench/contrib/chat/test/browser/chatEditingModifiedNotebookEntry.test.ts @@ -0,0 +1,1873 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import assert from 'assert'; +import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; +import { adjustCellDiffAndOriginalModelBasedOnCellAddDelete, adjustCellDiffAndOriginalModelBasedOnCellMovements, adjustCellDiffForKeepingADeletedCell, adjustCellDiffForKeepingAnInsertedCell, adjustCellDiffForRevertingADeletedCell, adjustCellDiffForRevertingAnInsertedCell } from '../../browser/chatEditing/notebook/helpers.js'; +import { ICellDiffInfo } from '../../browser/chatEditing/notebook/notebookCellChanges.js'; +import { nullDocumentDiff } from '../../../../../editor/common/diff/documentDiffProvider.js'; +import { ObservablePromise, observableValue } from '../../../../../base/common/observable.js'; +import { CellEditType, CellKind, ICell, ICellEditOperation, NotebookCellsChangeType } from '../../../notebook/common/notebookCommon.js'; +import { ITextModel } from '../../../../../editor/common/model.js'; +import { URI } from '../../../../../base/common/uri.js'; +import { hash } from '../../../../../base/common/hash.js'; +import { generateUuid } from '../../../../../base/common/uuid.js'; + +suite('ChatEditingModifiedNotebookEntry', function () { + suite('Keep Inserted Cell', function () { + + const keep = () => Promise.resolve(true); + const undo = () => Promise.resolve(true); + const diff = observableValue('cell1', nullDocumentDiff); + const appliedEdits: ICellEditOperation[] = []; + setup(() => { + appliedEdits.length = 0; + }); + ensureNoDisposablesAreLeakedInTestSuite(); + function createModifiedModel(id: string): ObservablePromise { + return `Modified:${id}` as any; + + } + function createOriginalModel(id: string): ObservablePromise { + return `Original:${id}` as any; + + } + function applyEdits(edits: ICellEditOperation[]): boolean { + appliedEdits.push(...edits); + return true; + } + + function createModifiedCellDiffInfo(modifiedCellIndex: number, originalCellIndex: number): ICellDiffInfo { + return { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel(`InsertedOriginal:${originalCellIndex}`), originalCellIndex, + modifiedCellIndex, modifiedModel: createModifiedModel(`InsertedModified:${modifiedCellIndex}`), + }; + } + test('Keep first inserted', async function () { + const cellsDiffInfo: ICellDiffInfo[] = [ + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('New0'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('0'), + }, + ]; + + const result = adjustCellDiffForKeepingAnInsertedCell(0, + cellsDiffInfo, {} as any, + applyEdits, createModifiedCellDiffInfo); + + assert.deepStrictEqual(appliedEdits, [ + { editType: CellEditType.Replace, index: 0, cells: [{}], count: 0 }, + ]); + assert.deepStrictEqual(result, [ + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel(`InsertedOriginal:0`), originalCellIndex: 0, + modifiedCellIndex: 0, modifiedModel: createModifiedModel(`InsertedModified:0`), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 1, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('0'), + }, + ]); + }); + test('Keep first inserted with multiple cells', async function () { + const cellsDiffInfo: ICellDiffInfo[] = [ + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('New0'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('0'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('1'), originalCellIndex: 1, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 2, modifiedModel: createModifiedModel('1'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('2'), originalCellIndex: 2, + modifiedCellIndex: 3, modifiedModel: createModifiedModel('2'), + }, + ]; + + const result = adjustCellDiffForKeepingAnInsertedCell(0, + cellsDiffInfo, {} as any, + applyEdits, createModifiedCellDiffInfo); + + assert.deepStrictEqual(appliedEdits, [ + { editType: CellEditType.Replace, index: 0, cells: [{}], count: 0 }, + ]); + assert.deepStrictEqual(result, [ + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('InsertedOriginal:0'), originalCellIndex: 0, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('InsertedModified:0'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 1, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('0'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('1'), originalCellIndex: 2, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 2, modifiedModel: createModifiedModel('1'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('2'), originalCellIndex: 3, + modifiedCellIndex: 3, modifiedModel: createModifiedModel('2'), + }, + ]); + }); + test('Keep second inserted with multiple cells', async function () { + const cellsDiffInfo: ICellDiffInfo[] = [ + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('New0'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('0'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('1'), originalCellIndex: 1, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 2, modifiedModel: createModifiedModel('1'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('2'), originalCellIndex: 2, + modifiedCellIndex: 3, modifiedModel: createModifiedModel('2'), + }, + ]; + + const result = adjustCellDiffForKeepingAnInsertedCell(2, + cellsDiffInfo, {} as any, + applyEdits, createModifiedCellDiffInfo); + + assert.deepStrictEqual(appliedEdits, [ + { editType: CellEditType.Replace, index: 2, cells: [{}], count: 0 }, + ]); + assert.deepStrictEqual(result, [ + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('New0'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('0'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('1'), originalCellIndex: 1, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('InsertedOriginal:2'), originalCellIndex: 2, + modifiedCellIndex: 2, modifiedModel: createModifiedModel('InsertedModified:2'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('2'), originalCellIndex: 3, + modifiedCellIndex: 3, modifiedModel: createModifiedModel('2'), + }, + ]); + }); + }); + + suite('Revert Inserted Cell', function () { + + const keep = () => Promise.resolve(true); + const undo = () => Promise.resolve(true); + const diff = observableValue('cell1', nullDocumentDiff); + const appliedEdits: ICellEditOperation[] = []; + setup(() => { + appliedEdits.length = 0; + }); + ensureNoDisposablesAreLeakedInTestSuite(); + function createModifiedModel(id: string): ObservablePromise { + return `Modified:${id}` as any; + + } + function createOriginalModel(id: string): ObservablePromise { + return `Original:${id}` as any; + + } + function applyEdits(edits: ICellEditOperation[]): boolean { + appliedEdits.push(...edits); + return true; + } + + test('Delete first inserted', async function () { + const cellsDiffInfo: ICellDiffInfo[] = [ + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('New0'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('0'), + }, + ]; + + const result = adjustCellDiffForRevertingAnInsertedCell(0, + cellsDiffInfo, + applyEdits); + + assert.deepStrictEqual(appliedEdits, [ + { editType: CellEditType.Replace, index: 0, cells: [], count: 1 }, + ]); + assert.deepStrictEqual(result, [ + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('0'), + }, + ]); + }); + test('Delete first inserted with multiple cells', async function () { + const cellsDiffInfo: ICellDiffInfo[] = [ + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('New0'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('0'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('1'), originalCellIndex: 1, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 2, modifiedModel: createModifiedModel('1'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('2'), originalCellIndex: 2, + modifiedCellIndex: 3, modifiedModel: createModifiedModel('2'), + }, + ]; + + const result = adjustCellDiffForRevertingAnInsertedCell(0, + cellsDiffInfo, + applyEdits); + + assert.deepStrictEqual(appliedEdits, [ + { editType: CellEditType.Replace, index: 0, cells: [], count: 1 }, + ]); + assert.deepStrictEqual(result, [ + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('0'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('1'), originalCellIndex: 1, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('1'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('2'), originalCellIndex: 2, + modifiedCellIndex: 2, modifiedModel: createModifiedModel('2'), + }, + ]); + }); + test('Delete second inserted with multiple cells', async function () { + const cellsDiffInfo: ICellDiffInfo[] = [ + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('New0'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('0'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('1'), originalCellIndex: 1, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 2, modifiedModel: createModifiedModel('1'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('2'), originalCellIndex: 2, + modifiedCellIndex: 3, modifiedModel: createModifiedModel('2'), + }, + ]; + + const result = adjustCellDiffForRevertingAnInsertedCell(2, + cellsDiffInfo, + applyEdits); + + assert.deepStrictEqual(appliedEdits, [ + { editType: CellEditType.Replace, index: 2, cells: [], count: 1 }, + ]); + assert.deepStrictEqual(result, [ + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('New0'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('0'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('1'), originalCellIndex: 1, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('2'), originalCellIndex: 2, + modifiedCellIndex: 2, modifiedModel: createModifiedModel('2'), + }, + ]); + }); + test('Delete second inserted with multiple cells (subsequent inserts)', async function () { + const cellsDiffInfo: ICellDiffInfo[] = [ + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('New0'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('0'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('1'), originalCellIndex: 1, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 2, modifiedModel: createModifiedModel('1'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 3, modifiedModel: createModifiedModel('3'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('2'), originalCellIndex: 2, + modifiedCellIndex: 4, modifiedModel: createModifiedModel('4'), + }, + ]; + + const result = adjustCellDiffForRevertingAnInsertedCell(2, + cellsDiffInfo, + applyEdits); + + assert.deepStrictEqual(appliedEdits, [ + { editType: CellEditType.Replace, index: 2, cells: [], count: 1 }, + ]); + assert.deepStrictEqual(result, [ + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('New0'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('0'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('1'), originalCellIndex: 1, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 2, modifiedModel: createModifiedModel('3'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('2'), originalCellIndex: 2, + modifiedCellIndex: 3, modifiedModel: createModifiedModel('4'), + }, + ]); + }); + }); + + suite('Keep Deleted Cell', function () { + + const keep = () => Promise.resolve(true); + const undo = () => Promise.resolve(true); + const diff = observableValue('cell1', nullDocumentDiff); + const appliedEdits: ICellEditOperation[] = []; + setup(() => { + appliedEdits.length = 0; + }); + ensureNoDisposablesAreLeakedInTestSuite(); + function createModifiedModel(id: string): ObservablePromise { + return `Modified:${id}` as any; + + } + function createOriginalModel(id: string): ObservablePromise { + return `Original:${id}` as any; + + } + function applyEdits(edits: ICellEditOperation[]): boolean { + appliedEdits.push(...edits); + return true; + } + + test('Keep first deleted cell', async function () { + const cellsDiffInfo: ICellDiffInfo[] = [ + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('1'), originalCellIndex: 1, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('New0'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('2'), originalCellIndex: 2, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('0'), + }, + ]; + + const result = adjustCellDiffForKeepingADeletedCell(0, + cellsDiffInfo, + applyEdits); + + assert.deepStrictEqual(appliedEdits, [ + { editType: CellEditType.Replace, index: 0, cells: [], count: 1 }, + ]); + assert.deepStrictEqual(result, [ + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('1'), originalCellIndex: 0, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('New0'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('2'), originalCellIndex: 1, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('0'), + }, + ]); + }); + test('Keep second deleted cell', async function () { + const cellsDiffInfo: ICellDiffInfo[] = [ + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('1'), originalCellIndex: 1, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('New0'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('2'), originalCellIndex: 2, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('0'), + }, + ]; + + const result = adjustCellDiffForKeepingADeletedCell(1, + cellsDiffInfo, + applyEdits); + + assert.deepStrictEqual(appliedEdits, [ + { editType: CellEditType.Replace, index: 1, cells: [], count: 1 }, + ]); + assert.deepStrictEqual(result, [ + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('New0'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('2'), originalCellIndex: 1, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('0'), + }, + ]); + }); + + test('Keep first deleted with multiple cells', async function () { + const cellsDiffInfo: ICellDiffInfo[] = [ + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('New0'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('0'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('1'), originalCellIndex: 1, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 2, modifiedModel: createModifiedModel('1'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('2'), originalCellIndex: 2, + modifiedCellIndex: 3, modifiedModel: createModifiedModel('2'), + }, + ]; + + const result = adjustCellDiffForKeepingADeletedCell(1, + cellsDiffInfo, + applyEdits); + + assert.deepStrictEqual(appliedEdits, [ + { editType: CellEditType.Replace, index: 1, cells: [], count: 1 }, + ]); + assert.deepStrictEqual(result, [ + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('New0'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('0'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 2, modifiedModel: createModifiedModel('1'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('2'), originalCellIndex: 1, + modifiedCellIndex: 3, modifiedModel: createModifiedModel('2'), + }, + ]); + }); + }); + + suite('Revert Deleted Cell', function () { + + const keep = () => Promise.resolve(true); + const undo = () => Promise.resolve(true); + const diff = observableValue('cell1', nullDocumentDiff); + const appliedEdits: ICellEditOperation[] = []; + setup(() => { + appliedEdits.length = 0; + }); + ensureNoDisposablesAreLeakedInTestSuite(); + function createModifiedModel(id: string): ObservablePromise { + return `Modified:${id}` as any; + + } + function createOriginalModel(id: string): ObservablePromise { + return `Original:${id}` as any; + + } + function applyEdits(edits: ICellEditOperation[]): boolean { + appliedEdits.push(...edits); + return true; + } + function createModifiedCellDiffInfo(modifiedCellIndex: number, originalCellIndex: number): ICellDiffInfo { + return { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel(`InsertedOriginal:${originalCellIndex}`), originalCellIndex, + modifiedCellIndex, modifiedModel: createModifiedModel(`InsertedModified:${modifiedCellIndex}`), + }; + } + + test('Revert first deleted cell', async function () { + const cellsDiffInfo: ICellDiffInfo[] = [ + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('1'), originalCellIndex: 1, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('New0'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('2'), originalCellIndex: 2, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('0'), + }, + ]; + + const result = adjustCellDiffForRevertingADeletedCell(0, + cellsDiffInfo, + {} as any, + applyEdits, + createModifiedCellDiffInfo); + + assert.deepStrictEqual(appliedEdits, [ + { editType: CellEditType.Replace, index: 0, cells: [{}], count: 0 }, + ]); + assert.deepStrictEqual(result, [ + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('InsertedOriginal:0'), originalCellIndex: 0, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('InsertedModified:0'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('1'), originalCellIndex: 1, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('New0'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('2'), originalCellIndex: 2, + modifiedCellIndex: 2, modifiedModel: createModifiedModel('0'), + }, + ]); + }); + test('Revert second deleted cell', async function () { + const cellsDiffInfo: ICellDiffInfo[] = [ + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('1'), originalCellIndex: 1, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('New0'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('2'), originalCellIndex: 2, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('0'), + }, + ]; + + const result = adjustCellDiffForRevertingADeletedCell(1, + cellsDiffInfo, + {} as any, + applyEdits, + createModifiedCellDiffInfo); + + assert.deepStrictEqual(appliedEdits, [ + { editType: CellEditType.Replace, index: 0, cells: [{}], count: 0 }, + ]); + assert.deepStrictEqual(result, [ + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('InsertedOriginal:1'), originalCellIndex: 1, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('InsertedModified:0'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('New0'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('2'), originalCellIndex: 2, + modifiedCellIndex: 2, modifiedModel: createModifiedModel('0'), + }, + ]); + }); + + test('Revert first deleted with multiple cells', async function () { + const cellsDiffInfo: ICellDiffInfo[] = [ + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('New0'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('New1'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: 2, modifiedModel: createModifiedModel('0'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('1'), originalCellIndex: 1, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 3, modifiedModel: createModifiedModel('1'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('2'), originalCellIndex: 2, + modifiedCellIndex: 4, modifiedModel: createModifiedModel('2'), + }, + ]; + + const result = adjustCellDiffForRevertingADeletedCell(1, + cellsDiffInfo, + {} as any, + applyEdits, + createModifiedCellDiffInfo); + + assert.deepStrictEqual(appliedEdits, [ + { editType: CellEditType.Replace, index: 3, cells: [{}], count: 0 }, + ]); + assert.deepStrictEqual(result, [ + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('New0'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('New1'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: 2, modifiedModel: createModifiedModel('0'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('InsertedOriginal:1'), originalCellIndex: 1, + modifiedCellIndex: 3, modifiedModel: createModifiedModel('InsertedModified:3'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 4, modifiedModel: createModifiedModel('1'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('2'), originalCellIndex: 2, + modifiedCellIndex: 5, modifiedModel: createModifiedModel('2'), + }, + ]); + }); + }); + + suite('Cell Addition', function () { + + const keep = () => Promise.resolve(true); + const undo = () => Promise.resolve(true); + const diff = observableValue('cell1', nullDocumentDiff); + const appliedEdits: ICellEditOperation[] = []; + setup(() => { + appliedEdits.length = 0; + }); + ensureNoDisposablesAreLeakedInTestSuite(); + function createModifiedModel(id: string): ObservablePromise { + return `Modified:${id}` as any; + + } + function createOriginalModel(id: string): ObservablePromise { + return `Original:${id}` as any; + + } + function applyEdits(edits: ICellEditOperation[]): boolean { + appliedEdits.push(...edits); + return true; + } + + function createICell(cellKind: CellKind, source: string): ICell { + const handle = hash(generateUuid()); + return { + uri: URI.parse(`file:///path/${handle}`), + handle, + cellKind, + language: cellKind === CellKind.Markup ? 'markdown' : 'python', + outputs: [], + metadata: {}, + getHashValue: () => { + return hash(`${handle}=>${cellKind}=>${source}`); + }, + getValue: () => { + return source; + }, + internalMetadata: {}, + } as any; + } + function createModifiedCellDiffInfo(modifiedCellIndex: number, originalCellIndex: number): ICellDiffInfo { + return { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel(`InsertedOriginal:${originalCellIndex}`), originalCellIndex, + modifiedCellIndex, modifiedModel: createModifiedModel(`InsertedModified:${modifiedCellIndex}`), + }; + } + test('Insert a new cell into an unchanged notebook', async function () { + const cellsDiffInfo: ICellDiffInfo[] = [ + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('0'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('1'), originalCellIndex: 1, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('1'), + }, + ]; + + const cell = createICell(CellKind.Code, 'print("Hello World")'); + const result = adjustCellDiffAndOriginalModelBasedOnCellAddDelete([0, 0, [cell]], + cellsDiffInfo, 2, 2, applyEdits, createModifiedCellDiffInfo); + assert.deepStrictEqual(appliedEdits, [ + { + editType: CellEditType.Replace, + index: 0, + cells: [{ + cellKind: CellKind.Code, + language: 'python', + outputs: [], + mime: undefined, + metadata: {}, + internalMetadata: {}, + source: cell.getValue(), + }], count: 0 + } + ]); + assert.deepStrictEqual(result, [ + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel(`InsertedOriginal:0`), originalCellIndex: 0, + modifiedCellIndex: 0, modifiedModel: createModifiedModel(`InsertedModified:0`), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 1, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('0'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('1'), originalCellIndex: 2, + modifiedCellIndex: 2, modifiedModel: createModifiedModel('1'), + }, + ]); + }); + test('Insert a new cell into an notebook with 3 cells deleted', async function () { + const cellsDiffInfo: ICellDiffInfo[] = [ + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('0'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('1'), originalCellIndex: 1, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('2'), originalCellIndex: 2, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('3'), originalCellIndex: 3, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('New1'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('4'), originalCellIndex: 4, + modifiedCellIndex: 2, modifiedModel: createModifiedModel('4'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('5'), originalCellIndex: 5, + modifiedCellIndex: 3, modifiedModel: createModifiedModel('5'), + }, + { + diff, keep, undo, type: 'modified', originalModel: createOriginalModel('6'), originalCellIndex: 6, + modifiedCellIndex: 4, modifiedModel: createModifiedModel('6'), + }, + ]; + const cell = createICell(CellKind.Code, 'print("Hello World")'); + const result = adjustCellDiffAndOriginalModelBasedOnCellAddDelete([2, 0, [cell]], + cellsDiffInfo, 5, 7, applyEdits, createModifiedCellDiffInfo); + + assert.deepStrictEqual(appliedEdits, [ + { + editType: CellEditType.Replace, + index: 4, + cells: [{ + cellKind: CellKind.Code, + language: 'python', + outputs: [], + mime: undefined, + metadata: {}, + internalMetadata: {}, + source: cell.getValue(), + }], count: 0 + } + ]); + + assert.deepStrictEqual(result, [ + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('0'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('1'), originalCellIndex: 1, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('2'), originalCellIndex: 2, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('3'), originalCellIndex: 3, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('New1'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('InsertedOriginal:4'), originalCellIndex: 4, + modifiedCellIndex: 2, modifiedModel: createModifiedModel('InsertedModified:2'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('4'), originalCellIndex: 5, + modifiedCellIndex: 3, modifiedModel: createModifiedModel('4'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('5'), originalCellIndex: 6, + modifiedCellIndex: 4, modifiedModel: createModifiedModel('5'), + }, + { + diff, keep, undo, type: 'modified', originalModel: createOriginalModel('6'), originalCellIndex: 7, + modifiedCellIndex: 5, modifiedModel: createModifiedModel('6'), + }, + ]); + }); + test('Insert 2 new cells into an notebook with 3 cells deleted', async function () { + const cellsDiffInfo: ICellDiffInfo[] = [ + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('0'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('2'), originalCellIndex: 1, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('3'), originalCellIndex: 2, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('4'), originalCellIndex: 3, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('New1'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('5'), originalCellIndex: 4, + modifiedCellIndex: 2, modifiedModel: createModifiedModel('5'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('1'), originalCellIndex: 5, + modifiedCellIndex: 3, modifiedModel: createModifiedModel('1'), + }, + ]; + const cell1 = createICell(CellKind.Code, 'print("Hello World")'); + const cell2 = createICell(CellKind.Code, 'print("Foo Bar")'); + const result = adjustCellDiffAndOriginalModelBasedOnCellAddDelete([2, 0, [cell1, cell2]], + cellsDiffInfo, 4, 6, applyEdits, createModifiedCellDiffInfo); + + assert.deepStrictEqual(appliedEdits, [ + { + editType: CellEditType.Replace, + index: 4, + cells: [{ + cellKind: CellKind.Code, + language: 'python', + outputs: [], + mime: undefined, + metadata: {}, + internalMetadata: {}, + source: cell1.getValue(), + }, { + cellKind: CellKind.Code, + language: 'python', + outputs: [], + mime: undefined, + metadata: {}, + internalMetadata: {}, + source: cell2.getValue(), + }], count: 0 + } + ]); + + assert.deepStrictEqual(result, [ + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('0'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('2'), originalCellIndex: 1, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('3'), originalCellIndex: 2, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('4'), originalCellIndex: 3, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('New1'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel(`InsertedOriginal:4`), originalCellIndex: 4, + modifiedCellIndex: 2, modifiedModel: createModifiedModel(`InsertedModified:2`), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel(`InsertedOriginal:5`), originalCellIndex: 5, + modifiedCellIndex: 3, modifiedModel: createModifiedModel(`InsertedModified:3`), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('5'), originalCellIndex: 6, + modifiedCellIndex: 4, modifiedModel: createModifiedModel('5'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('1'), originalCellIndex: 7, + modifiedCellIndex: 5, modifiedModel: createModifiedModel('1'), + }, + ]); + }); + test('Delete a cell from an unchanged notebook', async function () { + const cellsDiffInfo: ICellDiffInfo[] = [ + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('0'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('1'), originalCellIndex: 1, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('1'), + }, + ]; + + const result = adjustCellDiffAndOriginalModelBasedOnCellAddDelete([0, 1, []], + cellsDiffInfo, 2, 2, applyEdits, createModifiedCellDiffInfo); + + assert.deepStrictEqual(appliedEdits, [ + { + editType: CellEditType.Replace, + index: 0, + cells: [], count: 1 + } + ]); + + assert.deepStrictEqual(result, [ + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('1'), originalCellIndex: 0, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('1'), + }, + ]); + }); + test('Delete last cell from an unchanged notebook', async function () { + const cellsDiffInfo: ICellDiffInfo[] = [ + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('0'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('1'), originalCellIndex: 1, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('1'), + }, + ]; + + const result = adjustCellDiffAndOriginalModelBasedOnCellAddDelete([1, 1, []], + cellsDiffInfo, 2, 2, applyEdits, createModifiedCellDiffInfo); + assert.deepStrictEqual(appliedEdits, [ + { + editType: CellEditType.Replace, + index: 1, + cells: [], count: 1 + } + ]); + + assert.deepStrictEqual(result, [ + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('0'), + }, + ]); + }); + test('Delete a new cell from a notebook with 3 cells deleted', async function () { + const cellsDiffInfo: ICellDiffInfo[] = [ + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('0'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('2'), originalCellIndex: 1, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('3'), originalCellIndex: 2, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('4'), originalCellIndex: 3, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('New1'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('5'), originalCellIndex: 4, + modifiedCellIndex: 2, modifiedModel: createModifiedModel('5'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('1'), originalCellIndex: 5, + modifiedCellIndex: 3, modifiedModel: createModifiedModel('1'), + }, + ]; + + const result = adjustCellDiffAndOriginalModelBasedOnCellAddDelete([1, 1, [ + // createICell(CellKind.Code, 'print("Hello World")') + ]], + cellsDiffInfo, 4, 6, applyEdits, createModifiedCellDiffInfo); + + assert.deepStrictEqual(appliedEdits, [ + ]); + + assert.deepStrictEqual(result, [ + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('0'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('2'), originalCellIndex: 1, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('3'), originalCellIndex: 2, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('4'), originalCellIndex: 3, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('5'), originalCellIndex: 4, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('5'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('1'), originalCellIndex: 5, + modifiedCellIndex: 2, modifiedModel: createModifiedModel('1'), + }, + ]); + }); + test('Delete 2 cells from a notebook with 3 cells deleted', async function () { + const cellsDiffInfo: ICellDiffInfo[] = [ + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('0'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('2'), originalCellIndex: 1, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('3'), originalCellIndex: 2, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('4'), originalCellIndex: 3, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('New1'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('5'), originalCellIndex: 4, + modifiedCellIndex: 2, modifiedModel: createModifiedModel('5'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('1'), originalCellIndex: 5, + modifiedCellIndex: 3, modifiedModel: createModifiedModel('1'), + }, + ]; + + const result = adjustCellDiffAndOriginalModelBasedOnCellAddDelete([1, 2, [ + ]], + cellsDiffInfo, 4, 6, applyEdits, createModifiedCellDiffInfo); + + assert.deepStrictEqual(appliedEdits, [ + { + editType: CellEditType.Replace, + index: 4, + cells: [], count: 1 + } + ]); + + assert.deepStrictEqual(result, [ + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('0'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('2'), originalCellIndex: 1, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('3'), originalCellIndex: 2, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('4'), originalCellIndex: 3, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('1'), originalCellIndex: 4, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('1'), + }, + ]); + }); + test('Delete 3 cells from a notebook with 3 cells deleted', async function () { + const cellsDiffInfo: ICellDiffInfo[] = [ + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('0'), + }, + { + diff, keep, undo, type: 'modified', originalModel: createOriginalModel('1'), originalCellIndex: 1, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('1'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('2'), originalCellIndex: 2, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('3'), originalCellIndex: 3, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('4'), originalCellIndex: 4, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 2, modifiedModel: createModifiedModel('New1'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('5'), originalCellIndex: 5, + modifiedCellIndex: 3, modifiedModel: createModifiedModel('5'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('6'), originalCellIndex: 6, + modifiedCellIndex: 4, modifiedModel: createModifiedModel('6'), + }, + ]; + + const result = adjustCellDiffAndOriginalModelBasedOnCellAddDelete([1, 3, [ + ]], + cellsDiffInfo, 5, 7, applyEdits, createModifiedCellDiffInfo); + + assert.deepStrictEqual(appliedEdits, [ + { + editType: CellEditType.Replace, + index: 1, + cells: [], count: 1 + }, + { + editType: CellEditType.Replace, + index: 5, + cells: [], count: 1 + } + ]); + + assert.deepStrictEqual(result, [ + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('0'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('2'), originalCellIndex: 1, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('3'), originalCellIndex: 2, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('4'), originalCellIndex: 3, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('6'), originalCellIndex: 4, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('6'), + }, + ]); + }); + }); + + suite('Cell Movements', function () { + + const keep = () => Promise.resolve(true); + const undo = () => Promise.resolve(true); + const diff = observableValue('cell1', nullDocumentDiff); + + ensureNoDisposablesAreLeakedInTestSuite(); + function createModifiedModel(id: string): ObservablePromise { + return `Modified:${id}` as any; + + } + function createOriginalModel(id: string): ObservablePromise { + return `Original:${id}` as any; + + } + test('Swap first two inserted cells in a previously empty notebook', async function () { + const cellsDiffInfo: ICellDiffInfo[] = [ + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('0'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('1'), + } + ]; + const result = adjustCellDiffAndOriginalModelBasedOnCellMovements({ + cells: [], kind: NotebookCellsChangeType.Move, + index: 0, length: 1, newIdx: 1 + }, cellsDiffInfo); + + assert.ok(result); + assert.strictEqual(result[1].length, 0); + assert.deepStrictEqual(result[0], [ + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('1'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('0'), + }, + ]); + }); + test('Swap first two inserted cells in a notebook that had 2 cells', async function () { + const cellsDiffInfo: ICellDiffInfo[] = [ + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('0'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('1'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: 2, modifiedModel: createModifiedModel('2'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('1'), originalCellIndex: 1, + modifiedCellIndex: 3, modifiedModel: createModifiedModel('3'), + } + ]; + const result = adjustCellDiffAndOriginalModelBasedOnCellMovements({ + cells: [], kind: NotebookCellsChangeType.Move, + index: 0, length: 1, newIdx: 1 + }, cellsDiffInfo); + + assert.ok(result); + assert.strictEqual(result[1].length, 0); + assert.deepStrictEqual(result[0], [ + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('1'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('0'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: 2, modifiedModel: createModifiedModel('2'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('1'), originalCellIndex: 1, + modifiedCellIndex: 3, modifiedModel: createModifiedModel('3'), + } + ]); + }); + test('Move first inserted cell to the very bottom of notebook that had 2 cells', async function () { + const cellsDiffInfo: ICellDiffInfo[] = [ + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('0'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('1'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: 2, modifiedModel: createModifiedModel('2'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('1'), originalCellIndex: 1, + modifiedCellIndex: 3, modifiedModel: createModifiedModel('3'), + } + ]; + const result = adjustCellDiffAndOriginalModelBasedOnCellMovements({ + cells: [], kind: NotebookCellsChangeType.Move, + index: 0, length: 1, newIdx: 3 + }, cellsDiffInfo); + + assert.ok(result); + assert.strictEqual(result[1].length, 0); + assert.deepStrictEqual(result[0], [ + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('1'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('2'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('1'), originalCellIndex: 1, + modifiedCellIndex: 2, modifiedModel: createModifiedModel('3'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 3, modifiedModel: createModifiedModel('0'), + }, + ]); + }); + test('Move last cell to top of notebook after 2 cells were inserted', async function () { + const cellsDiffInfo: ICellDiffInfo[] = [ + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('0'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('1'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: 2, modifiedModel: createModifiedModel('2'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('1'), originalCellIndex: 1, + modifiedCellIndex: 3, modifiedModel: createModifiedModel('3'), + } + ]; + const result = adjustCellDiffAndOriginalModelBasedOnCellMovements({ + cells: [], kind: NotebookCellsChangeType.Move, + index: 3, length: 1, newIdx: 0 + }, cellsDiffInfo); + + assert.ok(result); + assert.deepStrictEqual(result[1], [ + { + editType: CellEditType.Move, + index: 1, + length: 1, + newIdx: 0 + } + ]); + assert.deepStrictEqual(result[0], [ + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('1'), originalCellIndex: 0, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('3'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('0'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 2, modifiedModel: createModifiedModel('1'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 1, + modifiedCellIndex: 3, modifiedModel: createModifiedModel('2'), + }, + ]); + }); + + test('Move second inserted cell to the very bottom of notebook that had 2 cells', async function () { + const cellsDiffInfo: ICellDiffInfo[] = [ + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('0'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('1'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: 2, modifiedModel: createModifiedModel('2'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('1'), originalCellIndex: 1, + modifiedCellIndex: 3, modifiedModel: createModifiedModel('3'), + } + ]; + const result = adjustCellDiffAndOriginalModelBasedOnCellMovements({ + cells: [], kind: NotebookCellsChangeType.Move, + index: 1, length: 1, newIdx: 3 + }, cellsDiffInfo); + + assert.ok(result); + assert.strictEqual(result[1].length, 0); + assert.deepStrictEqual(result[0], [ + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('0'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('2'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('1'), originalCellIndex: 1, + modifiedCellIndex: 2, modifiedModel: createModifiedModel('3'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 3, modifiedModel: createModifiedModel('1'), + }, + ]); + }); + test('Move second inserted cell to the second last position of notebook that had 2 cells', async function () { + const cellsDiffInfo: ICellDiffInfo[] = [ + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('0'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('1'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: 2, modifiedModel: createModifiedModel('2'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('1'), originalCellIndex: 1, + modifiedCellIndex: 3, modifiedModel: createModifiedModel('3'), + } + ]; + const result = adjustCellDiffAndOriginalModelBasedOnCellMovements({ + cells: [], kind: NotebookCellsChangeType.Move, + index: 1, length: 1, newIdx: 2 + }, cellsDiffInfo); + + assert.ok(result); + assert.strictEqual(result[1].length, 0); + assert.deepStrictEqual(result[0], [ + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('0'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('2'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 2, modifiedModel: createModifiedModel('1'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('1'), originalCellIndex: 1, + modifiedCellIndex: 3, modifiedModel: createModifiedModel('3'), + } + ]); + }); + test('Move first cell to the last position of notebook that had 3 cells deleted from the middle', async function () { + const cellsDiffInfo: ICellDiffInfo[] = [ + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('0'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('1'), originalCellIndex: 1, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('1'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('2'), originalCellIndex: 2, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('3'), originalCellIndex: 3, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('4'), originalCellIndex: 4, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('5'), originalCellIndex: 5, + modifiedCellIndex: 2, modifiedModel: createModifiedModel('2'), + }, + ]; + const result = adjustCellDiffAndOriginalModelBasedOnCellMovements({ + cells: [], kind: NotebookCellsChangeType.Move, + index: 0, length: 1, newIdx: 2 + }, cellsDiffInfo); + + assert.ok(result); + assert.deepStrictEqual(result[1], [ + { + editType: CellEditType.Move, + index: 0, + length: 1, + newIdx: 5 + } + ]); + assert.deepStrictEqual(result[0], [ + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('1'), originalCellIndex: 0, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('1'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('2'), originalCellIndex: 1, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('3'), originalCellIndex: 2, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('4'), originalCellIndex: 3, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('5'), originalCellIndex: 4, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('2'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 5, + modifiedCellIndex: 2, modifiedModel: createModifiedModel('0'), + }, + ]); + }); + test('Move second cell to the last position of notebook that had 3 cells deleted from the middle', async function () { + const cellsDiffInfo: ICellDiffInfo[] = [ + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('0'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('1'), originalCellIndex: 1, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('1'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('2'), originalCellIndex: 2, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('3'), originalCellIndex: 3, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('4'), originalCellIndex: 4, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('5'), originalCellIndex: 5, + modifiedCellIndex: 2, modifiedModel: createModifiedModel('2'), + }, + ]; + const result = adjustCellDiffAndOriginalModelBasedOnCellMovements({ + cells: [], kind: NotebookCellsChangeType.Move, + index: 1, length: 1, newIdx: 2 + }, cellsDiffInfo); + + assert.ok(result); + assert.deepStrictEqual(result[1], [ + { + editType: CellEditType.Move, + index: 1, + length: 1, + newIdx: 5 + } + ]); + assert.deepStrictEqual(result[0], [ + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('0'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('2'), originalCellIndex: 1, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('3'), originalCellIndex: 2, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('4'), originalCellIndex: 3, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('5'), originalCellIndex: 4, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('2'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('1'), originalCellIndex: 5, + modifiedCellIndex: 2, modifiedModel: createModifiedModel('1'), + }, + ]); + }); + + test('Move second cell to the last position of notebook that had 3 cells deleted from middle and 1 inserted in the middle', async function () { + const cellsDiffInfo: ICellDiffInfo[] = [ + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('0'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('1'), originalCellIndex: 1, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('1'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('2'), originalCellIndex: 2, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('3'), originalCellIndex: 3, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('4'), originalCellIndex: 4, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 2, modifiedModel: createModifiedModel('New1'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('5'), originalCellIndex: 5, + modifiedCellIndex: 3, modifiedModel: createModifiedModel('5'), + }, + ]; + const result = adjustCellDiffAndOriginalModelBasedOnCellMovements({ + cells: [], kind: NotebookCellsChangeType.Move, + index: 1, length: 1, newIdx: 3 + }, cellsDiffInfo); + + assert.ok(result); + assert.deepStrictEqual(result[1], [ + { + editType: CellEditType.Move, + index: 1, + length: 1, + newIdx: 5 + } + ]); + assert.deepStrictEqual(result[0], [ + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('0'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('2'), originalCellIndex: 1, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('3'), originalCellIndex: 2, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('4'), originalCellIndex: 3, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('New1'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('5'), originalCellIndex: 4, + modifiedCellIndex: 2, modifiedModel: createModifiedModel('5'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('1'), originalCellIndex: 5, + modifiedCellIndex: 3, modifiedModel: createModifiedModel('1'), + }, + ]); + }); + test('Move last cell to the second position of notebook that had 3 cells deleted from middle and 1 inserted in the middle', async function () { + const cellsDiffInfo: ICellDiffInfo[] = [ + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('0'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('1'), originalCellIndex: 1, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('1'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('2'), originalCellIndex: 2, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('3'), originalCellIndex: 3, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('4'), originalCellIndex: 4, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 2, modifiedModel: createModifiedModel('New1'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('5'), originalCellIndex: 5, + modifiedCellIndex: 3, modifiedModel: createModifiedModel('5'), + }, + ]; + const result = adjustCellDiffAndOriginalModelBasedOnCellMovements({ + cells: [], kind: NotebookCellsChangeType.Move, + index: 3, length: 1, newIdx: 1 + }, cellsDiffInfo); + + assert.ok(result); + assert.deepStrictEqual(result[1], [ + { + editType: CellEditType.Move, + index: 5, + length: 1, + newIdx: 1 + } + ]); + assert.deepStrictEqual(result[0], [ + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('0'), originalCellIndex: 0, + modifiedCellIndex: 0, modifiedModel: createModifiedModel('0'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('5'), originalCellIndex: 1, + modifiedCellIndex: 1, modifiedModel: createModifiedModel('5'), + }, + { + diff, keep, undo, type: 'unchanged', originalModel: createOriginalModel('1'), originalCellIndex: 2, + modifiedCellIndex: 2, modifiedModel: createModifiedModel('1'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('2'), originalCellIndex: 3, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('3'), originalCellIndex: 4, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'delete', originalModel: createOriginalModel('4'), originalCellIndex: 5, + modifiedCellIndex: undefined, modifiedModel: createModifiedModel('null'), + }, + { + diff, keep, undo, type: 'insert', originalModel: createOriginalModel('null'), originalCellIndex: undefined, + modifiedCellIndex: 3, modifiedModel: createModifiedModel('New1'), + }, + ]); + }); + }); + +}); diff --git a/src/vs/workbench/contrib/chat/test/browser/chatEditingService.test.ts b/src/vs/workbench/contrib/chat/test/browser/chatEditingService.test.ts index 974e7f0f5fd1..5fed1eb581a6 100644 --- a/src/vs/workbench/contrib/chat/test/browser/chatEditingService.test.ts +++ b/src/vs/workbench/contrib/chat/test/browser/chatEditingService.test.ts @@ -17,12 +17,21 @@ import { IChatEditingService } from '../../common/chatEditingService.js'; import { assertThrowsAsync, ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; import { IChatVariablesService } from '../../common/chatVariables.js'; import { MockChatVariablesService } from '../common/mockChatVariables.js'; -import { ChatAgentLocation, ChatAgentService, IChatAgentImplementation, IChatAgentService } from '../../common/chatAgents.js'; +import { ChatAgentService, IChatAgentImplementation, IChatAgentService } from '../../common/chatAgents.js'; import { IChatSlashCommandService } from '../../common/chatSlashCommands.js'; import { IWorkbenchAssignmentService } from '../../../../services/assignment/common/assignmentService.js'; import { NullWorkbenchAssignmentService } from '../../../../services/assignment/test/common/nullAssignmentService.js'; import { CancellationToken } from '../../../../../base/common/cancellation.js'; import { nullExtensionDescription } from '../../../../services/extensions/common/extensions.js'; +import { ITextModelService } from '../../../../../editor/common/services/resolverService.js'; +import { IModelService } from '../../../../../editor/common/services/model.js'; +import { URI } from '../../../../../base/common/uri.js'; +import { assertType } from '../../../../../base/common/types.js'; +import { isEqual } from '../../../../../base/common/resources.js'; +import { waitForState } from '../../../../../base/common/observable.js'; +import { INotebookService } from '../../../notebook/common/notebookService.js'; +import { Range } from '../../../../../editor/common/core/range.js'; +import { ChatAgentLocation } from '../../common/constants.js'; function getAgentData(id: string) { return { @@ -44,6 +53,7 @@ suite('ChatEditingService', function () { const store = new DisposableStore(); let editingService: ChatEditingService; let chatService: IChatService; + let textModelService: ITextModelService; setup(function () { const collection = new ServiceCollection(); @@ -58,6 +68,11 @@ suite('ChatEditingService', function () { return Disposable.None; } }); + collection.set(INotebookService, new class extends mock() { + override hasSupportedNotebooks(resource: URI): boolean { + return false; + } + }); const insta = store.add(store.add(workbenchInstantiationService(undefined, store)).createChild(collection)); const value = insta.get(IChatEditingService); assert.ok(value instanceof ChatEditingService); @@ -74,6 +89,16 @@ suite('ChatEditingService', function () { }; store.add(chatAgentService.registerAgent('testAgent', { ...getAgentData('testAgent'), isDefault: true })); store.add(chatAgentService.registerAgentImplementation('testAgent', agent)); + + textModelService = insta.get(ITextModelService); + + const modelService = insta.get(IModelService); + + store.add(textModelService.registerTextModelContentProvider('test', { + async provideTextContent(resource) { + return modelService.createModel(resource.path.repeat(10), null, resource, false); + }, + })); }); teardown(() => { @@ -100,4 +125,38 @@ suite('ChatEditingService', function () { model.dispose(); }); + + test('create session, file entry & isCurrentlyBeingModifiedBy', async function () { + assert.ok(editingService); + + const uri = URI.from({ scheme: 'test', path: 'HelloWorld' }); + + const model = chatService.startSession(ChatAgentLocation.EditingSession, CancellationToken.None); + const session = await editingService.createEditingSession(model.sessionId, true); + + const chatRequest = model?.addRequest({ text: '', parts: [] }, { variables: [] }, 0); + assertType(chatRequest.response); + chatRequest.response.updateContent({ kind: 'textEdit', uri, edits: [], done: false }); + chatRequest.response.updateContent({ kind: 'textEdit', uri, edits: [{ range: new Range(1, 1, 1, 1), text: 'FarBoo\n' }], done: false }); + chatRequest.response.updateContent({ kind: 'textEdit', uri, edits: [], done: true }); + + const entry = await waitForState(session.entries.map(value => value.find(a => isEqual(a.modifiedURI, uri)))); + + assert.ok(isEqual(entry.modifiedURI, uri)); + + await waitForState(entry.isCurrentlyBeingModifiedBy.map(value => value === chatRequest.response)); + assert.ok(entry.isCurrentlyBeingModifiedBy.get() === chatRequest.response); + + const unset = waitForState(entry.isCurrentlyBeingModifiedBy.map(res => res === undefined)); + + chatRequest.response.complete(); + + await unset; + + await entry.reject(undefined); + + session.dispose(); + model.dispose(); + }); + }); diff --git a/src/vs/workbench/contrib/chat/test/browser/mockChatWidget.ts b/src/vs/workbench/contrib/chat/test/browser/mockChatWidget.ts index cf64e8379ac5..4924c4714d22 100644 --- a/src/vs/workbench/contrib/chat/test/browser/mockChatWidget.ts +++ b/src/vs/workbench/contrib/chat/test/browser/mockChatWidget.ts @@ -6,7 +6,7 @@ import { Event } from '../../../../../base/common/event.js'; import { URI } from '../../../../../base/common/uri.js'; import { IChatWidget, IChatWidgetService } from '../../browser/chat.js'; -import { ChatAgentLocation } from '../../common/chatAgents.js'; +import { ChatAgentLocation } from '../../common/constants.js'; export class MockChatWidgetService implements IChatWidgetService { readonly onDidAddWidget: Event = Event.None; diff --git a/src/vs/workbench/contrib/chat/test/common/__snapshots__/ChatRequestParser_agent_but_edit_mode.0.snap b/src/vs/workbench/contrib/chat/test/common/__snapshots__/ChatRequestParser_agent_but_edit_mode.0.snap new file mode 100644 index 000000000000..c746469fe1ec --- /dev/null +++ b/src/vs/workbench/contrib/chat/test/common/__snapshots__/ChatRequestParser_agent_but_edit_mode.0.snap @@ -0,0 +1,19 @@ +{ + parts: [ + { + range: { + start: 0, + endExclusive: 12 + }, + editorRange: { + startLineNumber: 1, + startColumn: 1, + endLineNumber: 1, + endColumn: 13 + }, + text: "@agent hello", + kind: "text" + } + ], + text: "@agent hello" +} \ No newline at end of file diff --git a/src/vs/workbench/contrib/chat/test/common/chatAgents.test.ts b/src/vs/workbench/contrib/chat/test/common/chatAgents.test.ts index 580fa44ce454..cec803118e1e 100644 --- a/src/vs/workbench/contrib/chat/test/common/chatAgents.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/chatAgents.test.ts @@ -9,7 +9,6 @@ import { ContextKeyExpression } from '../../../../../platform/contextkey/common/ import { ExtensionIdentifier } from '../../../../../platform/extensions/common/extensions.js'; import { MockContextKeyService } from '../../../../../platform/keybinding/test/common/mockKeybindingService.js'; import { ChatAgentService, IChatAgentData, IChatAgentImplementation } from '../../common/chatAgents.js'; -import { TestStorageService } from '../../../../test/common/workbenchTestServices.js'; const testAgentId = 'testAgent'; const testAgentData: IChatAgentData = { @@ -42,7 +41,7 @@ suite('ChatAgents', function () { let contextKeyService: TestingContextKeyService; setup(() => { contextKeyService = new TestingContextKeyService(); - chatAgentService = store.add(new ChatAgentService(contextKeyService, store.add(new TestStorageService()))); + chatAgentService = store.add(new ChatAgentService(contextKeyService)); }); test('registerAgent', async () => { diff --git a/src/vs/workbench/contrib/chat/test/common/chatModel.test.ts b/src/vs/workbench/contrib/chat/test/common/chatModel.test.ts index b8e38fcb6cef..9f5444ca5cdf 100644 --- a/src/vs/workbench/contrib/chat/test/common/chatModel.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/chatModel.test.ts @@ -16,11 +16,12 @@ import { TestInstantiationService } from '../../../../../platform/instantiation/ import { MockContextKeyService } from '../../../../../platform/keybinding/test/common/mockKeybindingService.js'; import { ILogService, NullLogService } from '../../../../../platform/log/common/log.js'; import { IStorageService } from '../../../../../platform/storage/common/storage.js'; -import { ChatAgentLocation, ChatAgentService, IChatAgentService } from '../../common/chatAgents.js'; +import { ChatAgentService, IChatAgentService } from '../../common/chatAgents.js'; import { ChatModel, ISerializableChatData1, ISerializableChatData2, ISerializableChatData3, normalizeSerializableChatData, Response } from '../../common/chatModel.js'; import { ChatRequestTextPart } from '../../common/chatParserTypes.js'; import { IExtensionService } from '../../../../services/extensions/common/extensions.js'; import { TestExtensionService, TestStorageService } from '../../../../test/common/workbenchTestServices.js'; +import { ChatAgentLocation } from '../../common/constants.js'; suite('ChatModel', () => { const testDisposables = ensureNoDisposablesAreLeakedInTestSuite(); diff --git a/src/vs/workbench/contrib/chat/test/common/chatRequestParser.test.ts b/src/vs/workbench/contrib/chat/test/common/chatRequestParser.test.ts index 7a2714635a63..b9dd31e234af 100644 --- a/src/vs/workbench/contrib/chat/test/common/chatRequestParser.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/chatRequestParser.test.ts @@ -13,11 +13,12 @@ import { ILogService, NullLogService } from '../../../../../platform/log/common/ import { IStorageService } from '../../../../../platform/storage/common/storage.js'; import { IExtensionService, nullExtensionDescription } from '../../../../services/extensions/common/extensions.js'; import { TestExtensionService, TestStorageService } from '../../../../test/common/workbenchTestServices.js'; -import { ChatAgentLocation, ChatAgentService, IChatAgentCommand, IChatAgentData, IChatAgentService } from '../../common/chatAgents.js'; +import { ChatAgentService, IChatAgentCommand, IChatAgentData, IChatAgentService } from '../../common/chatAgents.js'; import { ChatRequestParser } from '../../common/chatRequestParser.js'; import { IChatService } from '../../common/chatService.js'; import { IChatSlashCommandService } from '../../common/chatSlashCommands.js'; import { IChatVariablesService } from '../../common/chatVariables.js'; +import { ChatMode, ChatAgentLocation } from '../../common/constants.js'; import { ILanguageModelToolsService, IToolData } from '../../common/languageModelToolsService.js'; import { MockChatService } from './mockChatService.js'; import { MockChatVariablesService } from './mockChatVariables.js'; @@ -142,6 +143,16 @@ suite('ChatRequestParser', () => { await assertSnapshot(result); }); + test('agent but edit mode', async () => { + const agentsService = mockObject()({}); + agentsService.getAgentsByName.returns([getAgentWithSlashCommands([])]); + instantiationService.stub(IChatAgentService, agentsService as any); + + parser = instantiationService.createInstance(ChatRequestParser); + const result = parser.parseChatRequest('1', '@agent hello', undefined, { mode: ChatMode.Edit }); + await assertSnapshot(result); + }); + test('agent with question mark', async () => { const agentsService = mockObject()({}); agentsService.getAgentsByName.returns([getAgentWithSlashCommands([{ name: 'subCommand', description: '' }])]); diff --git a/src/vs/workbench/contrib/chat/test/common/chatService.test.ts b/src/vs/workbench/contrib/chat/test/common/chatService.test.ts index 8334b23f3baa..8f950258be99 100644 --- a/src/vs/workbench/contrib/chat/test/common/chatService.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/chatService.test.ts @@ -20,7 +20,7 @@ import { IStorageService } from '../../../../../platform/storage/common/storage. import { ITelemetryService } from '../../../../../platform/telemetry/common/telemetry.js'; import { NullTelemetryService } from '../../../../../platform/telemetry/common/telemetryUtils.js'; import { IWorkspaceContextService } from '../../../../../platform/workspace/common/workspace.js'; -import { ChatAgentLocation, ChatAgentService, IChatAgent, IChatAgentImplementation, IChatAgentService } from '../../common/chatAgents.js'; +import { ChatAgentService, IChatAgent, IChatAgentImplementation, IChatAgentService } from '../../common/chatAgents.js'; import { IChatModel, ISerializableChatData } from '../../common/chatModel.js'; import { IChatFollowup, IChatService } from '../../common/chatService.js'; import { ChatService } from '../../common/chatServiceImpl.js'; @@ -34,6 +34,7 @@ import { IExtensionService, nullExtensionDescription } from '../../../../service import { IViewsService } from '../../../../services/views/common/viewsService.js'; import { TestContextService, TestExtensionService, TestStorageService } from '../../../../test/common/workbenchTestServices.js'; import { MarkdownString } from '../../../../../base/common/htmlContent.js'; +import { ChatAgentLocation } from '../../common/constants.js'; const chatAgentWithUsedContextId = 'ChatProviderWithUsedContext'; const chatAgentWithUsedContext: IChatAgent = { diff --git a/src/vs/workbench/contrib/chat/test/common/mockChatService.ts b/src/vs/workbench/contrib/chat/test/common/mockChatService.ts index 841b65ccc085..833dbc57c4ad 100644 --- a/src/vs/workbench/contrib/chat/test/common/mockChatService.ts +++ b/src/vs/workbench/contrib/chat/test/common/mockChatService.ts @@ -6,14 +6,15 @@ import { CancellationToken } from '../../../../../base/common/cancellation.js'; import { Event } from '../../../../../base/common/event.js'; import { URI } from '../../../../../base/common/uri.js'; -import { ChatAgentLocation } from '../../common/chatAgents.js'; import { ChatModel, IChatModel, IChatRequestModel, IChatRequestVariableData, ISerializableChatData } from '../../common/chatModel.js'; import { IParsedChatRequest } from '../../common/chatParserTypes.js'; import { IChatCompleteResponse, IChatDetail, IChatProviderInfo, IChatSendRequestData, IChatSendRequestOptions, IChatService, IChatTransferredSessionData, IChatUserActionEvent } from '../../common/chatService.js'; +import { ChatAgentLocation } from '../../common/constants.js'; export class MockChatService implements IChatService { _serviceBrand: undefined; transferredSessionData: IChatTransferredSessionData | undefined; + onDidSubmitRequest: Event<{ chatSessionId: string }> = Event.None; private sessions = new Map(); @@ -89,4 +90,9 @@ export class MockChatService implements IChatService { setChatSessionTitle(sessionId: string, title: string): void { throw new Error('Method not implemented.'); } + + unifiedViewEnabled = false; + isEditingLocation(location: ChatAgentLocation): boolean { + throw new Error('Method not implemented.'); + } } diff --git a/src/vs/workbench/contrib/chat/test/common/mockChatVariables.ts b/src/vs/workbench/contrib/chat/test/common/mockChatVariables.ts index 9a59dc5e5ed9..267af76a9f11 100644 --- a/src/vs/workbench/contrib/chat/test/common/mockChatVariables.ts +++ b/src/vs/workbench/contrib/chat/test/common/mockChatVariables.ts @@ -3,10 +3,10 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { ChatAgentLocation } from '../../common/chatAgents.js'; import { IChatRequestVariableData, IChatRequestVariableEntry } from '../../common/chatModel.js'; import { IParsedChatRequest } from '../../common/chatParserTypes.js'; import { IChatVariablesService, IDynamicVariable } from '../../common/chatVariables.js'; +import { ChatAgentLocation } from '../../common/constants.js'; export class MockChatVariablesService implements IChatVariablesService { _serviceBrand: undefined; diff --git a/src/vs/workbench/contrib/chat/test/common/promptSyntax/codecs/chatPromptCodec.test.ts b/src/vs/workbench/contrib/chat/test/common/promptSyntax/codecs/chatPromptCodec.test.ts index dceacea29d5c..b063215ec7df 100644 --- a/src/vs/workbench/contrib/chat/test/common/promptSyntax/codecs/chatPromptCodec.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/promptSyntax/codecs/chatPromptCodec.test.ts @@ -45,11 +45,11 @@ export class TestChatPromptCodec extends TestDecoder { const testDisposables = ensureNoDisposablesAreLeakedInTestSuite(); - test('produces expected tokens', async () => { + test('• produces expected tokens', async () => { const test = testDisposables.add(new TestChatPromptCodec()); await test.run( - '#file:/etc/hosts some text\t\n for #file:./README.md\t testing\n ✔ purposes\n#file:LICENSE.md ✌ \t#file:.gitignore\n\n\n\t #file:/Users/legomushroom/repos/vscode ', + '#file:/etc/hosts some text\t\n for #file:./README.md\t testing\n ✔ purposes\n#file:LICENSE.md ✌ \t#file:.gitignore\n\n\n\t #file:/Users/legomushroom/repos/vscode \n\nsomething #file:\tsomewhere\n', [ new FileReference( new Range(1, 1, 1, 1 + 16), @@ -71,6 +71,10 @@ suite('ChatPromptCodec', () => { new Range(7, 5, 7, 5 + 38), '/Users/legomushroom/repos/vscode', ), + new FileReference( + new Range(9, 11, 9, 11 + 6), + '', + ), ], ); }); diff --git a/src/vs/workbench/contrib/chat/test/common/promptSyntax/codecs/chatPromptDecoder.test.ts b/src/vs/workbench/contrib/chat/test/common/promptSyntax/codecs/chatPromptDecoder.test.ts index 1f73d7febae4..6f009d574683 100644 --- a/src/vs/workbench/contrib/chat/test/common/promptSyntax/codecs/chatPromptDecoder.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/promptSyntax/codecs/chatPromptDecoder.test.ts @@ -46,7 +46,7 @@ export class TestChatPromptDecoder extends TestDecoder { const testDisposables = ensureNoDisposablesAreLeakedInTestSuite(); - test('produces expected tokens', async () => { + test('• produces expected tokens', async () => { const test = testDisposables.add( new TestChatPromptDecoder(), ); @@ -59,6 +59,7 @@ suite('ChatPromptDecoder', () => { '## Heading Title', ' \t#file:a/b/c/filename2.md\t🖖\t#file:other-file.md', ' [#file:reference.md](./reference.md)some text #file:/some/file/with/absolute/path.md', + 'text text #file: another text', ]; await test.run( @@ -86,6 +87,10 @@ suite('ChatPromptDecoder', () => { new Range(7, 48, 7, 48 + 38), '/some/file/with/absolute/path.md', ), + new FileReference( + new Range(8, 11, 8, 11 + 6), + '', + ), ], ); }); diff --git a/src/vs/workbench/contrib/chat/test/common/promptSyntax/codecs/tokens/fileReference.test.ts b/src/vs/workbench/contrib/chat/test/common/promptSyntax/codecs/tokens/fileReference.test.ts index 4a48b3a850d4..3478fd7d34d4 100644 --- a/src/vs/workbench/contrib/chat/test/common/promptSyntax/codecs/tokens/fileReference.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/promptSyntax/codecs/tokens/fileReference.test.ts @@ -7,14 +7,16 @@ import assert from 'assert'; import { randomInt } from '../../../../../../../../base/common/numbers.js'; import { Range } from '../../../../../../../../editor/common/core/range.js'; import { assertDefined } from '../../../../../../../../base/common/types.js'; +import { BaseToken } from '../../../../../../../../editor/common/codecs/baseToken.js'; +import { PromptToken } from '../../../../../common/promptSyntax/codecs/tokens/promptToken.js'; import { FileReference } from '../../../../../common/promptSyntax/codecs/tokens/fileReference.js'; import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../../../../base/test/common/utils.js'; -import { BaseToken } from '../../../../../../../../editor/common/codecs/baseToken.js'; +import { PromptVariable, PromptVariableWithData } from '../../../../../common/promptSyntax/codecs/tokens/promptVariable.js'; suite('FileReference', () => { ensureNoDisposablesAreLeakedInTestSuite(); - test('`linkRange`', () => { + test('• linkRange', () => { const lineNumber = randomInt(100, 1); const columnStartNumber = randomInt(100, 1); const path = `/temp/test/file-${randomInt(Number.MAX_SAFE_INTEGER)}.txt`; @@ -46,7 +48,7 @@ suite('FileReference', () => { ); }); - test('`path`', () => { + test('• path', () => { const lineNumber = randomInt(100, 1); const columnStartNumber = randomInt(100, 1); const link = `/temp/test/file-${randomInt(Number.MAX_SAFE_INTEGER)}.txt`; @@ -67,7 +69,7 @@ suite('FileReference', () => { ); }); - test('extends `BaseToken`', () => { + test('• extends `PromptVariableWithData` and others', () => { const lineNumber = randomInt(100, 1); const columnStartNumber = randomInt(100, 1); const link = `/temp/test/file-${randomInt(Number.MAX_SAFE_INTEGER)}.txt`; @@ -81,6 +83,21 @@ suite('FileReference', () => { ); const fileReference = new FileReference(range, link); + assert( + fileReference instanceof PromptVariableWithData, + 'Must extend `PromptVariableWithData`.', + ); + + assert( + fileReference instanceof PromptVariable, + 'Must extend `PromptVariable`.', + ); + + assert( + fileReference instanceof PromptToken, + 'Must extend `PromptToken`.', + ); + assert( fileReference instanceof BaseToken, 'Must extend `BaseToken`.', diff --git a/src/vs/workbench/contrib/chat/test/common/promptSyntax/parsers/textModelPromptParser.test.ts b/src/vs/workbench/contrib/chat/test/common/promptSyntax/parsers/textModelPromptParser.test.ts index 95ec4f3bd17a..c13322ea1a2a 100644 --- a/src/vs/workbench/contrib/chat/test/common/promptSyntax/parsers/textModelPromptParser.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/promptSyntax/parsers/textModelPromptParser.test.ts @@ -10,7 +10,7 @@ import { Schemas } from '../../../../../../../base/common/network.js'; import { ExpectedReference } from '../testUtils/expectedReference.js'; import { ITextModel } from '../../../../../../../editor/common/model.js'; import { Disposable } from '../../../../../../../base/common/lifecycle.js'; -import { FileOpenFailed } from '../../../../common/promptFileReferenceErrors.js'; +import { OpenFailed } from '../../../../common/promptFileReferenceErrors.js'; import { IFileService } from '../../../../../../../platform/files/common/files.js'; import { randomBoolean } from '../../../../../../../base/test/common/testUtils.js'; import { FileService } from '../../../../../../../platform/files/common/fileService.js'; @@ -143,7 +143,7 @@ suite('TextModelPromptParser', () => { startLine: 1, startColumn: 27, pathStartColumn: 33, - childrenOrError: new FileOpenFailed(createURI('/abs/path/to/file.md'), 'File not found.'), + childrenOrError: new OpenFailed(createURI('/abs/path/to/file.md'), 'File not found.'), }), new ExpectedReference({ uri: createURI('/foo/folder/binary.file'), @@ -152,7 +152,7 @@ suite('TextModelPromptParser', () => { startLine: 7, startColumn: 10, pathStartColumn: 16, - childrenOrError: new FileOpenFailed(createURI('/foo/folder/binary.file'), 'File not found.'), + childrenOrError: new OpenFailed(createURI('/foo/folder/binary.file'), 'File not found.'), }), new ExpectedReference({ uri: createURI('/etc/hosts/random-file.txt'), @@ -161,7 +161,7 @@ suite('TextModelPromptParser', () => { startLine: 7, startColumn: 81, pathStartColumn: 91, - childrenOrError: new FileOpenFailed(createURI('/etc/hosts/random-file.txt'), 'File not found.'), + childrenOrError: new OpenFailed(createURI('/etc/hosts/random-file.txt'), 'File not found.'), }), ]); }); @@ -196,7 +196,7 @@ suite('TextModelPromptParser', () => { startLine: 3, startColumn: 43, pathStartColumn: 55, - childrenOrError: new FileOpenFailed(createURI('/absolute/folder/and/a/foo-bar-baz/another-file.ts'), 'File not found.'), + childrenOrError: new OpenFailed(createURI('/absolute/folder/and/a/foo-bar-baz/another-file.ts'), 'File not found.'), }), new ExpectedReference({ uri: createURI('/absolute/c/file_name.prompt.md'), @@ -205,7 +205,7 @@ suite('TextModelPromptParser', () => { startLine: 6, startColumn: 7, pathStartColumn: 17, - childrenOrError: new FileOpenFailed(createURI('/absolute/c/file_name.prompt.md'), 'File not found.'), + childrenOrError: new OpenFailed(createURI('/absolute/c/file_name.prompt.md'), 'File not found.'), }), new ExpectedReference({ uri: createURI('/absolute/folder/main.rs'), @@ -214,7 +214,7 @@ suite('TextModelPromptParser', () => { startLine: 11, startColumn: 36, pathStartColumn: 42, - childrenOrError: new FileOpenFailed(createURI('/absolute/folder/main.rs'), 'File not found.'), + childrenOrError: new OpenFailed(createURI('/absolute/folder/main.rs'), 'File not found.'), }), new ExpectedReference({ uri: createURI('/absolute/folder/and/a/samefile.jpeg'), @@ -223,7 +223,7 @@ suite('TextModelPromptParser', () => { startLine: 11, startColumn: 56, pathStartColumn: 62, - childrenOrError: new FileOpenFailed(createURI('/absolute/folder/and/a/samefile.jpeg'), 'File not found.'), + childrenOrError: new OpenFailed(createURI('/absolute/folder/and/a/samefile.jpeg'), 'File not found.'), }), ]); }); diff --git a/src/vs/workbench/contrib/chat/test/common/promptSyntax/promptFileReference.test.ts b/src/vs/workbench/contrib/chat/test/common/promptSyntax/promptFileReference.test.ts index 0a19b7d85e5b..f9c6214af670 100644 --- a/src/vs/workbench/contrib/chat/test/common/promptSyntax/promptFileReference.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/promptSyntax/promptFileReference.test.ts @@ -25,8 +25,8 @@ import { IConfigurationService } from '../../../../../../platform/configuration/ import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; import { ConfigurationService } from '../../../../../../platform/configuration/common/configurationService.js'; import { InMemoryFileSystemProvider } from '../../../../../../platform/files/common/inMemoryFilesystemProvider.js'; -import { NonPromptSnippetFile, RecursiveReference, FileOpenFailed } from '../../../common/promptFileReferenceErrors.js'; import { TestInstantiationService } from '../../../../../../platform/instantiation/test/common/instantiationServiceMock.js'; +import { NotPromptFile, RecursiveReference, OpenFailed, FolderReference } from '../../../common/promptFileReferenceErrors.js'; /** * Represents a file reference with an expected @@ -147,6 +147,8 @@ class TestPromptFileReference extends Disposable { /** * Create expected file reference for testing purposes. * + * Note! This utility also use for `markdown links` at the moment. + * * @param filePath The expected path of the file reference (without the `#file:` prefix). * @param lineNumber The expected line number of the file reference. * @param startColumnNumber The expected start column number of the file reference. @@ -187,7 +189,7 @@ suite('PromptFileReference (Unix)', function () { instantiationService.stub(IConfigurationService, nullConfigService); }); - test('resolves nested file references', async function () { + test('• resolves nested file references', async function () { if (isWindows) { this.skip(); } @@ -216,14 +218,14 @@ suite('PromptFileReference (Unix)', function () { children: [ { name: 'file3.prompt.md', - contents: `\n\n\t- some seemingly random #file:${rootFolder}/folder1/some-other-folder/yetAnotherFolder🤭/another-file.prompt.md contents\n some more\t content`, + contents: `\n[](./some-other-folder/non-existing-folder)\n\t- some seemingly random #file:${rootFolder}/folder1/some-other-folder/yetAnotherFolder🤭/another-file.prompt.md contents\n some more\t content`, }, { name: 'some-other-folder', children: [ { name: 'file4.prompt.md', - contents: 'this file has a non-existing #file:./some-non-existing/file.prompt.md\t\treference\n\n\nand some\n non-prompt #file:./some-non-prompt-file.md', + contents: 'this file has a non-existing #file:./some-non-existing/file.prompt.md\t\treference\n\n\nand some\n non-prompt #file:./some-non-prompt-file.md\t\t \t[](../../folder1/)\t', }, { name: 'file.txt', @@ -234,7 +236,7 @@ suite('PromptFileReference (Unix)', function () { children: [ { name: 'another-file.prompt.md', - contents: 'another-file.prompt.md contents\t [#file:file.txt](../file.txt)', + contents: `[](${rootFolder}/folder1/some-other-folder)\nanother-file.prompt.md contents\t [#file:file.txt](../file.txt)`, }, { name: 'one_more_file_just_in_case.prompt.md', @@ -260,6 +262,18 @@ suite('PromptFileReference (Unix)', function () { rootUri, createTestFileReference('folder1/file3.prompt.md', 2, 14), ), + new ExpectedReference( + URI.joinPath(rootUri, './folder1'), + createTestFileReference( + `./some-other-folder/non-existing-folder`, + 2, + 1, + ), + new OpenFailed( + URI.joinPath(rootUri, './folder1/some-other-folder/non-existing-folder'), + 'Reference to non-existing file cannot be opened.', + ), + ), new ExpectedReference( URI.joinPath(rootUri, './folder1'), createTestFileReference( @@ -268,10 +282,18 @@ suite('PromptFileReference (Unix)', function () { 26, ), ), + new ExpectedReference( + URI.joinPath(rootUri, './folder1/some-other-folder'), + createTestFileReference('.', 1, 1), + new FolderReference( + URI.joinPath(rootUri, './folder1/some-other-folder'), + 'This folder is not a prompt file!', + ), + ), new ExpectedReference( URI.joinPath(rootUri, './folder1/some-other-folder/yetAnotherFolder🤭'), - createTestFileReference('../file.txt', 1, 35), - new NonPromptSnippetFile( + createTestFileReference('../file.txt', 2, 35), + new NotPromptFile( URI.joinPath(rootUri, './folder1/some-other-folder/file.txt'), 'Ughh oh, that is not a prompt file!', ), @@ -283,7 +305,7 @@ suite('PromptFileReference (Unix)', function () { new ExpectedReference( URI.joinPath(rootUri, './folder1/some-other-folder'), createTestFileReference('./some-non-existing/file.prompt.md', 1, 30), - new FileOpenFailed( + new OpenFailed( URI.joinPath(rootUri, './folder1/some-other-folder/some-non-existing/file.prompt.md'), 'Failed to open non-existring prompt snippets file', ), @@ -291,18 +313,26 @@ suite('PromptFileReference (Unix)', function () { new ExpectedReference( URI.joinPath(rootUri, './folder1/some-other-folder'), createTestFileReference('./some-non-prompt-file.md', 5, 13), - new FileOpenFailed( + new OpenFailed( URI.joinPath(rootUri, './folder1/some-other-folder/some-non-prompt-file.md'), 'Oh no!', ), ), + new ExpectedReference( + URI.joinPath(rootUri, './some-other-folder/folder1'), + createTestFileReference('../../folder1', 5, 48), + new FolderReference( + URI.joinPath(rootUri, './folder1'), + 'Uggh ohh!', + ), + ), ] )); await test.run(); }); - test('does not fall into infinite reference recursion', async function () { + test('• does not fall into infinite reference recursion', async function () { if (isWindows) { this.skip(); } @@ -409,7 +439,7 @@ suite('PromptFileReference (Unix)', function () { new ExpectedReference( URI.joinPath(rootUri, './folder1/some-other-folder'), createTestFileReference('../some-non-existing/file.prompt.md', 1, 30), - new FileOpenFailed( + new OpenFailed( URI.joinPath(rootUri, './folder1/some-non-existing/file.prompt.md'), 'Uggh ohh!', ), @@ -442,7 +472,7 @@ suite('PromptFileReference (Unix)', function () { new ExpectedReference( rootUri, createTestFileReference('./file1.md', 6, 2), - new NonPromptSnippetFile( + new NotPromptFile( URI.joinPath(rootUri, './file1.md'), 'Uggh oh!', ), diff --git a/src/vs/workbench/contrib/chat/test/common/promptSyntax/testUtils/expectedReference.ts b/src/vs/workbench/contrib/chat/test/common/promptSyntax/testUtils/expectedReference.ts index 45bd8552489d..d43123799e11 100644 --- a/src/vs/workbench/contrib/chat/test/common/promptSyntax/testUtils/expectedReference.ts +++ b/src/vs/workbench/contrib/chat/test/common/promptSyntax/testUtils/expectedReference.ts @@ -7,8 +7,8 @@ import assert from 'assert'; import { URI } from '../../../../../../../base/common/uri.js'; import { Range } from '../../../../../../../editor/common/core/range.js'; import { assertDefined } from '../../../../../../../base/common/types.js'; -import { ParseError } from '../../../../common/promptFileReferenceErrors.js'; -import { IPromptFileReference } from '../../../../common/promptSyntax/parsers/types.js'; +import { ResolveError } from '../../../../common/promptFileReferenceErrors.js'; +import { IPromptReference } from '../../../../common/promptSyntax/parsers/types.js'; import { TErrorCondition } from '../../../../common/promptSyntax/parsers/basePromptParser.js'; /** @@ -63,7 +63,7 @@ export class ExpectedReference { /** * Validate that the provided reference is equal to this object. */ - public validateEqual(other: IPromptFileReference) { + public validateEqual(other: IPromptReference) { const { uri, text, path, childrenOrError = [] } = this.options; const errorPrefix = `[${uri}] `; @@ -130,7 +130,7 @@ export class ExpectedReference { * Next validate children or error condition. */ - if (childrenOrError instanceof ParseError) { + if (childrenOrError instanceof ResolveError) { const error = childrenOrError; const { errorCondition } = other; assertDefined( @@ -139,8 +139,8 @@ export class ExpectedReference { ); assert( - errorCondition instanceof ParseError, - `${errorPrefix} Expected 'errorCondition' to be a 'ParseError'.`, + errorCondition instanceof ResolveError, + `${errorPrefix} Expected 'errorCondition' to be a 'ResolveError'.`, ); assert( diff --git a/src/vs/workbench/contrib/chat/test/common/promptSyntax/utils/promptFilesLocator.test.ts b/src/vs/workbench/contrib/chat/test/common/promptSyntax/utils/promptFilesLocator.test.ts index 14347ec2af32..8228a56d3cf1 100644 --- a/src/vs/workbench/contrib/chat/test/common/promptSyntax/utils/promptFilesLocator.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/promptSyntax/utils/promptFilesLocator.test.ts @@ -27,10 +27,14 @@ import { IWorkspace, IWorkspaceContextService, IWorkspaceFolder } from '../../.. const mockConfigService = (value: T): IConfigurationService => { return mockService({ getValue(key?: string | IConfigurationOverrides) { - assert.strictEqual( - key, - PromptsConfig.CONFIG_KEY, - `Mocked service supports only one configuration key: '${PromptsConfig.CONFIG_KEY}'.`, + assert( + typeof key === 'string', + `Expected string configuration key, got '${typeof key}'.`, + ); + + assert( + [PromptsConfig.CONFIG_KEY, PromptsConfig.LOCATIONS_CONFIG_KEY].includes(key), + `Unsupported configuration key '${key}'.`, ); return value; diff --git a/src/vs/workbench/contrib/chat/test/common/voiceChatService.test.ts b/src/vs/workbench/contrib/chat/test/common/voiceChatService.test.ts index 0005bca091e8..16e504d20ac1 100644 --- a/src/vs/workbench/contrib/chat/test/common/voiceChatService.test.ts +++ b/src/vs/workbench/contrib/chat/test/common/voiceChatService.test.ts @@ -13,10 +13,11 @@ import { ExtensionIdentifier } from '../../../../../platform/extensions/common/e import { MockContextKeyService } from '../../../../../platform/keybinding/test/common/mockKeybindingService.js'; import { nullExtensionDescription } from '../../../../services/extensions/common/extensions.js'; import { ISpeechProvider, ISpeechService, ISpeechToTextEvent, ISpeechToTextSession, ITextToSpeechSession, KeywordRecognitionStatus, SpeechToTextStatus } from '../../../speech/common/speechService.js'; -import { ChatAgentLocation, IChatAgent, IChatAgentCommand, IChatAgentCompletionItem, IChatAgentData, IChatAgentHistoryEntry, IChatAgentImplementation, IChatAgentMetadata, IChatAgentRequest, IChatAgentResult, IChatAgentService, IChatParticipantDetectionProvider, IChatWelcomeMessageContent } from '../../common/chatAgents.js'; +import { IChatAgent, IChatAgentCommand, IChatAgentCompletionItem, IChatAgentData, IChatAgentHistoryEntry, IChatAgentImplementation, IChatAgentMetadata, IChatAgentRequest, IChatAgentResult, IChatAgentService, IChatParticipantDetectionProvider, IChatWelcomeMessageContent } from '../../common/chatAgents.js'; import { IChatModel } from '../../common/chatModel.js'; import { IChatFollowup, IChatProgress } from '../../common/chatService.js'; import { IVoiceChatSessionOptions, IVoiceChatTextEvent, VoiceChatService } from '../../common/voiceChatService.js'; +import { ChatAgentLocation } from '../../common/constants.js'; suite('VoiceChat', () => { @@ -66,7 +67,6 @@ suite('VoiceChat', () => { class TestChatAgentService implements IChatAgentService { _serviceBrand: undefined; readonly onDidChangeAgents = Event.None; - readonly onDidChangeToolsAgentModeEnabled = Event.None; registerAgentImplementation(id: string, agent: IChatAgentImplementation): IDisposable { throw new Error(); } registerDynamicAgent(data: IChatAgentData, agentImpl: IChatAgentImplementation): IDisposable { throw new Error('Method not implemented.'); } invokeAgent(id: string, request: IChatAgentRequest, progress: (part: IChatProgress) => void, history: IChatAgentHistoryEntry[], token: CancellationToken): Promise { throw new Error(); } @@ -86,10 +86,7 @@ suite('VoiceChat', () => { getAgentCompletionItems(id: string, query: string, token: CancellationToken): Promise { throw new Error('Method not implemented.'); } agentHasDupeName(id: string): boolean { throw new Error('Method not implemented.'); } getChatTitle(id: string, history: IChatAgentHistoryEntry[], token: CancellationToken): Promise { throw new Error('Method not implemented.'); } - readonly toolsAgentModeEnabled: boolean = false; - toggleToolsAgentMode(): void { - throw new Error('Method not implemented.'); - } + hasToolsAgent: boolean = false; hasChatParticipantDetectionProviders(): boolean { throw new Error('Method not implemented.'); } diff --git a/src/vs/workbench/contrib/codeEditor/browser/dictation/editorDictation.ts b/src/vs/workbench/contrib/codeEditor/browser/dictation/editorDictation.ts index 6c657fb91247..9a24f11cbb43 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/dictation/editorDictation.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/dictation/editorDictation.ts @@ -10,7 +10,7 @@ import { CancellationTokenSource } from '../../../../../base/common/cancellation import { Disposable, DisposableStore, MutableDisposable, toDisposable } from '../../../../../base/common/lifecycle.js'; import { ContentWidgetPositionPreference, ICodeEditor, IContentWidget, IContentWidgetPosition } from '../../../../../editor/browser/editorBrowser.js'; import { IEditorContribution } from '../../../../../editor/common/editorCommon.js'; -import { ContextKeyExpr, IContextKeyService, RawContextKey } from '../../../../../platform/contextkey/common/contextkey.js'; +import { ContextKeyExpr, IContextKey, IContextKeyService, RawContextKey } from '../../../../../platform/contextkey/common/contextkey.js'; import { HasSpeechProvider, ISpeechService, SpeechToTextInProgress, SpeechToTextStatus } from '../../../speech/common/speechService.js'; import { Codicon } from '../../../../../base/common/codicons.js'; import { EditorOption } from '../../../../../editor/common/config/editorOptions.js'; @@ -187,18 +187,21 @@ export class EditorDictation extends Disposable implements IEditorContribution { return editor.getContribution(EditorDictation.ID); } - private readonly widget = this._register(new DictationWidget(this.editor, this.keybindingService)); - private readonly editorDictationInProgress = EDITOR_DICTATION_IN_PROGRESS.bindTo(this.contextKeyService); + private readonly widget: DictationWidget; + private readonly editorDictationInProgress: IContextKey; private readonly sessionDisposables = this._register(new MutableDisposable()); constructor( private readonly editor: ICodeEditor, @ISpeechService private readonly speechService: ISpeechService, - @IContextKeyService private readonly contextKeyService: IContextKeyService, - @IKeybindingService private readonly keybindingService: IKeybindingService + @IContextKeyService contextKeyService: IContextKeyService, + @IKeybindingService keybindingService: IKeybindingService ) { super(); + + this.widget = this._register(new DictationWidget(this.editor, keybindingService)); + this.editorDictationInProgress = EDITOR_DICTATION_IN_PROGRESS.bindTo(contextKeyService); } async start(): Promise { diff --git a/src/vs/workbench/contrib/codeEditor/browser/emptyTextEditorHint/emptyTextEditorHint.ts b/src/vs/workbench/contrib/codeEditor/browser/emptyTextEditorHint/emptyTextEditorHint.ts index 540a6deec5f7..e8aedfe3cc5e 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/emptyTextEditorHint/emptyTextEditorHint.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/emptyTextEditorHint/emptyTextEditorHint.ts @@ -33,9 +33,10 @@ import { LOG_MODE_ID, OUTPUT_MODE_ID } from '../../../../services/output/common/ import { SEARCH_RESULT_LANGUAGE_ID } from '../../../../services/search/common/search.js'; import { getDefaultHoverDelegate } from '../../../../../base/browser/ui/hover/hoverDelegateFactory.js'; import { IHoverService } from '../../../../../platform/hover/browser/hover.js'; -import { ChatAgentLocation, IChatAgent, IChatAgentService } from '../../../chat/common/chatAgents.js'; +import { IChatAgent, IChatAgentService } from '../../../chat/common/chatAgents.js'; import { IContextMenuService } from '../../../../../platform/contextview/browser/contextView.js'; import { StandardMouseEvent } from '../../../../../base/browser/mouseEvent.js'; +import { ChatAgentLocation } from '../../../chat/common/constants.js'; const $ = dom.$; diff --git a/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.ts b/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.ts index ff2710dcd2b2..a896a8019fdc 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/inspectEditorTokens/inspectEditorTokens.ts @@ -419,7 +419,7 @@ class InspectEditorTokensWidget extends Disposable implements IContentWidget { } dom.append(tbody, $('tr', undefined, - $('td.tiw-metadata-key', undefined, 'tree-sitter scopes' as string), + $('td.tiw-metadata-key', undefined, 'tree-sitter tree' as string), $('td.tiw-metadata-value.tiw-metadata-scopes', undefined, ...scopes), )); @@ -428,7 +428,7 @@ class InspectEditorTokensWidget extends Disposable implements IContentWidget { if (captures && captures.length > 0) { dom.append(tbody, $('tr', undefined, $('td.tiw-metadata-key', undefined, 'foreground'), - $('td.tiw-metadata-value', undefined, captures[captures.length - 1].name), + $('td.tiw-metadata-value', undefined, captures.map(cap => cap.name).join(' ')), )); } } diff --git a/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts b/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts index 00a919f8aabe..d7ccb452f4b1 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/saveParticipants.ts @@ -6,7 +6,6 @@ import { CancellationToken, CancellationTokenSource } from '../../../../base/common/cancellation.js'; import { HierarchicalKind } from '../../../../base/common/hierarchicalKind.js'; import { Disposable } from '../../../../base/common/lifecycle.js'; -import { StopWatch } from '../../../../base/common/stopwatch.js'; import * as strings from '../../../../base/common/strings.js'; import { IActiveCodeEditor, isCodeEditor } from '../../../../editor/browser/editorBrowser.js'; import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js'; @@ -28,14 +27,13 @@ import { ExtensionIdentifier } from '../../../../platform/extensions/common/exte import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { IProgress, IProgressStep, Progress } from '../../../../platform/progress/common/progress.js'; import { Registry } from '../../../../platform/registry/common/platform.js'; -import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; import { IWorkbenchContribution, IWorkbenchContributionsRegistry, Extensions as WorkbenchContributionsExtensions } from '../../../common/contributions.js'; import { SaveReason } from '../../../common/editor.js'; -import { getModifiedRanges } from '../../format/browser/formatModified.js'; import { IEditorService } from '../../../services/editor/common/editorService.js'; import { IHostService } from '../../../services/host/browser/host.js'; import { LifecyclePhase } from '../../../services/lifecycle/common/lifecycle.js'; import { ITextFileEditorModel, ITextFileSaveParticipant, ITextFileSaveParticipantContext, ITextFileService } from '../../../services/textfile/common/textfiles.js'; +import { getModifiedRanges } from '../../format/browser/formatModified.js'; export class TrimWhitespaceParticipant implements ITextFileSaveParticipant { @@ -280,7 +278,6 @@ class CodeActionOnSaveParticipant extends Disposable implements ITextFileSavePar @IHostService private readonly hostService: IHostService, @IEditorService private readonly editorService: IEditorService, @ICodeEditorService private readonly codeEditorService: ICodeEditorService, - @ITelemetryService private readonly telemetryService: ITelemetryService ) { super(); @@ -411,27 +408,8 @@ class CodeActionOnSaveParticipant extends Disposable implements ITextFileSavePar }; for (const codeActionKind of codeActionsOnSave) { - const sw = new StopWatch(); - const actionsToRun = await this.getActionsToRun(model, codeActionKind, excludes, getActionProgress, token); - // Telemetry for duration of each code action on save. - type CodeActionOnSave = { - codeAction: string; - duration: number; - }; - type CodeActionOnSaveClassification = { - owner: 'justschen'; - comment: 'Information about the code action that was accepted on save.'; - codeAction: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Kind of the code action setting that is run.' }; - duration: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Duration it took for TS to return the action to run for each kind. ' }; - }; - - this.telemetryService.publicLog2('codeAction.appliedOnSave', { - codeAction: codeActionKind.value, - duration: sw.elapsed() - }); - if (token.isCancellationRequested) { actionsToRun.dispose(); return; diff --git a/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.ts b/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.ts index 8241f44ff195..2bea83e98e1c 100644 --- a/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.ts +++ b/src/vs/workbench/contrib/codeEditor/browser/suggestEnabledInput/suggestEnabledInput.ts @@ -361,7 +361,7 @@ export class SuggestEnabledInputWithHistory extends SuggestEnabledInput implemen @IConfigurationService configurationService: IConfigurationService ) { super(id, parent, suggestionProvider, ariaLabel, resourceHandle, suggestOptions, instantiationService, modelService, contextKeyService, languageFeaturesService, configurationService); - this.history = new HistoryNavigator(new Set(history), 100); + this.history = this._register(new HistoryNavigator(new Set(history), 100)); } public addToHistory(): void { diff --git a/src/vs/workbench/contrib/comments/browser/commentFormActions.ts b/src/vs/workbench/contrib/comments/browser/commentFormActions.ts index 9b559a15e10a..f83a1b60a533 100644 --- a/src/vs/workbench/contrib/comments/browser/commentFormActions.ts +++ b/src/vs/workbench/contrib/comments/browser/commentFormActions.ts @@ -52,11 +52,11 @@ export class CommentFormActions implements IDisposable { const button = dropDownActions.length ? new ButtonWithDropdown(this.container, { contextMenuProvider: this.contextMenuService, actions: dropDownActions, - actionRunner: new class extends ActionRunner { + actionRunner: this._toDispose.add(new class extends ActionRunner { protected override async runAction(action: IAction, context?: unknown): Promise { return actionHandler(action); } - }, + }), secondary: !isPrimary, title, addPrimaryActionToDropdown: false, diff --git a/src/vs/workbench/contrib/comments/browser/commentService.ts b/src/vs/workbench/contrib/comments/browser/commentService.ts index 309745ca99d2..550288cb0de9 100644 --- a/src/vs/workbench/contrib/comments/browser/commentService.ts +++ b/src/vs/workbench/contrib/comments/browser/commentService.ts @@ -22,6 +22,7 @@ import { CommentContextKeys } from '../common/commentContextKeys.js'; import { ILogService } from '../../../../platform/log/common/log.js'; import { CommentsModel, ICommentsModel } from './commentsModel.js'; import { IModelService } from '../../../../editor/common/services/model.js'; +import { Schemas } from '../../../../base/common/network.js'; export const ICommentService = createDecorator('commentService'); @@ -90,6 +91,7 @@ export interface ICommentService { readonly onDidSetDataProvider: Event; readonly onDidDeleteDataProvider: Event; readonly onDidChangeCommentingEnabled: Event; + readonly onResourceHasCommentingRanges: Event; readonly isCommentingEnabled: boolean; readonly commentsModel: ICommentsModel; readonly lastActiveCommentcontroller: ICommentController | undefined; @@ -154,6 +156,9 @@ export class CommentService extends Disposable implements ICommentService { private readonly _onDidChangeCommentingEnabled = this._register(new Emitter()); readonly onDidChangeCommentingEnabled = this._onDidChangeCommentingEnabled.event; + private readonly _onResourceHasCommentingRanges = this._register(new Emitter()); + readonly onResourceHasCommentingRanges = this._onResourceHasCommentingRanges.event; + private readonly _onDidChangeActiveCommentingRange: Emitter<{ range: Range; commentingRangesInfo: CommentingRanges; @@ -232,6 +237,10 @@ export class CommentService extends Disposable implements ICommentService { })); this._register(this.modelService.onModelAdded(model => { + // Excluded schemes + if ((model.uri.scheme === Schemas.vscodeSourceControl)) { + return; + } // Allows comment providers to cause their commenting ranges to be prefetched by opening text documents in the background. if (!this._commentingRangeResources.has(model.uri.toString())) { this.getDocumentComments(model.uri); @@ -240,11 +249,16 @@ export class CommentService extends Disposable implements ICommentService { } private _updateResourcesWithCommentingRanges(resource: URI, commentInfos: (ICommentInfo | null)[]) { + let addedResources = false; for (const comments of commentInfos) { if (comments && (comments.commentingRanges.ranges.length > 0 || comments.threads.length > 0)) { this._commentingRangeResources.add(resource.toString()); + addedResources = true; } } + if (addedResources) { + this._onResourceHasCommentingRanges.fire(); + } } private _handleConfiguration() { diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadBody.ts b/src/vs/workbench/contrib/comments/browser/commentThreadBody.ts index 035b88131fe7..7daa727eddcd 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadBody.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadBody.ts @@ -204,6 +204,8 @@ export class CommentThreadBody extends D let lastCommentElement: HTMLElement | null = null; const newCommentNodeList: CommentNode[] = []; const newCommentsInEditMode: CommentNode[] = []; + const startEditing: Promise[] = []; + for (let i = newCommentsLen - 1; i >= 0; i--) { const currentComment = commentThread.comments![i]; const oldCommentNode = this._commentElements.filter(commentNode => commentNode.comment.uniqueIdInThread === currentComment.uniqueIdInThread); @@ -223,7 +225,7 @@ export class CommentThreadBody extends D } if (currentComment.mode === languages.CommentMode.Editing) { - await newElement.switchToEditMode(); + startEditing.push(newElement.switchToEditMode()); newCommentsInEditMode.push(newElement); } } @@ -231,6 +233,8 @@ export class CommentThreadBody extends D this._commentThread = commentThread; this._commentElements = newCommentNodeList; + // Start editing *after* updating the thread and elements to avoid a sequencing issue https://github.com/microsoft/vscode/issues/239191 + await Promise.all(startEditing); if (newCommentsInEditMode.length) { const lastIndex = this._commentElements.indexOf(newCommentsInEditMode[newCommentsInEditMode.length - 1]); diff --git a/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts b/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts index 0039a4030eaa..e664efbc245d 100644 --- a/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts +++ b/src/vs/workbench/contrib/comments/browser/commentThreadZoneWidget.ts @@ -157,7 +157,7 @@ export class ReviewZoneWidget extends ZoneWidget implements ICommentThreadWidget this._commentThreadDisposables = []; this.create(); - this._globalToDispose.add(this.themeService.onDidColorThemeChange(this._applyTheme, this)); + this._globalToDispose.add(this.themeService.onDidColorThemeChange(e => this._applyTheme(e.theme))); this._globalToDispose.add(this.editor.onDidChangeConfiguration(e => { if (e.hasChanged(EditorOption.fontInfo)) { this._applyTheme(this.themeService.getColorTheme()); diff --git a/src/vs/workbench/contrib/comments/browser/commentsAccessibleView.ts b/src/vs/workbench/contrib/comments/browser/commentsAccessibleView.ts index c18066cbe58e..3efc58ca953c 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsAccessibleView.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsAccessibleView.ts @@ -23,6 +23,7 @@ import { isCodeEditor } from '../../../../editor/browser/editorBrowser.js'; import { URI } from '../../../../base/common/uri.js'; import { CommentThread, Comment } from '../../../../editor/common/languages.js'; import { IRange } from '../../../../editor/common/core/range.js'; +import { IAction } from '../../../../base/common/actions.js'; export class CommentsAccessibleView extends Disposable implements IAccessibleViewImplementation { readonly priority = 90; @@ -72,30 +73,33 @@ export class CommentThreadAccessibleView extends Disposable implements IAccessib class CommentsAccessibleContentProvider extends Disposable implements IAccessibleViewContentProvider { + public readonly actions: IAction[]; constructor( private readonly _commentsView: CommentsPanel, private readonly _focusedCommentNode: any, private readonly _menus: CommentsMenus, ) { super(); + + this.actions = [...this._menus.getResourceContextActions(this._focusedCommentNode)].filter(i => i.enabled).map(action => { + return { + ...action, + run: () => { + this._commentsView.focus(); + action.run({ + thread: this._focusedCommentNode.thread, + $mid: MarshalledId.CommentThread, + commentControlHandle: this._focusedCommentNode.controllerHandle, + commentThreadHandle: this._focusedCommentNode.threadHandle, + }); + } + }; + }); } readonly id = AccessibleViewProviderId.Comments; readonly verbositySettingKey = AccessibilityVerbositySettingId.Comments; readonly options = { type: AccessibleViewType.View }; - public actions = [...this._menus.getResourceContextActions(this._focusedCommentNode)].filter(i => i.enabled).map(action => { - return { - ...action, - run: () => { - this._commentsView.focus(); - action.run({ - thread: this._focusedCommentNode.thread, - $mid: MarshalledId.CommentThread, - commentControlHandle: this._focusedCommentNode.controllerHandle, - commentThreadHandle: this._focusedCommentNode.threadHandle, - }); - } - }; - }); + provideContent(): string { const commentNode = this._commentsView.focusedCommentNode; const content = this._commentsView.focusedCommentInfo?.toString(); diff --git a/src/vs/workbench/contrib/comments/browser/commentsView.ts b/src/vs/workbench/contrib/comments/browser/commentsView.ts index a78d91f4a7ca..e9dcf952046e 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsView.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsView.ts @@ -21,7 +21,6 @@ import { IContextKey, IContextKeyService, RawContextKey } from '../../../../plat import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js'; import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; import { IOpenerService } from '../../../../platform/opener/common/opener.js'; -import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js'; import { CommentsViewFilterFocusContextKey, ICommentsView } from './comments.js'; import { CommentsFilters, CommentsFiltersChangeEvent, CommentsSortOrder } from './commentsViewActions.js'; @@ -148,7 +147,6 @@ export class CommentsPanel extends FilterViewPane implements ICommentsView { @IOpenerService openerService: IOpenerService, @IThemeService themeService: IThemeService, @ICommentService private readonly commentService: ICommentService, - @ITelemetryService telemetryService: ITelemetryService, @IHoverService hoverService: IHoverService, @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, @IStorageService storageService: IStorageService, @@ -165,7 +163,7 @@ export class CommentsPanel extends FilterViewPane implements ICommentsView { text: viewState['filter'] || '', focusContextKey: CommentsViewFilterFocusContextKey.key } - }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService, hoverService); + }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, hoverService); this.hasCommentsContextKey = CONTEXT_KEY_HAS_COMMENTS.bindTo(contextKeyService); this.someCommentsExpandedContextKey = CONTEXT_KEY_SOME_COMMENTS_EXPANDED.bindTo(contextKeyService); this.commentsFocusedContextKey = CONTEXT_KEY_COMMENT_FOCUSED.bindTo(contextKeyService); diff --git a/src/vs/workbench/contrib/comments/browser/commentsViewActions.ts b/src/vs/workbench/contrib/comments/browser/commentsViewActions.ts index 016ead15476e..41ed56ce07f9 100644 --- a/src/vs/workbench/contrib/comments/browser/commentsViewActions.ts +++ b/src/vs/workbench/contrib/comments/browser/commentsViewActions.ts @@ -44,15 +44,20 @@ export class CommentsFilters extends Disposable { private readonly _onDidChange: Emitter = this._register(new Emitter()); readonly onDidChange: Event = this._onDidChange.event; + private readonly _showUnresolved: IContextKey; + private readonly _showResolved: IContextKey; + private readonly _sortBy: IContextKey; constructor(options: CommentsFiltersOptions, private readonly contextKeyService: IContextKeyService) { super(); + this._showUnresolved = CONTEXT_KEY_SHOW_UNRESOLVED.bindTo(this.contextKeyService); + this._showResolved = CONTEXT_KEY_SHOW_RESOLVED.bindTo(this.contextKeyService); + this._sortBy = CONTEXT_KEY_SORT_BY.bindTo(this.contextKeyService); this._showResolved.set(options.showResolved); this._showUnresolved.set(options.showUnresolved); this._sortBy.set(options.sortBy); } - private readonly _showUnresolved = CONTEXT_KEY_SHOW_UNRESOLVED.bindTo(this.contextKeyService); get showUnresolved(): boolean { return !!this._showUnresolved.get(); } @@ -63,7 +68,6 @@ export class CommentsFilters extends Disposable { } } - private _showResolved = CONTEXT_KEY_SHOW_RESOLVED.bindTo(this.contextKeyService); get showResolved(): boolean { return !!this._showResolved.get(); } @@ -74,7 +78,6 @@ export class CommentsFilters extends Disposable { } } - private _sortBy: IContextKey = CONTEXT_KEY_SORT_BY.bindTo(this.contextKeyService); get sortBy(): CommentsSortOrder { return this._sortBy.get() ?? CommentsSortOrder.ResourceAscending; } diff --git a/src/vs/workbench/contrib/comments/browser/media/panel.css b/src/vs/workbench/contrib/comments/browser/media/panel.css index e6c66b601742..971e079b14ce 100644 --- a/src/vs/workbench/contrib/comments/browser/media/panel.css +++ b/src/vs/workbench/contrib/comments/browser/media/panel.css @@ -105,6 +105,7 @@ .comments-panel .comments-panel-container .tree-container .comment-thread-container .range { opacity: 0.8; + overflow: visible; } .comments-panel .comments-panel-container .tree-container .comment-thread-container .comment-snippet-container .text code { diff --git a/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts b/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts index f3b3fb7f8d75..d246fb9fddae 100644 --- a/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts +++ b/src/vs/workbench/contrib/customEditor/browser/customEditorInput.ts @@ -113,6 +113,7 @@ export class CustomEditorInput extends LazilyResolvedWebviewEditorInput { this._register(this.fileService.onDidChangeFileSystemProviderRegistrations(e => this.onLabelEvent(e.scheme))); this._register(this.fileService.onDidChangeFileSystemProviderCapabilities(e => this.onLabelEvent(e.scheme))); this._register(this.customEditorLabelService.onDidChange(() => this.updateLabel())); + this._register(this.filesConfigurationService.onDidChangeReadonly(() => this._onDidChangeCapabilities.fire())); } private onLabelEvent(scheme: string): void { diff --git a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts index 3847044be077..0965dcaf6361 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts @@ -42,7 +42,6 @@ import { WorkbenchList } from '../../../../platform/list/browser/listService.js' import { INotificationService } from '../../../../platform/notification/common/notification.js'; import { IOpenerService } from '../../../../platform/opener/common/opener.js'; import { IQuickInputService } from '../../../../platform/quickinput/common/quickInput.js'; -import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; import { defaultInputBoxStyles } from '../../../../platform/theme/browser/defaultStyles.js'; import { IThemeService } from '../../../../platform/theme/common/themeService.js'; import { ViewAction, ViewPane } from '../../../browser/parts/views/viewPane.js'; @@ -116,13 +115,12 @@ export class BreakpointsView extends ViewPane { @IViewDescriptorService viewDescriptorService: IViewDescriptorService, @IContextKeyService contextKeyService: IContextKeyService, @IOpenerService openerService: IOpenerService, - @ITelemetryService telemetryService: ITelemetryService, @ILabelService private readonly labelService: ILabelService, @IMenuService menuService: IMenuService, @IHoverService hoverService: IHoverService, @ILanguageService private readonly languageService: ILanguageService, ) { - super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService, hoverService); + super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, hoverService); this.menu = menuService.createMenu(MenuId.DebugBreakpointsContext, contextKeyService); this._register(this.menu); @@ -226,7 +224,7 @@ export class BreakpointsView extends ViewPane { const iconLabelContainer = dom.append(container, $('span.breakpoint-warning')); this.hintContainer = this._register(new IconLabel(iconLabelContainer, { supportIcons: true, hoverDelegate: { - showHover: (options, focus?) => this.hoverService.showHover({ content: options.content, target: this.hintContainer!.element }, focus), + showHover: (options, focus?) => this.hoverService.showInstantHover({ content: options.content, target: this.hintContainer!.element }, focus), delay: this.configurationService.getValue('workbench.hover.delay') } })); diff --git a/src/vs/workbench/contrib/debug/browser/callStackEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/callStackEditorContribution.ts index 9a0a631edd98..cc2513d24a37 100644 --- a/src/vs/workbench/contrib/debug/browser/callStackEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackEditorContribution.ts @@ -9,7 +9,7 @@ import { Disposable } from '../../../../base/common/lifecycle.js'; import { Constants } from '../../../../base/common/uint.js'; import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js'; import { Range } from '../../../../editor/common/core/range.js'; -import { IEditorContribution } from '../../../../editor/common/editorCommon.js'; +import { IEditorContribution, IEditorDecorationsCollection } from '../../../../editor/common/editorCommon.js'; import { GlyphMarginLane, IModelDecorationOptions, IModelDeltaDecoration, OverviewRulerLane, TrackedRangeStickiness } from '../../../../editor/common/model.js'; import { localize } from '../../../../nls.js'; import { ILogService } from '../../../../platform/log/common/log.js'; @@ -116,7 +116,7 @@ export function createDecorationsForStackFrame(stackFrame: IStackFrame, isFocuse } export class CallStackEditorContribution extends Disposable implements IEditorContribution { - private decorations = this.editor.createDecorationsCollection(); + private decorations: IEditorDecorationsCollection; constructor( private readonly editor: ICodeEditor, @@ -125,6 +125,7 @@ export class CallStackEditorContribution extends Disposable implements IEditorCo @ILogService private readonly logService: ILogService, ) { super(); + this.decorations = this.editor.createDecorationsCollection(); const setDecorations = () => this.decorations.set(this.createCallStackDecorations()); this._register(Event.any(this.debugService.getViewModel().onDidFocusStackFrame, this.debugService.getModel().onDidChangeCallStack)(() => { diff --git a/src/vs/workbench/contrib/debug/browser/callStackView.ts b/src/vs/workbench/contrib/debug/browser/callStackView.ts index 4655198264d1..28d99d7646ff 100644 --- a/src/vs/workbench/contrib/debug/browser/callStackView.ts +++ b/src/vs/workbench/contrib/debug/browser/callStackView.ts @@ -34,7 +34,6 @@ import { ILabelService } from '../../../../platform/label/common/label.js'; import { WorkbenchCompressibleAsyncDataTree } from '../../../../platform/list/browser/listService.js'; import { INotificationService } from '../../../../platform/notification/common/notification.js'; import { IOpenerService } from '../../../../platform/opener/common/opener.js'; -import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; import { asCssVariable, textLinkForeground } from '../../../../platform/theme/common/colorRegistry.js'; import { IThemeService } from '../../../../platform/theme/common/themeService.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; @@ -158,11 +157,10 @@ export class CallStackView extends ViewPane { @IContextKeyService contextKeyService: IContextKeyService, @IOpenerService openerService: IOpenerService, @IThemeService themeService: IThemeService, - @ITelemetryService telemetryService: ITelemetryService, @IHoverService hoverService: IHoverService, @IMenuService private readonly menuService: IMenuService, ) { - super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService, hoverService); + super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, hoverService); // Create scheduler to prevent unnecessary flashing of tree when reacting to changes this.onCallStackChangeScheduler = this._register(new RunOnceScheduler(async () => { diff --git a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts index fe281b6c1b14..1c522165c231 100644 --- a/src/vs/workbench/contrib/debug/browser/debug.contribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debug.contribution.ts @@ -7,8 +7,6 @@ import { KeyCode, KeyMod } from '../../../../base/common/keyCodes.js'; import { FileAccess } from '../../../../base/common/network.js'; import { isMacintosh, isWeb } from '../../../../base/common/platform.js'; import { URI } from '../../../../base/common/uri.js'; -import './media/debug.contribution.css'; -import './media/debugHover.css'; import { EditorContributionInstantiation, registerEditorContribution } from '../../../../editor/browser/editorExtensions.js'; import * as nls from '../../../../nls.js'; import { AccessibleViewRegistry } from '../../../../platform/accessibility/browser/accessibleViewRegistry.js'; @@ -18,18 +16,29 @@ import { Extensions as ConfigurationExtensions, ConfigurationScope, IConfigurati import { ContextKeyExpr, ContextKeyExpression } from '../../../../platform/contextkey/common/contextkey.js'; import { SyncDescriptor } from '../../../../platform/instantiation/common/descriptors.js'; import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; +import { KeybindingWeight, KeybindingsRegistry } from '../../../../platform/keybinding/common/keybindingsRegistry.js'; import { IQuickAccessRegistry, Extensions as QuickAccessExtensions } from '../../../../platform/quickinput/common/quickAccess.js'; import { Registry } from '../../../../platform/registry/common/platform.js'; import { EditorPaneDescriptor, IEditorPaneRegistry } from '../../../browser/editor.js'; import { ViewPaneContainer } from '../../../browser/parts/views/viewPaneContainer.js'; -import { IWorkbenchContributionsRegistry, registerWorkbenchContribution2, Extensions as WorkbenchExtensions, WorkbenchPhase } from '../../../common/contributions.js'; +import { FocusedViewContext } from '../../../common/contextkeys.js'; +import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, WorkbenchPhase, registerWorkbenchContribution2 } from '../../../common/contributions.js'; import { EditorExtensions } from '../../../common/editor.js'; import { IViewContainersRegistry, IViewsRegistry, ViewContainer, ViewContainerLocation, Extensions as ViewExtensions } from '../../../common/views.js'; +import { launchSchemaId } from '../../../services/configuration/common/configuration.js'; +import { LifecyclePhase } from '../../../services/lifecycle/common/lifecycle.js'; +import { COPY_NOTEBOOK_VARIABLE_VALUE_ID, COPY_NOTEBOOK_VARIABLE_VALUE_LABEL } from '../../notebook/browser/contrib/notebookVariables/notebookVariableCommands.js'; +import { BREAKPOINTS_VIEW_ID, BREAKPOINT_EDITOR_CONTRIBUTION_ID, CALLSTACK_VIEW_ID, CONTEXT_BREAKPOINTS_EXIST, CONTEXT_BREAK_WHEN_VALUE_CHANGES_SUPPORTED, CONTEXT_BREAK_WHEN_VALUE_IS_ACCESSED_SUPPORTED, CONTEXT_BREAK_WHEN_VALUE_IS_READ_SUPPORTED, CONTEXT_CALLSTACK_ITEM_TYPE, CONTEXT_CAN_VIEW_MEMORY, CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_DEBUG_STATE, CONTEXT_DEBUG_UX, CONTEXT_EXPRESSION_SELECTED, CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_FOCUSED_SESSION_IS_NO_DEBUG, CONTEXT_HAS_DEBUGGED, CONTEXT_IN_DEBUG_MODE, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_RESTART_FRAME_SUPPORTED, CONTEXT_SET_EXPRESSION_SUPPORTED, CONTEXT_SET_VARIABLE_SUPPORTED, CONTEXT_STACK_FRAME_SUPPORTS_RESTART, CONTEXT_STEP_INTO_TARGETS_SUPPORTED, CONTEXT_SUSPEND_DEBUGGEE_SUPPORTED, CONTEXT_TERMINATE_DEBUGGEE_SUPPORTED, CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT, CONTEXT_VARIABLE_IS_READONLY, CONTEXT_VARIABLE_VALUE, CONTEXT_WATCH_ITEM_TYPE, DEBUG_PANEL_ID, DISASSEMBLY_VIEW_ID, EDITOR_CONTRIBUTION_ID, IDebugService, INTERNAL_CONSOLE_OPTIONS_SCHEMA, LOADED_SCRIPTS_VIEW_ID, REPL_VIEW_ID, State, VARIABLES_VIEW_ID, VIEWLET_ID, WATCH_VIEW_ID, getStateLabel } from '../common/debug.js'; +import { DebugWatchAccessibilityAnnouncer } from '../common/debugAccessibilityAnnouncer.js'; +import { DebugContentProvider } from '../common/debugContentProvider.js'; +import { DebugLifecycle } from '../common/debugLifecycle.js'; +import { DebugVisualizerService, IDebugVisualizerService } from '../common/debugVisualizers.js'; +import { DisassemblyViewInput } from '../common/disassemblyViewInput.js'; +import { ReplAccessibilityAnnouncer } from '../common/replAccessibilityAnnouncer.js'; import { BreakpointEditorContribution } from './breakpointEditorContribution.js'; import { BreakpointsView } from './breakpointsView.js'; import { CallStackEditorContribution } from './callStackEditorContribution.js'; import { CallStackView } from './callStackView.js'; -import { ReplAccessibleView } from './replAccessibleView.js'; import { registerColors } from './debugColors.js'; import { ADD_CONFIGURATION_ID, ADD_TO_WATCH_ID, ADD_TO_WATCH_LABEL, CALLSTACK_BOTTOM_ID, CALLSTACK_BOTTOM_LABEL, CALLSTACK_DOWN_ID, CALLSTACK_DOWN_LABEL, CALLSTACK_TOP_ID, CALLSTACK_TOP_LABEL, CALLSTACK_UP_ID, CALLSTACK_UP_LABEL, CONTINUE_ID, CONTINUE_LABEL, COPY_EVALUATE_PATH_ID, COPY_EVALUATE_PATH_LABEL, COPY_STACK_TRACE_ID, COPY_VALUE_ID, COPY_VALUE_LABEL, DEBUG_COMMAND_CATEGORY, DEBUG_CONSOLE_QUICK_ACCESS_PREFIX, DEBUG_QUICK_ACCESS_PREFIX, DEBUG_RUN_COMMAND_ID, DEBUG_RUN_LABEL, DEBUG_START_COMMAND_ID, DEBUG_START_LABEL, DISCONNECT_AND_SUSPEND_ID, DISCONNECT_AND_SUSPEND_LABEL, DISCONNECT_ID, DISCONNECT_LABEL, EDIT_EXPRESSION_COMMAND_ID, JUMP_TO_CURSOR_ID, NEXT_DEBUG_CONSOLE_ID, NEXT_DEBUG_CONSOLE_LABEL, OPEN_LOADED_SCRIPTS_LABEL, PAUSE_ID, PAUSE_LABEL, PREV_DEBUG_CONSOLE_ID, PREV_DEBUG_CONSOLE_LABEL, REMOVE_EXPRESSION_COMMAND_ID, RESTART_FRAME_ID, RESTART_LABEL, RESTART_SESSION_ID, SELECT_AND_START_ID, SELECT_AND_START_LABEL, SELECT_DEBUG_CONSOLE_ID, SELECT_DEBUG_CONSOLE_LABEL, SELECT_DEBUG_SESSION_ID, SELECT_DEBUG_SESSION_LABEL, SET_EXPRESSION_COMMAND_ID, SHOW_LOADED_SCRIPTS_ID, STEP_INTO_ID, STEP_INTO_LABEL, STEP_INTO_TARGET_ID, STEP_INTO_TARGET_LABEL, STEP_OUT_ID, STEP_OUT_LABEL, STEP_OVER_ID, STEP_OVER_LABEL, STOP_ID, STOP_LABEL, TERMINATE_THREAD_ID, TOGGLE_INLINE_BREAKPOINT_ID } from './debugCommands.js'; import { DebugConsoleQuickAccess } from './debugConsoleQuickAccess.js'; @@ -39,32 +48,23 @@ import * as icons from './debugIcons.js'; import { DebugProgressContribution } from './debugProgress.js'; import { StartDebugQuickAccessProvider } from './debugQuickAccess.js'; import { DebugService } from './debugService.js'; +import './debugSettingMigration.js'; import { DebugStatusContribution } from './debugStatus.js'; import { DebugTitleContribution } from './debugTitle.js'; import { DebugToolBar } from './debugToolBar.js'; import { DebugViewPaneContainer } from './debugViewlet.js'; import { DisassemblyView, DisassemblyViewContribution } from './disassemblyView.js'; import { LoadedScriptsView } from './loadedScriptsView.js'; +import './media/debug.contribution.css'; +import './media/debugHover.css'; import { Repl } from './repl.js'; +import { ReplAccessibilityHelp } from './replAccessibilityHelp.js'; +import { ReplAccessibleView } from './replAccessibleView.js'; +import { RunAndDebugAccessibilityHelp } from './runAndDebugAccessibilityHelp.js'; import { StatusBarColorProvider } from './statusbarColorProvider.js'; import { BREAK_WHEN_VALUE_CHANGES_ID, BREAK_WHEN_VALUE_IS_ACCESSED_ID, BREAK_WHEN_VALUE_IS_READ_ID, SET_VARIABLE_ID, VIEW_MEMORY_ID, VariablesView } from './variablesView.js'; import { ADD_WATCH_ID, ADD_WATCH_LABEL, REMOVE_WATCH_EXPRESSIONS_COMMAND_ID, REMOVE_WATCH_EXPRESSIONS_LABEL, WatchExpressionsView } from './watchExpressionsView.js'; import { WelcomeView } from './welcomeView.js'; -import { BREAKPOINTS_VIEW_ID, BREAKPOINT_EDITOR_CONTRIBUTION_ID, CALLSTACK_VIEW_ID, CONTEXT_BREAKPOINTS_EXIST, CONTEXT_BREAK_WHEN_VALUE_CHANGES_SUPPORTED, CONTEXT_BREAK_WHEN_VALUE_IS_ACCESSED_SUPPORTED, CONTEXT_BREAK_WHEN_VALUE_IS_READ_SUPPORTED, CONTEXT_CALLSTACK_ITEM_TYPE, CONTEXT_CAN_VIEW_MEMORY, CONTEXT_DEBUGGERS_AVAILABLE, CONTEXT_DEBUG_STATE, CONTEXT_DEBUG_UX, CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_FOCUSED_SESSION_IS_NO_DEBUG, CONTEXT_HAS_DEBUGGED, CONTEXT_IN_DEBUG_MODE, CONTEXT_JUMP_TO_CURSOR_SUPPORTED, CONTEXT_LOADED_SCRIPTS_SUPPORTED, CONTEXT_RESTART_FRAME_SUPPORTED, CONTEXT_SET_EXPRESSION_SUPPORTED, CONTEXT_SET_VARIABLE_SUPPORTED, CONTEXT_STACK_FRAME_SUPPORTS_RESTART, CONTEXT_STEP_INTO_TARGETS_SUPPORTED, CONTEXT_SUSPEND_DEBUGGEE_SUPPORTED, CONTEXT_TERMINATE_DEBUGGEE_SUPPORTED, CONTEXT_VARIABLE_EVALUATE_NAME_PRESENT, CONTEXT_VARIABLE_IS_READONLY, CONTEXT_VARIABLE_VALUE, CONTEXT_WATCH_ITEM_TYPE, DEBUG_PANEL_ID, DISASSEMBLY_VIEW_ID, EDITOR_CONTRIBUTION_ID, IDebugService, INTERNAL_CONSOLE_OPTIONS_SCHEMA, LOADED_SCRIPTS_VIEW_ID, REPL_VIEW_ID, State, VARIABLES_VIEW_ID, VIEWLET_ID, WATCH_VIEW_ID, getStateLabel } from '../common/debug.js'; -import { DebugContentProvider } from '../common/debugContentProvider.js'; -import { DebugLifecycle } from '../common/debugLifecycle.js'; -import { DebugVisualizerService, IDebugVisualizerService } from '../common/debugVisualizers.js'; -import { DisassemblyViewInput } from '../common/disassemblyViewInput.js'; -import { COPY_NOTEBOOK_VARIABLE_VALUE_ID, COPY_NOTEBOOK_VARIABLE_VALUE_LABEL } from '../../notebook/browser/contrib/notebookVariables/notebookVariableCommands.js'; -import { launchSchemaId } from '../../../services/configuration/common/configuration.js'; -import { LifecyclePhase } from '../../../services/lifecycle/common/lifecycle.js'; -import './debugSettingMigration.js'; -import { ReplAccessibilityHelp } from './replAccessibilityHelp.js'; -import { ReplAccessibilityAnnouncer } from '../common/replAccessibilityAnnouncer.js'; -import { RunAndDebugAccessibilityHelp } from './runAndDebugAccessibilityHelp.js'; -import { DebugWatchAccessibilityAnnouncer } from '../common/debugAccessibilityAnnouncer.js'; -import { KeybindingsRegistry, KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js'; -import { FocusedViewContext } from '../../../common/contextkeys.js'; const debugCategory = nls.localize('debugCategory', "Debug"); registerColors(); @@ -210,9 +210,12 @@ registerDebugViewMenuItem(MenuId.NotebookVariablesContext, COPY_NOTEBOOK_VARIABL KeybindingsRegistry.registerKeybindingRule({ id: COPY_VALUE_ID, weight: KeybindingWeight.WorkbenchContrib, - when: ContextKeyExpr.or( - FocusedViewContext.isEqualTo(WATCH_VIEW_ID), - FocusedViewContext.isEqualTo(VARIABLES_VIEW_ID), + when: ContextKeyExpr.and( + CONTEXT_EXPRESSION_SELECTED.negate(), + ContextKeyExpr.or( + FocusedViewContext.isEqualTo(WATCH_VIEW_ID), + FocusedViewContext.isEqualTo(VARIABLES_VIEW_ID), + ), ), primary: KeyMod.CtrlCmd | KeyCode.KeyC }); diff --git a/src/vs/workbench/contrib/debug/browser/debugANSIHandling.ts b/src/vs/workbench/contrib/debug/browser/debugANSIHandling.ts index 4db2d7eb8c77..455682400498 100644 --- a/src/vs/workbench/contrib/debug/browser/debugANSIHandling.ts +++ b/src/vs/workbench/contrib/debug/browser/debugANSIHandling.ts @@ -71,7 +71,7 @@ export function handleANSIOutput(text: string, linkDetector: ILinkDetector, work * Certain ranges that are matched here do not contain real graphics rendition sequences. For * the sake of having a simpler expression, they have been included anyway. */ - if (ansiSequence.match(/^(?:[34][0-8]|9[0-7]|10[0-7]|[0-9]|2[1-5,7-9]|[34]9|5[8,9]|1[0-9])(?:;[349][0-7]|10[0-7]|[013]|[245]|[34]9)?(?:;[012]?[0-9]?[0-9])*;?m$/)) { + if (ansiSequence.match(/^(?:[34][0-8]|9[0-7]|10[0-7]|[0-9]|2[1-5,7-9]|[34]9|5[8,9]|1[0-9])(?:;[349][0-7]|10[0-7]|[013]|[245]|[34]9)?(?:;[0-9]{1,3})*;?m$/)) { const styleCodes: number[] = ansiSequence.slice(0, -1) // Remove final 'm' character. .split(';') // Separate style codes. diff --git a/src/vs/workbench/contrib/debug/browser/debugCommands.ts b/src/vs/workbench/contrib/debug/browser/debugCommands.ts index 2d3cb0c46c83..92b79ee572be 100644 --- a/src/vs/workbench/contrib/debug/browser/debugCommands.ts +++ b/src/vs/workbench/contrib/debug/browser/debugCommands.ts @@ -66,6 +66,7 @@ export const DEBUG_CONFIGURE_COMMAND_ID = 'workbench.action.debug.configure'; export const DEBUG_START_COMMAND_ID = 'workbench.action.debug.start'; export const DEBUG_RUN_COMMAND_ID = 'workbench.action.debug.run'; export const EDIT_EXPRESSION_COMMAND_ID = 'debug.renameWatchExpression'; +export const COPY_WATCH_EXPRESSION_COMMAND_ID = 'debug.copyWatchExpression'; export const SET_EXPRESSION_COMMAND_ID = 'debug.setWatchExpression'; export const REMOVE_EXPRESSION_COMMAND_ID = 'debug.removeWatchExpression'; export const NEXT_DEBUG_CONSOLE_ID = 'workbench.action.debug.nextConsole'; diff --git a/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts index 277b6a907af3..285e99c61f97 100644 --- a/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts +++ b/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts @@ -30,7 +30,7 @@ import { EditOperation } from '../../../../editor/common/core/editOperation.js'; import { Position } from '../../../../editor/common/core/position.js'; import { IRange, Range } from '../../../../editor/common/core/range.js'; import { DEFAULT_WORD_REGEXP } from '../../../../editor/common/core/wordHelper.js'; -import { ScrollType } from '../../../../editor/common/editorCommon.js'; +import { IEditorDecorationsCollection, ScrollType } from '../../../../editor/common/editorCommon.js'; import { StandardTokenType } from '../../../../editor/common/encodedTokenAttributes.js'; import { InlineValue, InlineValueContext } from '../../../../editor/common/languages.js'; import { IModelDeltaDecoration, ITextModel, InjectedTextCursorStops } from '../../../../editor/common/model.js'; @@ -52,6 +52,7 @@ import { ExceptionWidget } from './exceptionWidget.js'; import { CONTEXT_EXCEPTION_WIDGET_VISIBLE, IDebugConfiguration, IDebugEditorContribution, IDebugService, IDebugSession, IExceptionInfo, IExpression, IStackFrame, State } from '../common/debug.js'; import { Expression } from '../common/debugModel.js'; import { IHostService } from '../../../services/host/browser/host.js'; +import { MarkdownString } from '../../../../base/common/htmlContent.js'; const MAX_NUM_INLINE_VALUES = 100; // JS Global scope can have 700+ entries. We want to limit ourselves for perf reasons const MAX_INLINE_DECORATOR_LENGTH = 150; // Max string length of each inline decorator when debugging. If exceeded ... is added @@ -73,10 +74,60 @@ class InlineSegment { } } -function createInlineValueDecoration(lineNumber: number, contentText: string, column = Constants.MAX_SAFE_SMALL_INTEGER): IModelDeltaDecoration[] { - // If decoratorText is too long, trim and add ellipses. This could happen for minified files with everything on a single line - if (contentText.length > MAX_INLINE_DECORATOR_LENGTH) { - contentText = contentText.substring(0, MAX_INLINE_DECORATOR_LENGTH) + '...'; +export function formatHoverContent(contentText: string): MarkdownString { + if (contentText.includes(',') && contentText.includes('=')) { + // Custom split: for each equals sign after the first, backtrack to the nearest comma + const customSplit = (text: string): string[] => { + const splits: number[] = []; + let equalsFound = 0; + let start = 0; + for (let i = 0; i < text.length; i++) { + if (text[i] === '=') { + if (equalsFound === 0) { + equalsFound++; + continue; + } + const commaIndex = text.lastIndexOf(',', i); + if (commaIndex !== -1 && commaIndex >= start) { + splits.push(commaIndex); + start = commaIndex + 1; + } + equalsFound++; + } + } + const result: string[] = []; + let s = 0; + for (const index of splits) { + result.push(text.substring(s, index).trim()); + s = index + 1; + } + if (s < text.length) { + result.push(text.substring(s).trim()); + } + return result; + }; + + const pairs = customSplit(contentText); + const formattedPairs = pairs.map(pair => { + const equalsIndex = pair.indexOf('='); + if (equalsIndex !== -1) { + const indent = ' '.repeat(equalsIndex + 2); + const [firstLine, ...restLines] = pair.split(/\r?\n/); + return [firstLine, ...restLines.map(line => indent + line)].join('\n'); + } + return pair; + }); + return new MarkdownString().appendCodeblock('', formattedPairs.join(',\n')); + } + return new MarkdownString().appendCodeblock('', contentText); +} + +export function createInlineValueDecoration(lineNumber: number, contentText: string, classNamePrefix: string, column = Constants.MAX_SAFE_SMALL_INTEGER, viewportMaxCol: number = MAX_INLINE_DECORATOR_LENGTH): IModelDeltaDecoration[] { + const rawText = contentText; // store raw text for hover message + + // Truncate contentText if it exceeds the viewport max column + if (contentText.length > viewportMaxCol) { + contentText = contentText.substring(0, viewportMaxCol) + '...'; } return [ @@ -88,7 +139,7 @@ function createInlineValueDecoration(lineNumber: number, contentText: string, co endColumn: column }, options: { - description: 'debug-inline-value-decoration-spacer', + description: `${classNamePrefix}-inline-value-decoration-spacer`, after: { content: strings.noBreakWhitespace, cursorStops: InjectedTextCursorStops.None @@ -104,14 +155,15 @@ function createInlineValueDecoration(lineNumber: number, contentText: string, co endColumn: column }, options: { - description: 'debug-inline-value-decoration', + description: `${classNamePrefix}-inline-value-decoration`, after: { content: replaceWsWithNoBreakWs(contentText), - inlineClassName: 'debug-inline-value', + inlineClassName: `${classNamePrefix}-inline-value`, inlineClassNameAffectsLetterSpacing: true, cursorStops: InjectedTextCursorStops.None }, showIfCollapsed: true, + hoverMessage: formatHoverContent(rawText) } }, ]; @@ -209,7 +261,7 @@ export class DebugEditorContribution implements IDebugEditorContribution { private configurationWidget: FloatingEditorClickWidget | undefined; private readonly altListener = new MutableDisposable(); private altPressed = false; - private oldDecorations = this.editor.createDecorationsCollection(); + private oldDecorations: IEditorDecorationsCollection; private readonly displayedStore = new DisposableStore(); private editorHoverOptions: IEditorHoverOptions | undefined; private readonly debounceInfo: IFeatureDebounceInformation; @@ -229,6 +281,7 @@ export class DebugEditorContribution implements IDebugEditorContribution { @ILanguageFeaturesService private readonly languageFeaturesService: ILanguageFeaturesService, @ILanguageFeatureDebounceService featureDebounceService: ILanguageFeatureDebounceService ) { + this.oldDecorations = this.editor.createDecorationsCollection(); this.debounceInfo = featureDebounceService.for(languageFeaturesService.inlineValuesProvider, 'InlineValues', { min: DEAFULT_INLINE_DEBOUNCE_DELAY }); this.hoverWidget = this.instantiationService.createInstance(DebugHoverWidget, this.editor); this.toDispose = [this.defaultHoverLockout, this.altListener, this.displayedStore]; @@ -769,7 +822,10 @@ export class DebugEditorContribution implements IDebugEditorContribution { if (segments.length > 0) { segments = segments.sort((a, b) => a.column - b.column); const text = segments.map(s => s.text).join(separator); - allDecorations.push(...createInlineValueDecoration(line, text)); + const editorWidth = this.editor.getLayoutInfo().width; + const fontInfo = this.editor.getOption(EditorOption.fontInfo); + const viewportMaxCol = Math.floor((editorWidth - 50) / fontInfo.typicalHalfwidthCharacterWidth); + allDecorations.push(...createInlineValueDecoration(line, text, 'debug', undefined, viewportMaxCol)); } }); @@ -813,9 +869,13 @@ export class DebugEditorContribution implements IDebugEditorContribution { } } - allDecorations = [...valuesPerLine.entries()].flatMap(([line, values]) => - createInlineValueDecoration(line, [...values].map(([n, v]) => `${n} = ${v}`).join(', ')) - ); + allDecorations = [...valuesPerLine.entries()].flatMap(([line, values]) => { + const text = [...values].map(([n, v]) => `${n} = ${v}`).join(', '); + const editorWidth = this.editor.getLayoutInfo().width; + const fontInfo = this.editor.getOption(EditorOption.fontInfo); + const viewportMaxCol = Math.floor((editorWidth - 50) / fontInfo.typicalHalfwidthCharacterWidth); + return createInlineValueDecoration(line, text, 'debug', undefined, viewportMaxCol); + }); } if (cts.token.isCancellationRequested) { diff --git a/src/vs/workbench/contrib/debug/browser/debugHover.ts b/src/vs/workbench/contrib/debug/browser/debugHover.ts index ddd5e8bdbc68..2d2a3ea5f63f 100644 --- a/src/vs/workbench/contrib/debug/browser/debugHover.ts +++ b/src/vs/workbench/contrib/debug/browser/debugHover.ts @@ -23,6 +23,7 @@ import { ConfigurationChangedEvent, EditorOption } from '../../../../editor/comm import { IDimension } from '../../../../editor/common/core/dimension.js'; import { Position } from '../../../../editor/common/core/position.js'; import { Range } from '../../../../editor/common/core/range.js'; +import { IEditorDecorationsCollection } from '../../../../editor/common/editorCommon.js'; import { ModelDecorationOptions } from '../../../../editor/common/model/textModel.js'; import { ILanguageFeaturesService } from '../../../../editor/common/services/languageFeatures.js'; import * as nls from '../../../../nls.js'; @@ -93,7 +94,7 @@ export class DebugHoverWidget implements IContentWidget { private tree!: AsyncDataTree; private showAtPosition: Position | null; private positionPreference: ContentWidgetPositionPreference[]; - private readonly highlightDecorations = this.editor.createDecorationsCollection(); + private readonly highlightDecorations: IEditorDecorationsCollection; private complexValueContainer!: HTMLElement; private complexValueTitle!: HTMLElement; private valueContainer!: HTMLElement; @@ -118,6 +119,7 @@ export class DebugHoverWidget implements IContentWidget { @IContextKeyService private readonly contextKeyService: IContextKeyService, @IContextMenuService private readonly contextMenuService: IContextMenuService, ) { + this.highlightDecorations = this.editor.createDecorationsCollection(); this.toDispose = []; this.showAtPosition = null; diff --git a/src/vs/workbench/contrib/debug/browser/debugService.ts b/src/vs/workbench/contrib/debug/browser/debugService.ts index 879fc9155249..9a359f4f9d8a 100644 --- a/src/vs/workbench/contrib/debug/browser/debugService.ts +++ b/src/vs/workbench/contrib/debug/browser/debugService.ts @@ -867,7 +867,7 @@ export class DebugService implements IDebugService { // the session, then start the test run again; tests have no notion of restarts. if (session.correlatedTestRun) { if (!session.correlatedTestRun.completedAt) { - this.testService.cancelTestRun(session.correlatedTestRun.id); + session.cancelCorrelatedTestRun(); await Event.toPromise(session.correlatedTestRun.onComplete); // todo@connor4312 is there any reason to wait for the debug session to // terminate? I don't think so, test extension should already handle any diff --git a/src/vs/workbench/contrib/debug/browser/debugSession.ts b/src/vs/workbench/contrib/debug/browser/debugSession.ts index 107ead5e51a0..3e63e4891679 100644 --- a/src/vs/workbench/contrib/debug/browser/debugSession.ts +++ b/src/vs/workbench/contrib/debug/browser/debugSession.ts @@ -402,6 +402,16 @@ export class DebugSession implements IDebugSession, IDisposable { } } + /** + * Terminate any linked test run. + */ + cancelCorrelatedTestRun() { + if (this.correlatedTestRun && !this.correlatedTestRun.completedAt) { + this.didTerminateTestRun = true; + this.testService.cancelTestRun(this.correlatedTestRun.id); + } + } + /** * terminate the current debug adapter session */ @@ -415,8 +425,7 @@ export class DebugSession implements IDebugSession, IDisposable { if (this._options.lifecycleManagedByParent && this.parentSession) { await this.parentSession.terminate(restart); } else if (this.correlatedTestRun && !this.correlatedTestRun.completedAt && !this.didTerminateTestRun) { - this.didTerminateTestRun = true; - this.testService.cancelTestRun(this.correlatedTestRun.id); + this.cancelCorrelatedTestRun(); } else if (this.raw) { if (this.raw.capabilities.supportsTerminateRequest && this._configuration.resolved.request === 'launch') { await this.raw.terminate(restart); diff --git a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts index 41f28eabfc27..b9ea3a411785 100644 --- a/src/vs/workbench/contrib/debug/browser/debugToolBar.ts +++ b/src/vs/workbench/contrib/debug/browser/debugToolBar.ts @@ -32,7 +32,7 @@ import { IStorageService, StorageScope, StorageTarget } from '../../../../platfo import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; import { widgetBorder, widgetShadow } from '../../../../platform/theme/common/colorRegistry.js'; import { IThemeService, Themable } from '../../../../platform/theme/common/themeService.js'; -import { getTitleBarStyle, TitlebarStyle } from '../../../../platform/window/common/window.js'; +import { getWindowControlsStyle, WindowControlsStyle } from '../../../../platform/window/common/window.js'; import { IWorkbenchContribution } from '../../../common/contributions.js'; import { EditorTabsMode, IWorkbenchLayoutService, LayoutSettings, Parts } from '../../../services/layout/browser/layoutService.js'; import { CONTEXT_DEBUG_STATE, CONTEXT_FOCUSED_SESSION_IS_ATTACH, CONTEXT_FOCUSED_SESSION_IS_NO_DEBUG, CONTEXT_IN_DEBUG_MODE, CONTEXT_MULTI_SESSION_DEBUG, CONTEXT_STEP_BACK_SUPPORTED, CONTEXT_SUSPEND_DEBUGGEE_SUPPORTED, CONTEXT_TERMINATE_DEBUGGEE_SUPPORTED, IDebugConfiguration, IDebugService, State, VIEWLET_ID } from '../common/debug.js'; @@ -80,12 +80,12 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution { this.$el = dom.$('div.debug-toolbar'); // Note: changes to this setting require a restart, so no need to listen to it. - const customTitleBar = getTitleBarStyle(this.configurationService) === TitlebarStyle.CUSTOM; + const customWindowControls = getWindowControlsStyle(this.configurationService) === WindowControlsStyle.CUSTOM; // Do not allow the widget to overflow or underflow window controls. // Use CSS calculations to avoid having to force layout with `.clientWidth` - const controlsOnLeft = customTitleBar && platform === Platform.Mac; - const controlsOnRight = customTitleBar && (platform === Platform.Windows || platform === Platform.Linux); + const controlsOnLeft = customWindowControls && platform === Platform.Mac; + const controlsOnRight = customWindowControls && (platform === Platform.Windows || platform === Platform.Linux); this.$el.style.transform = `translate( min( max(${controlsOnLeft ? '60px' : '0px'}, calc(-50% + (100vw * var(--x-position)))), @@ -94,8 +94,6 @@ export class DebugToolBar extends Themable implements IWorkbenchContribution { var(--y-position) )`; - - this.dragArea = dom.append(this.$el, dom.$('div.drag-area' + ThemeIcon.asCSSSelector(icons.debugGripper))); const actionBarContainer = dom.append(this.$el, dom.$('div.action-bar-container')); diff --git a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts index c98e35153df9..95c740dcaa6a 100644 --- a/src/vs/workbench/contrib/debug/browser/disassemblyView.ts +++ b/src/vs/workbench/contrib/debug/browser/disassemblyView.ts @@ -775,8 +775,8 @@ class InstructionRenderer extends Disposable implements ITableRenderer { - this._topStackFrameColor = e.getColor(topStackFrameColor); - this._focusedStackFrameColor = e.getColor(focusedStackFrameColor); + this._topStackFrameColor = e.theme.getColor(topStackFrameColor); + this._focusedStackFrameColor = e.theme.getColor(focusedStackFrameColor); })); } diff --git a/src/vs/workbench/contrib/debug/browser/exceptionWidget.ts b/src/vs/workbench/contrib/debug/browser/exceptionWidget.ts index 35479f2cef73..9f8d26570ede 100644 --- a/src/vs/workbench/contrib/debug/browser/exceptionWidget.ts +++ b/src/vs/workbench/contrib/debug/browser/exceptionWidget.ts @@ -10,7 +10,7 @@ import { ZoneWidget } from '../../../../editor/contrib/zoneWidget/browser/zoneWi import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js'; import { IExceptionInfo, IDebugSession, IDebugEditorContribution, EDITOR_CONTRIBUTION_ID } from '../common/debug.js'; import { RunOnceScheduler } from '../../../../base/common/async.js'; -import { IThemeService, IColorTheme } from '../../../../platform/theme/common/themeService.js'; +import { IThemeService, IColorTheme, IThemeChangeEvent } from '../../../../platform/theme/common/themeService.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; import { Color } from '../../../../base/common/color.js'; import { registerColor } from '../../../../platform/theme/common/colorRegistry.js'; @@ -41,7 +41,7 @@ export class ExceptionWidget extends ZoneWidget { super(editor, { showFrame: true, showArrow: true, isAccessible: true, frameWidth: 1, className: 'exception-widget-container' }); this.applyTheme(themeService.getColorTheme()); - this._disposables.add(themeService.onDidColorThemeChange(this.applyTheme.bind(this))); + this._disposables.add(themeService.onDidColorThemeChange(this.onDidColorThemeChange.bind(this))); this.create(); const onDidLayoutChangeScheduler = new RunOnceScheduler(() => this._doLayout(undefined, undefined), 50); @@ -49,6 +49,10 @@ export class ExceptionWidget extends ZoneWidget { this._disposables.add(onDidLayoutChangeScheduler); } + private onDidColorThemeChange(e: IThemeChangeEvent): void { + this.applyTheme(e.theme); + } + private applyTheme(theme: IColorTheme): void { this.backgroundColor = theme.getColor(debugExceptionWidgetBackground); const frameColor = theme.getColor(debugExceptionWidgetBorder); diff --git a/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts b/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts index 4876cc3a7977..daf85e275b12 100644 --- a/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts +++ b/src/vs/workbench/contrib/debug/browser/loadedScriptsView.ts @@ -30,7 +30,6 @@ import { IKeybindingService } from '../../../../platform/keybinding/common/keybi import { ILabelService } from '../../../../platform/label/common/label.js'; import { WorkbenchCompressibleObjectTree } from '../../../../platform/list/browser/listService.js'; import { IOpenerService } from '../../../../platform/opener/common/opener.js'; -import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; import { IFileIconTheme, IThemeService } from '../../../../platform/theme/common/themeService.js'; import { IWorkspaceContextService, IWorkspaceFolder } from '../../../../platform/workspace/common/workspace.js'; import { IResourceLabel, IResourceLabelOptions, IResourceLabelProps, ResourceLabels } from '../../../browser/labels.js'; @@ -437,10 +436,9 @@ export class LoadedScriptsView extends ViewPane { @IPathService private readonly pathService: IPathService, @IOpenerService openerService: IOpenerService, @IThemeService themeService: IThemeService, - @ITelemetryService telemetryService: ITelemetryService, @IHoverService hoverService: IHoverService, ) { - super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService, hoverService); + super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, hoverService); this.loadedScriptsItemType = CONTEXT_LOADED_SCRIPTS_ITEM_TYPE.bindTo(contextKeyService); } diff --git a/src/vs/workbench/contrib/debug/browser/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index 0e8672cdf642..930812de0f99 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -56,7 +56,6 @@ import { WorkbenchAsyncDataTree } from '../../../../platform/list/browser/listSe import { ILogService } from '../../../../platform/log/common/log.js'; import { IOpenerService } from '../../../../platform/opener/common/opener.js'; import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; -import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; import { editorForeground, resolveColorValue } from '../../../../platform/theme/common/colorRegistry.js'; import { IThemeService } from '../../../../platform/theme/common/themeService.js'; import { registerNavigableContainer } from '../../../browser/actions/widgetNavigationCommands.js'; @@ -139,7 +138,6 @@ export class Repl extends FilterViewPane implements IHistoryNavigationWidget { @IEditorService private readonly editorService: IEditorService, @IKeybindingService protected override readonly keybindingService: IKeybindingService, @IOpenerService openerService: IOpenerService, - @ITelemetryService telemetryService: ITelemetryService, @IHoverService hoverService: IHoverService, @IMenuService menuService: IMenuService, @ILanguageFeaturesService private readonly languageFeaturesService: ILanguageFeaturesService, @@ -153,11 +151,11 @@ export class Repl extends FilterViewPane implements IHistoryNavigationWidget { text: filterText, history: JSON.parse(storageService.get(FILTER_HISTORY_STORAGE_KEY, StorageScope.WORKSPACE, '[]')) as string[], } - }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService, hoverService); + }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, hoverService); this.menu = menuService.createMenu(MenuId.DebugConsoleContext, contextKeyService); this._register(this.menu); - this.history = new HistoryNavigator(new Set(JSON.parse(this.storageService.get(HISTORY_STORAGE_KEY, StorageScope.WORKSPACE, '[]'))), 100); + this.history = this._register(new HistoryNavigator(new Set(JSON.parse(this.storageService.get(HISTORY_STORAGE_KEY, StorageScope.WORKSPACE, '[]'))), 100)); this.filter = new ReplFilter(); this.filter.filterQuery = filterText; this.multiSessionRepl = CONTEXT_MULTI_SESSION_REPL.bindTo(contextKeyService); @@ -175,9 +173,7 @@ export class Repl extends FilterViewPane implements IHistoryNavigationWidget { } this._register(this.debugService.getViewModel().onDidFocusSession(session => { - if (this.isVisible()) { - this.onDidFocusSession(session); - } + this.onDidFocusSession(session); })); this._register(this.debugService.getViewModel().onDidEvaluateLazyExpression(async e => { if (e instanceof Variable && this.tree?.hasNode(e)) { @@ -215,8 +211,6 @@ export class Repl extends FilterViewPane implements IHistoryNavigationWidget { const focusedSession = this.debugService.getViewModel().focusedSession; if (this.tree && this.tree.getInput() !== focusedSession) { this.onDidFocusSession(focusedSession); - } else { - this.selectSession(); } this.setMode(); diff --git a/src/vs/workbench/contrib/debug/browser/variablesView.ts b/src/vs/workbench/contrib/debug/browser/variablesView.ts index 74c22d9ee976..d578ce13fb41 100644 --- a/src/vs/workbench/contrib/debug/browser/variablesView.ts +++ b/src/vs/workbench/contrib/debug/browser/variablesView.ts @@ -85,11 +85,10 @@ export class VariablesView extends ViewPane implements IDebugViewWithVariables { @IContextKeyService contextKeyService: IContextKeyService, @IOpenerService openerService: IOpenerService, @IThemeService themeService: IThemeService, - @ITelemetryService telemetryService: ITelemetryService, @IHoverService hoverService: IHoverService, @IMenuService private readonly menuService: IMenuService ) { - super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService, hoverService); + super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, hoverService); // Use scheduler to prevent unnecessary flashing this.updateTreeScheduler = new RunOnceScheduler(async () => { diff --git a/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts b/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts index a56d4aae4623..55398607cd36 100644 --- a/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts +++ b/src/vs/workbench/contrib/debug/browser/watchExpressionsView.ts @@ -13,25 +13,29 @@ import { ITreeContextMenuEvent, ITreeDragAndDrop, ITreeDragOverReaction, ITreeMo import { RunOnceScheduler } from '../../../../base/common/async.js'; import { Codicon } from '../../../../base/common/codicons.js'; import { FuzzyScore } from '../../../../base/common/filters.js'; +import { KeyCode, KeyMod } from '../../../../base/common/keyCodes.js'; import { localize } from '../../../../nls.js'; import { getContextMenuActions, getFlatContextMenuActions } from '../../../../platform/actions/browser/menuEntryActionViewItem.js'; import { Action2, IMenu, IMenuService, MenuId, registerAction2 } from '../../../../platform/actions/common/actions.js'; +import { IClipboardService } from '../../../../platform/clipboard/common/clipboardService.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { ContextKeyExpr, IContextKey, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { IContextMenuService, IContextViewService } from '../../../../platform/contextview/browser/contextView.js'; import { IHoverService } from '../../../../platform/hover/browser/hover.js'; import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; +import { KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js'; import { WorkbenchAsyncDataTree } from '../../../../platform/list/browser/listService.js'; import { IOpenerService } from '../../../../platform/opener/common/opener.js'; -import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; import { IThemeService } from '../../../../platform/theme/common/themeService.js'; import { ViewAction, ViewPane } from '../../../browser/parts/views/viewPane.js'; import { IViewletViewOptions } from '../../../browser/parts/views/viewsViewlet.js'; +import { FocusedViewContext } from '../../../common/contextkeys.js'; import { IViewDescriptorService } from '../../../common/views.js'; -import { CONTEXT_CAN_VIEW_MEMORY, CONTEXT_VARIABLE_IS_READONLY, CONTEXT_WATCH_EXPRESSIONS_EXIST, CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_WATCH_ITEM_TYPE, IDebugConfiguration, IDebugService, IDebugViewWithVariables, IExpression, WATCH_VIEW_ID } from '../common/debug.js'; +import { CONTEXT_CAN_VIEW_MEMORY, CONTEXT_EXPRESSION_SELECTED, CONTEXT_VARIABLE_IS_READONLY, CONTEXT_WATCH_EXPRESSIONS_EXIST, CONTEXT_WATCH_EXPRESSIONS_FOCUSED, CONTEXT_WATCH_ITEM_TYPE, IDebugConfiguration, IDebugService, IDebugViewWithVariables, IExpression, WATCH_VIEW_ID } from '../common/debug.js'; import { Expression, Variable, VisualizedExpression } from '../common/debugModel.js'; import { AbstractExpressionDataSource, AbstractExpressionsRenderer, expressionAndScopeLabelProvider, IExpressionTemplateData, IInputBoxOptions, renderViewTree } from './baseDebugView.js'; +import { COPY_WATCH_EXPRESSION_COMMAND_ID } from './debugCommands.js'; import { DebugExpressionRenderer } from './debugExpressionRenderer.js'; import { watchExpressionsAdd, watchExpressionsRemoveAll } from './debugIcons.js'; import { VariablesRenderer, VisualizedVariableRenderer } from './variablesView.js'; @@ -66,11 +70,10 @@ export class WatchExpressionsView extends ViewPane implements IDebugViewWithVari @IContextKeyService contextKeyService: IContextKeyService, @IOpenerService openerService: IOpenerService, @IThemeService themeService: IThemeService, - @ITelemetryService telemetryService: ITelemetryService, @IHoverService hoverService: IHoverService, @IMenuService menuService: IMenuService ) { - super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService, hoverService); + super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, hoverService); this.menu = menuService.createMenu(MenuId.DebugWatchContext, contextKeyService); this._register(this.menu); @@ -572,3 +575,39 @@ registerAction2(class RemoveAllWatchExpressionsAction extends Action2 { debugService.removeWatchExpressions(); } }); + +registerAction2(class CopyExpression extends ViewAction { + constructor() { + super({ + id: COPY_WATCH_EXPRESSION_COMMAND_ID, + title: localize('copyWatchExpression', "Copy Expression"), + f1: false, + viewId: WATCH_VIEW_ID, + precondition: CONTEXT_WATCH_EXPRESSIONS_EXIST, + keybinding: { + primary: KeyMod.CtrlCmd | KeyMod.Alt | KeyCode.KeyC, + weight: KeybindingWeight.WorkbenchContrib, + when: ContextKeyExpr.and( + FocusedViewContext.isEqualTo(WATCH_VIEW_ID), + CONTEXT_EXPRESSION_SELECTED.negate(), + ), + }, + menu: { + id: MenuId.DebugWatchContext, + order: 20, + group: '3_modification', + when: CONTEXT_WATCH_ITEM_TYPE.isEqualTo('expression') + } + }); + } + + runInView(accessor: ServicesAccessor, view: WatchExpressionsView, value?: IExpression): void { + const clipboardService = accessor.get(IClipboardService); + if (!value) { + value = view.treeSelection.at(-1); + } + if (value) { + clipboardService.writeText(value.name); + } + } +}); diff --git a/src/vs/workbench/contrib/debug/browser/welcomeView.ts b/src/vs/workbench/contrib/debug/browser/welcomeView.ts index f1ca97ff5e6d..ebb161fc785f 100644 --- a/src/vs/workbench/contrib/debug/browser/welcomeView.ts +++ b/src/vs/workbench/contrib/debug/browser/welcomeView.ts @@ -17,7 +17,6 @@ import { IKeybindingService } from '../../../../platform/keybinding/common/keybi import { IOpenerService } from '../../../../platform/opener/common/opener.js'; import { Registry } from '../../../../platform/registry/common/platform.js'; import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; -import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; import { IThemeService } from '../../../../platform/theme/common/themeService.js'; import { OpenFileAction, OpenFileFolderAction, OpenFolderAction } from '../../../browser/actions/workspaceActions.js'; import { ViewPane } from '../../../browser/parts/views/viewPane.js'; @@ -53,10 +52,9 @@ export class WelcomeView extends ViewPane { @IViewDescriptorService viewDescriptorService: IViewDescriptorService, @IOpenerService openerService: IOpenerService, @IStorageService storageSevice: IStorageService, - @ITelemetryService telemetryService: ITelemetryService, @IHoverService hoverService: IHoverService, ) { - super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService, hoverService); + super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, hoverService); this.debugStartLanguageContext = CONTEXT_DEBUG_START_LANGUAGE.bindTo(contextKeyService); this.debuggerInterestedContext = CONTEXT_DEBUGGER_INTERESTED_IN_ACTIVE_EDITOR.bindTo(contextKeyService); diff --git a/src/vs/workbench/contrib/debug/common/debug.ts b/src/vs/workbench/contrib/debug/common/debug.ts index 8df2579316ae..6ce5e3e38a93 100644 --- a/src/vs/workbench/contrib/debug/common/debug.ts +++ b/src/vs/workbench/contrib/debug/common/debug.ts @@ -412,6 +412,8 @@ export interface IDebugSession extends ITreeElement { removeReplExpressions(): void; addReplExpression(stackFrame: IStackFrame | undefined, name: string): Promise; appendToRepl(data: INewReplElementData): void; + /** Cancel any associated test run set through the DebugSessionOptions */ + cancelCorrelatedTestRun(): void; // session events readonly onDidEndAdapter: Event; diff --git a/src/vs/workbench/contrib/debug/node/debugAdapter.ts b/src/vs/workbench/contrib/debug/node/debugAdapter.ts index 4892338a1c1a..c6cdb3c6979e 100644 --- a/src/vs/workbench/contrib/debug/node/debugAdapter.ts +++ b/src/vs/workbench/contrib/debug/node/debugAdapter.ts @@ -240,7 +240,10 @@ export class ExecutableDebugAdapter extends StreamDebugAdapter { spawnOptions.shell = true; spawnCommand = `"${command}"`; spawnArgs = args.map(a => { - a = a.replace(/"/g, '\\"'); // Escape existing double quotes with \ + // Escape backslashes first + a = a.replace(/\\/g, '\\\\'); + // Then escape double quotes + a = a.replace(/"/g, '\\"'); // Wrap in double quotes return `"${a}"`; }); diff --git a/src/vs/workbench/contrib/debug/test/common/mockDebug.ts b/src/vs/workbench/contrib/debug/test/common/mockDebug.ts index add32108f5f7..8d3c433daac5 100644 --- a/src/vs/workbench/contrib/debug/test/common/mockDebug.ts +++ b/src/vs/workbench/contrib/debug/test/common/mockDebug.ts @@ -195,6 +195,10 @@ export class MockSession implements IDebugSession { throw new Error('Method not implemented.'); } + cancelCorrelatedTestRun(): void { + + } + get compoundRoot(): DebugCompoundRoot | undefined { return undefined; } diff --git a/src/vs/workbench/contrib/editSessions/browser/editSessions.contribution.ts b/src/vs/workbench/contrib/editSessions/browser/editSessions.contribution.ts index cb2e6e815551..2751d212bc66 100644 --- a/src/vs/workbench/contrib/editSessions/browser/editSessions.contribution.ts +++ b/src/vs/workbench/contrib/editSessions/browser/editSessions.contribution.ts @@ -3,79 +3,64 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { Disposable, DisposableStore, MutableDisposable } from '../../../../base/common/lifecycle.js'; -import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from '../../../common/contributions.js'; -import { Registry } from '../../../../platform/registry/common/platform.js'; -import { ILifecycleService, LifecyclePhase, ShutdownReason } from '../../../services/lifecycle/common/lifecycle.js'; -import { Action2, IAction2Options, MenuId, MenuRegistry, registerAction2 } from '../../../../platform/actions/common/actions.js'; -import { ServicesAccessor } from '../../../../editor/browser/editorExtensions.js'; -import { localize, localize2 } from '../../../../nls.js'; -import { IEditSessionsStorageService, Change, ChangeType, Folder, EditSession, FileType, EDIT_SESSION_SYNC_CATEGORY, EDIT_SESSIONS_CONTAINER_ID, EditSessionSchemaVersion, IEditSessionsLogService, EDIT_SESSIONS_VIEW_ICON, EDIT_SESSIONS_TITLE, EDIT_SESSIONS_SHOW_VIEW, EDIT_SESSIONS_DATA_VIEW_ID, decodeEditSessionFileContent, hashedEditSessionId, editSessionsLogId, EDIT_SESSIONS_PENDING } from '../common/editSessions.js'; -import { ISCMRepository, ISCMService } from '../../scm/common/scm.js'; -import { IFileService } from '../../../../platform/files/common/files.js'; -import { IWorkspaceContextService, IWorkspaceFolder, WorkbenchState } from '../../../../platform/workspace/common/workspace.js'; -import { URI } from '../../../../base/common/uri.js'; -import { basename, joinPath, relativePath } from '../../../../base/common/resources.js'; -import { encodeBase64 } from '../../../../base/common/buffer.js'; -import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; -import { IProgress, IProgressService, IProgressStep, ProgressLocation } from '../../../../platform/progress/common/progress.js'; -import { EditSessionsWorkbenchService } from './editSessionsStorageService.js'; -import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; -import { UserDataSyncErrorCode, UserDataSyncStoreError } from '../../../../platform/userDataSync/common/userDataSync.js'; -import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; -import { INotificationService, Severity } from '../../../../platform/notification/common/notification.js'; -import { getFileNamesMessage, IDialogService, IFileDialogService } from '../../../../platform/dialogs/common/dialogs.js'; -import { IProductService } from '../../../../platform/product/common/productService.js'; -import { IOpenerService } from '../../../../platform/opener/common/opener.js'; -import { IEnvironmentService } from '../../../../platform/environment/common/environment.js'; -import { workbenchConfigurationNodeBase } from '../../../common/configuration.js'; -import { Extensions as ConfigurationExtensions, IConfigurationRegistry } from '../../../../platform/configuration/common/configurationRegistry.js'; -import { IQuickInputButton, IQuickInputService, IQuickPickItem, IQuickPickSeparator } from '../../../../platform/quickinput/common/quickInput.js'; -import { ExtensionsRegistry } from '../../../services/extensions/common/extensionsRegistry.js'; -import { ContextKeyExpr, ContextKeyExpression, IContextKey, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; -import { ICommandService } from '../../../../platform/commands/common/commands.js'; -import { getVirtualWorkspaceLocation } from '../../../../platform/workspace/common/virtualWorkspace.js'; -import { Schemas } from '../../../../base/common/network.js'; -import { IsWebContext } from '../../../../platform/contextkey/common/contextkeys.js'; -import { IExtensionService, isProposedApiEnabled } from '../../../services/extensions/common/extensions.js'; -import { EditSessionsLogService } from '../common/editSessionsLogService.js'; -import { IViewContainersRegistry, Extensions as ViewExtensions, ViewContainerLocation } from '../../../common/views.js'; -import { IViewsService } from '../../../services/views/common/viewsService.js'; -import { SyncDescriptor } from '../../../../platform/instantiation/common/descriptors.js'; -import { ViewPaneContainer } from '../../../browser/parts/views/viewPaneContainer.js'; -import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; -import { EditSessionsDataViews } from './editSessionsViews.js'; -import { EditSessionsFileSystemProvider } from './editSessionsFileSystemProvider.js'; -import { isNative, isWeb } from '../../../../base/common/platform.js'; -import { VirtualWorkspaceContext, WorkspaceFolderCountContext } from '../../../common/contextkeys.js'; -import { CancellationToken, CancellationTokenSource } from '../../../../base/common/cancellation.js'; -import { equals } from '../../../../base/common/objects.js'; -import { EditSessionIdentityMatch, IEditSessionIdentityService } from '../../../../platform/workspace/common/editSessions.js'; -import { ThemeIcon } from '../../../../base/common/themables.js'; -import { IOutputService } from '../../../services/output/common/output.js'; -import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; -import { IActivityService, NumberBadge } from '../../../services/activity/common/activity.js'; -import { IEditorService } from '../../../services/editor/common/editorService.js'; -import { ILocalizedString } from '../../../../platform/action/common/action.js'; -import { Codicon } from '../../../../base/common/codicons.js'; -import { CancellationError } from '../../../../base/common/errors.js'; -import { IRemoteAgentService } from '../../../services/remote/common/remoteAgentService.js'; -import { IExtensionsWorkbenchService } from '../../extensions/common/extensions.js'; -import { WorkspaceStateSynchroniser } from '../common/workspaceStateSync.js'; -import { IUserDataProfilesService } from '../../../../platform/userDataProfile/common/userDataProfile.js'; -import { IRequestService } from '../../../../platform/request/common/request.js'; -import { EditSessionsStoreClient } from '../common/editSessionsStorageClient.js'; -import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js'; -import { IWorkspaceIdentityService } from '../../../services/workspaces/common/workspaceIdentityService.js'; -import { hashAsync } from '../../../../base/common/hash.js'; - -registerSingleton(IEditSessionsLogService, EditSessionsLogService, InstantiationType.Delayed); -registerSingleton(IEditSessionsStorageService, EditSessionsWorkbenchService, InstantiationType.Delayed); - - -const continueWorkingOnCommand: IAction2Options = { - id: '_workbench.editSessions.actions.continueEditSession', - title: localize2('continue working on', 'Continue Working On...'), +import { Disposable } from 'vs/base/common/lifecycle'; +import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from 'vs/workbench/common/contributions'; +import { Registry } from 'vs/platform/registry/common/platform'; +import { ILifecycleService, LifecyclePhase } from 'vs/workbench/services/lifecycle/common/lifecycle'; +import { Action2, IAction2Options, MenuRegistry, registerAction2 } from 'vs/platform/actions/common/actions'; +import { ServicesAccessor } from 'vs/editor/browser/editorExtensions'; +import { localize } from 'vs/nls'; +import { IEditSessionsStorageService, Change, ChangeType, Folder, EditSession, FileType, EDIT_SESSION_SYNC_CATEGORY, EDIT_SESSIONS_CONTAINER_ID, EditSessionSchemaVersion, IEditSessionsLogService, EDIT_SESSIONS_VIEW_ICON, EDIT_SESSIONS_TITLE, EDIT_SESSIONS_SHOW_VIEW, EDIT_SESSIONS_SIGNED_IN, EDIT_SESSIONS_DATA_VIEW_ID, decodeEditSessionFileContent } from 'vs/workbench/contrib/editSessions/common/editSessions'; +import { ISCMRepository, ISCMService } from 'vs/workbench/contrib/scm/common/scm'; +import { IFileService } from 'vs/platform/files/common/files'; +import { IWorkspaceContextService, IWorkspaceFolder, WorkbenchState } from 'vs/platform/workspace/common/workspace'; +import { URI } from 'vs/base/common/uri'; +import { joinPath, relativePath } from 'vs/base/common/resources'; +import { encodeBase64 } from 'vs/base/common/buffer'; +import { IConfigurationService } from 'vs/platform/configuration/common/configuration'; +import { IProgressService, ProgressLocation } from 'vs/platform/progress/common/progress'; +import { EditSessionsWorkbenchService } from 'vs/workbench/contrib/editSessions/browser/editSessionsStorageService'; +import { registerSingleton } from 'vs/platform/instantiation/common/extensions'; +import { UserDataSyncErrorCode, UserDataSyncStoreError } from 'vs/platform/userDataSync/common/userDataSync'; +import { ITelemetryService } from 'vs/platform/telemetry/common/telemetry'; +import { INotificationService, Severity } from 'vs/platform/notification/common/notification'; +import { IDialogService, IFileDialogService } from 'vs/platform/dialogs/common/dialogs'; +import { IProductService } from 'vs/platform/product/common/productService'; +import { IOpenerService } from 'vs/platform/opener/common/opener'; +import { IEnvironmentService } from 'vs/platform/environment/common/environment'; +import { workbenchConfigurationNodeBase } from 'vs/workbench/common/configuration'; +import { Extensions, IConfigurationRegistry } from 'vs/platform/configuration/common/configurationRegistry'; +import { IQuickInputService, IQuickPickItem } from 'vs/platform/quickinput/common/quickInput'; +import { ExtensionsRegistry } from 'vs/workbench/services/extensions/common/extensionsRegistry'; +import { ContextKeyExpr, ContextKeyExpression, IContextKey, IContextKeyService } from 'vs/platform/contextkey/common/contextkey'; +import { ICommandService } from 'vs/platform/commands/common/commands'; +import { getVirtualWorkspaceLocation } from 'vs/platform/workspace/common/virtualWorkspace'; +import { Schemas } from 'vs/base/common/network'; +import { IsWebContext } from 'vs/platform/contextkey/common/contextkeys'; +import { isProposedApiEnabled } from 'vs/workbench/services/extensions/common/extensions'; +import { EditSessionsLogService } from 'vs/workbench/contrib/editSessions/common/editSessionsLogService'; +import { IViewContainersRegistry, Extensions as ViewExtensions, ViewContainerLocation, IViewsService } from 'vs/workbench/common/views'; +import { SyncDescriptor } from 'vs/platform/instantiation/common/descriptors'; +import { ViewPaneContainer } from 'vs/workbench/browser/parts/views/viewPaneContainer'; +import { IInstantiationService } from 'vs/platform/instantiation/common/instantiation'; +import { EditSessionsDataViews } from 'vs/workbench/contrib/editSessions/browser/editSessionsViews'; +import { EditSessionsFileSystemProvider } from 'vs/workbench/contrib/editSessions/browser/editSessionsFileSystemProvider'; +import { isNative } from 'vs/base/common/platform'; +import { WorkspaceFolderCountContext } from 'vs/workbench/common/contextkeys'; +import { CancellationTokenSource } from 'vs/base/common/cancellation'; +import { equals } from 'vs/base/common/objects'; +import { IEditSessionIdentityService } from 'vs/platform/workspace/common/editSessions'; +import { ThemeIcon } from 'vs/platform/theme/common/themeService'; +import { IOutputService } from 'vs/workbench/services/output/common/output'; +import * as Constants from 'vs/workbench/contrib/logs/common/logConstants'; + +registerSingleton(IEditSessionsLogService, EditSessionsLogService, false); +registerSingleton(IEditSessionsStorageService, EditSessionsWorkbenchService, false); + +const continueEditSessionCommand: IAction2Options = { + id: '_workbench.experimental.editSessions.actions.continueEditSession', + title: { value: localize('continue edit session', "Continue Edit Session..."), original: 'Continue Edit Session...' }, + category: EDIT_SESSION_SYNC_CATEGORY, precondition: WorkspaceFolderCountContext.notEqualsTo('0'), f1: true }; @@ -540,19 +525,20 @@ export class EditSessionsContribution extends Disposable implements IWorkbenchCo return; } - // TODO@joyceerhl Provide the option to diff files which would be overwritten by edit session contents - if (conflictingChanges.length > 0) { - // Allow to show edit sessions - - const { confirmed } = await this.dialogService.confirm({ - type: Severity.Warning, - message: conflictingChanges.length > 1 ? - localize('resume edit session warning many', 'Resuming your working changes from the cloud will overwrite the following {0} files. Do you want to proceed?', conflictingChanges.length) : - localize('resume edit session warning 1', 'Resuming your working changes from the cloud will overwrite {0}. Do you want to proceed?', basename(conflictingChanges[0].uri)), - detail: conflictingChanges.length > 1 ? getFileNamesMessage(conflictingChanges.map((c) => c.uri)) : undefined - }); - - if (!confirmed) { + if (hasLocalUncommittedChanges) { + // TODO@joyceerhl Provide the option to diff files which would be overwritten by edit session contents + const yes = localize('resume edit session yes', 'Yes'); + const cancel = localize('resume edit session cancel', 'Cancel'); + + const result = await this.dialogService.show( + Severity.Warning, + localize('resume edit session warning', 'Resuming your edit session may overwrite your existing uncommitted changes. Do you want to proceed?'), + [cancel, yes], + { + custom: true, + cancelId: 0 + }); + if (result.choice === 0) { return; } } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts index 8542167a79b3..6c235d893935 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionEditor.ts @@ -42,7 +42,6 @@ import { ITelemetryService } from '../../../../platform/telemetry/common/telemet import { defaultCheckboxStyles } from '../../../../platform/theme/browser/defaultStyles.js'; import { buttonForeground, buttonHoverBackground, editorBackground, textLinkActiveForeground, textLinkForeground } from '../../../../platform/theme/common/colorRegistry.js'; import { IColorTheme, ICssStyleCollector, IThemeService, registerThemingParticipant } from '../../../../platform/theme/common/themeService.js'; -import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; import { EditorPane } from '../../../browser/parts/editor/editorPane.js'; import { IEditorOpenContext } from '../../../common/editor.js'; import { ExtensionFeaturesTab } from './extensionFeaturesTab.js'; @@ -73,18 +72,15 @@ import { } from './extensionsActions.js'; import { Delegate } from './extensionsList.js'; import { ExtensionData, ExtensionsGridView, ExtensionsTree, getExtensions } from './extensionsViewer.js'; -import { ExtensionRecommendationWidget, ExtensionStatusWidget, ExtensionWidget, InstallCountWidget, RatingsWidget, RemoteBadgeWidget, SponsorWidget, VerifiedPublisherWidget, onClick } from './extensionsWidgets.js'; +import { ExtensionRecommendationWidget, ExtensionStatusWidget, ExtensionWidget, InstallCountWidget, RatingsWidget, RemoteBadgeWidget, SponsorWidget, PublisherWidget, onClick, ExtensionKindIndicatorWidget } from './extensionsWidgets.js'; import { ExtensionContainers, ExtensionEditorTab, ExtensionState, IExtension, IExtensionContainer, IExtensionsWorkbenchService } from '../common/extensions.js'; import { ExtensionsInput, IExtensionEditorOptions } from '../common/extensionsInput.js'; -import { IExplorerService } from '../../files/browser/files.js'; import { DEFAULT_MARKDOWN_STYLES, renderMarkdownDocument } from '../../markdown/browser/markdownDocumentRenderer.js'; import { IWebview, IWebviewService, KEYBINDING_CONTEXT_WEBVIEW_FIND_WIDGET_FOCUSED } from '../../webview/browser/webview.js'; import { IEditorGroup } from '../../../services/editor/common/editorGroupsService.js'; import { IEditorService } from '../../../services/editor/common/editorService.js'; import { IExtensionRecommendationsService } from '../../../services/extensionRecommendations/common/extensionRecommendations.js'; import { IExtensionService } from '../../../services/extensions/common/extensions.js'; -import { IViewsService } from '../../../services/views/common/viewsService.js'; -import { VIEW_ID as EXPLORER_VIEW_ID } from '../../files/common/files.js'; import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js'; import { IHoverService } from '../../../../platform/hover/browser/hover.js'; import { ByteSize, IFileService } from '../../../../platform/files/common/files.js'; @@ -161,11 +157,6 @@ interface IExtensionEditorTemplate { name: HTMLElement; preview: HTMLElement; builtin: HTMLElement; - publisher: HTMLElement; - publisherDisplayName: HTMLElement; - resource: HTMLElement; - installCount: HTMLElement; - rating: HTMLElement; description: HTMLElement; actionsAndStatusContainer: HTMLElement; extensionActionBar: ActionBar; @@ -257,10 +248,6 @@ export class ExtensionEditor extends EditorPane { @ILanguageService private readonly languageService: ILanguageService, @IContextMenuService private readonly contextMenuService: IContextMenuService, @IContextKeyService private readonly contextKeyService: IContextKeyService, - @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, - @IExplorerService private readonly explorerService: IExplorerService, - @IViewsService private readonly viewsService: IViewsService, - @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, @IHoverService private readonly hoverService: IHoverService, ) { super(ExtensionEditor.ID, group, telemetryService, themeService, storageService); @@ -302,29 +289,33 @@ export class ExtensionEditor extends EditorPane { builtin.textContent = localize('builtin', "Built-in"); const subtitle = append(details, $('.subtitle')); - const publisher = append(append(subtitle, $('.subtitle-entry')), $('.publisher.clickable', { tabIndex: 0 })); - publisher.setAttribute('role', 'button'); - const publisherDisplayName = append(publisher, $('.publisher-name')); - const verifiedPublisherWidget = this.instantiationService.createInstance(VerifiedPublisherWidget, append(publisher, $('.verified-publisher')), false); + const subTitleEntryContainers: HTMLElement[] = []; + + const publisherContainer = append(subtitle, $('.subtitle-entry')); + subTitleEntryContainers.push(publisherContainer); + const publisherWidget = this.instantiationService.createInstance(PublisherWidget, publisherContainer, false); - const resource = append(append(subtitle, $('.subtitle-entry.resource')), $('', { tabIndex: 0 })); - resource.setAttribute('role', 'button'); + const extensionKindContainer = append(subtitle, $('.subtitle-entry')); + subTitleEntryContainers.push(extensionKindContainer); + const extensionKindWidget = this.instantiationService.createInstance(ExtensionKindIndicatorWidget, extensionKindContainer, false); - const installCount = append(append(subtitle, $('.subtitle-entry')), $('span.install', { tabIndex: 0 })); - this._register(this.hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), installCount, localize('install count', "Install count"))); - const installCountWidget = this.instantiationService.createInstance(InstallCountWidget, installCount, false); + const installCountContainer = append(subtitle, $('.subtitle-entry')); + subTitleEntryContainers.push(installCountContainer); + const installCountWidget = this.instantiationService.createInstance(InstallCountWidget, installCountContainer, false); - const rating = append(append(subtitle, $('.subtitle-entry')), $('span.rating.clickable', { tabIndex: 0 })); - this._register(this.hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), rating, localize('rating', "Rating"))); - rating.setAttribute('role', 'link'); // #132645 - const ratingsWidget = this.instantiationService.createInstance(RatingsWidget, rating, false); + const ratingsContainer = append(subtitle, $('.subtitle-entry')); + subTitleEntryContainers.push(ratingsContainer); + const ratingsWidget = this.instantiationService.createInstance(RatingsWidget, ratingsContainer, false); - const sponsorWidget = this.instantiationService.createInstance(SponsorWidget, append(subtitle, $('.subtitle-entry'))); + const sponsorContainer = append(subtitle, $('.subtitle-entry')); + subTitleEntryContainers.push(sponsorContainer); + const sponsorWidget = this.instantiationService.createInstance(SponsorWidget, sponsorContainer); const widgets: ExtensionWidget[] = [ remoteBadge, versionWidget, - verifiedPublisherWidget, + publisherWidget, + extensionKindWidget, installCountWidget, ratingsWidget, sponsorWidget, @@ -440,18 +431,23 @@ export class ExtensionEditor extends EditorPane { header, icon, iconContainer, - installCount, name, navbar, preview, - publisher, - publisherDisplayName, - resource, - rating, actionsAndStatusContainer, extensionActionBar, set extension(extension: IExtension) { extensionContainers.extension = extension; + let lastNonEmptySubtitleEntryContainer; + for (const subTitleEntryElement of subTitleEntryContainers) { + subTitleEntryElement.classList.remove('last-non-empty'); + if (subTitleEntryElement.children.length > 0) { + lastNonEmptySubtitleEntryContainer = subTitleEntryElement; + } + } + if (lastNonEmptySubtitleEntryContainer) { + lastNonEmptySubtitleEntryContainer.classList.add('last-non-empty'); + } }, set gallery(gallery: IGalleryExtension | null) { versionWidget.gallery = gallery; @@ -559,38 +555,8 @@ export class ExtensionEditor extends EditorPane { template.description.textContent = extension.description; - // subtitle - template.publisher.classList.toggle('clickable', !!extension.url); - template.publisherDisplayName.textContent = extension.publisherDisplayName; - template.publisher.parentElement?.classList.toggle('hide', !!extension.resourceExtension || extension.local?.source === 'resource'); - this.transientDisposables.add(this.hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), template.publisher, localize('publisher', "Publisher ({0})", extension.publisher))); - - const location = extension.resourceExtension?.location ?? (extension.local?.source === 'resource' ? extension.local?.location : undefined); - template.resource.parentElement?.classList.toggle('hide', !location); - if (location) { - const workspaceFolder = this.contextService.getWorkspaceFolder(location); - if (workspaceFolder && extension.isWorkspaceScoped) { - template.resource.parentElement?.classList.add('clickable'); - this.transientDisposables.add(this.hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), template.resource, this.uriIdentityService.extUri.relativePath(workspaceFolder.uri, location))); - template.resource.textContent = localize('workspace extension', "Workspace Extension"); - this.transientDisposables.add(onClick(template.resource, () => { - this.viewsService.openView(EXPLORER_VIEW_ID, true).then(() => this.explorerService.select(location, true)); - })); - } else { - template.resource.parentElement?.classList.remove('clickable'); - this.transientDisposables.add(this.hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), template.resource, location.path)); - template.resource.textContent = localize('local extension', "Local Extension"); - } - } - - template.installCount.parentElement?.classList.toggle('hide', !extension.url); - template.rating.parentElement?.classList.toggle('hide', !extension.url); - template.rating.classList.toggle('clickable', !!extension.url); - if (extension.url) { this.transientDisposables.add(onClick(template.name, () => this.openerService.open(URI.parse(extension.url!)))); - this.transientDisposables.add(onClick(template.rating, () => this.openerService.open(URI.parse(`${extension.url}&ssr=false#review-details`)))); - this.transientDisposables.add(onClick(template.publisher, () => this.extensionsWorkbenchService.openSearch(`publisher:"${extension.publisherDisplayName}"`))); } const manifest = await this.extensionManifest.get().promise; @@ -1105,7 +1071,7 @@ class AdditionalDetailsWidget extends Disposable { if (extension.url) { resources.push([localize('Marketplace', "Marketplace"), URI.parse(extension.url)]); } - if (extension.url && extension.supportUrl) { + if (extension.supportUrl) { try { resources.push([localize('issues', "Issues"), URI.parse(extension.supportUrl)]); } catch (error) {/* Ignore */ } @@ -1115,7 +1081,7 @@ class AdditionalDetailsWidget extends Disposable { resources.push([localize('repository', "Repository"), URI.parse(extension.repository)]); } catch (error) {/* Ignore */ } } - if (extension.url && extension.licenseUrl) { + if (extension.licenseUrl) { try { resources.push([localize('license', "License"), URI.parse(extension.licenseUrl)]); } catch (error) {/* Ignore */ } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionFeaturesTab.ts b/src/vs/workbench/contrib/extensions/browser/extensionFeaturesTab.ts index 9d0f876a3075..0b90d652d3ff 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionFeaturesTab.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionFeaturesTab.ts @@ -27,7 +27,7 @@ import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; import Severity from '../../../../base/common/severity.js'; import { errorIcon, infoIcon, warningIcon } from './extensionsIcons.js'; -import { SeverityIcon } from '../../../../platform/severityIcon/browser/severityIcon.js'; +import { SeverityIcon } from '../../../../base/browser/ui/severityIcon/severityIcon.js'; import { KeybindingLabel } from '../../../../base/browser/ui/keybindingLabel/keybindingLabel.js'; import { OS } from '../../../../base/common/platform.js'; import { IMarkdownString, MarkdownString, isMarkdownString } from '../../../../base/common/htmlContent.js'; @@ -292,7 +292,7 @@ class RuntimeStatusMarkdownRenderer extends Disposable implements IExtensionFeat highlightCircle.style.display = 'block'; tooltip.style.left = `${closestPoint.x + 24}px`; tooltip.style.top = `${closestPoint.y + 14}px`; - hoverDisposable.value = this.hoverService.showHover({ + hoverDisposable.value = this.hoverService.showInstantHover({ content: new MarkdownString(`${closestPoint.date}: ${closestPoint.count} requests`), target: tooltip, appearance: { diff --git a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts index 0fb425ed79c8..19926de31713 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensions.contribution.ts @@ -8,12 +8,12 @@ import { KeyMod, KeyCode } from '../../../../base/common/keyCodes.js'; import { Registry } from '../../../../platform/registry/common/platform.js'; import { MenuRegistry, MenuId, registerAction2, Action2, IMenuItem, IAction2Options } from '../../../../platform/actions/common/actions.js'; import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; -import { ExtensionsLocalizedLabel, IExtensionManagementService, IExtensionGalleryService, PreferencesLocalizedLabel, EXTENSION_INSTALL_SOURCE_CONTEXT, ExtensionInstallSource, UseUnpkgResourceApiConfigKey, AllowedExtensionsConfigKey } from '../../../../platform/extensionManagement/common/extensionManagement.js'; +import { ExtensionsLocalizedLabel, IExtensionManagementService, IExtensionGalleryService, PreferencesLocalizedLabel, EXTENSION_INSTALL_SOURCE_CONTEXT, ExtensionInstallSource, UseUnpkgResourceApiConfigKey, AllowedExtensionsConfigKey, SortBy, FilterType } from '../../../../platform/extensionManagement/common/extensionManagement.js'; import { EnablementState, IExtensionManagementServerService, IPublisherInfo, IWorkbenchExtensionEnablementService, IWorkbenchExtensionManagementService } from '../../../services/extensionManagement/common/extensionManagement.js'; import { IExtensionIgnoredRecommendationsService, IExtensionRecommendationsService } from '../../../services/extensionRecommendations/common/extensionRecommendations.js'; import { IWorkbenchContributionsRegistry, Extensions as WorkbenchExtensions, IWorkbenchContribution } from '../../../common/contributions.js'; import { SyncDescriptor } from '../../../../platform/instantiation/common/descriptors.js'; -import { VIEWLET_ID, IExtensionsWorkbenchService, IExtensionsViewPaneContainer, TOGGLE_IGNORE_EXTENSION_ACTION_ID, INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID, WORKSPACE_RECOMMENDATIONS_VIEW_ID, IWorkspaceRecommendedExtensionsView, AutoUpdateConfigurationKey, HasOutdatedExtensionsContext, SELECT_INSTALL_VSIX_EXTENSION_COMMAND_ID, LIST_WORKSPACE_UNSUPPORTED_EXTENSIONS_COMMAND_ID, ExtensionEditorTab, THEME_ACTIONS_GROUP, INSTALL_ACTIONS_GROUP, OUTDATED_EXTENSIONS_VIEW_ID, CONTEXT_HAS_GALLERY, extensionsSearchActionsMenu, UPDATE_ACTIONS_GROUP, IExtensionArg, ExtensionRuntimeActionType, EXTENSIONS_CATEGORY } from '../common/extensions.js'; +import { VIEWLET_ID, IExtensionsWorkbenchService, IExtensionsViewPaneContainer, TOGGLE_IGNORE_EXTENSION_ACTION_ID, INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID, WORKSPACE_RECOMMENDATIONS_VIEW_ID, IWorkspaceRecommendedExtensionsView, AutoUpdateConfigurationKey, HasOutdatedExtensionsContext, SELECT_INSTALL_VSIX_EXTENSION_COMMAND_ID, LIST_WORKSPACE_UNSUPPORTED_EXTENSIONS_COMMAND_ID, ExtensionEditorTab, THEME_ACTIONS_GROUP, INSTALL_ACTIONS_GROUP, OUTDATED_EXTENSIONS_VIEW_ID, CONTEXT_HAS_GALLERY, extensionsSearchActionsMenu, UPDATE_ACTIONS_GROUP, IExtensionArg, ExtensionRuntimeActionType, EXTENSIONS_CATEGORY, AutoRestartConfigurationKey } from '../common/extensions.js'; import { InstallSpecificVersionOfExtensionAction, ConfigureWorkspaceRecommendedExtensionsAction, ConfigureWorkspaceFolderRecommendedExtensionsAction, SetColorThemeAction, SetFileIconThemeAction, SetProductIconThemeAction, ClearLanguageAction, ToggleAutoUpdateForExtensionAction, ToggleAutoUpdatesForPublisherAction, TogglePreReleaseExtensionAction, InstallAnotherVersionAction, InstallAction } from './extensionsActions.js'; import { ExtensionsInput } from '../common/extensionsInput.js'; import { ExtensionEditor } from './extensionEditor.js'; @@ -79,6 +79,8 @@ import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uri import { IConfigurationMigrationRegistry, Extensions as ConfigurationMigrationExtensions } from '../../../common/configuration.js'; import { IProductService } from '../../../../platform/product/common/productService.js'; import { IUserDataProfilesService } from '../../../../platform/userDataProfile/common/userDataProfile.js'; +import product from '../../../../platform/product/common/product.js'; +import { IExtensionGalleryManifest, IExtensionGalleryManifestService } from '../../../../platform/extensionManagement/common/extensionGalleryManifest.js'; // Singletons registerSingleton(IExtensionsWorkbenchService, ExtensionsWorkbenchService, InstantiationType.Eager /* Auto updates extensions */); @@ -265,6 +267,12 @@ Registry.as(ConfigurationExtensions.Configuration) scope: ConfigurationScope.APPLICATION, included: isNative }, + [AutoRestartConfigurationKey]: { + type: 'boolean', + description: localize('autoRestart', "If activated, extensions will automatically restart following an update if the window is not in focus. There can be a data loss if you have open Notebooks or Custom Editors."), + default: false, + included: product.quality !== 'stable' + }, [UseUnpkgResourceApiConfigKey]: { type: 'boolean', description: localize('extensions.gallery.useUnpkgResourceApi', "When enabled, extensions to update are fetched from Unpkg service."), @@ -456,7 +464,7 @@ CommandsRegistry.registerCommand({ } } else { const vsix = URI.revive(arg); - await extensionsWorkbenchService.install(vsix, { installOnlyNewlyAddedFromExtensionPack: options?.installOnlyNewlyAddedFromExtensionPackVSIX, installGivenVersion: true }); + await extensionsWorkbenchService.install(vsix, { installGivenVersion: true }); } } catch (e) { onUnexpectedError(e); @@ -539,6 +547,9 @@ overrideActionForActiveExtensionEditorWebview(PasteAction, webview => webview.pa export const CONTEXT_HAS_LOCAL_SERVER = new RawContextKey('hasLocalServer', false); export const CONTEXT_HAS_REMOTE_SERVER = new RawContextKey('hasRemoteServer', false); export const CONTEXT_HAS_WEB_SERVER = new RawContextKey('hasWebServer', false); +const CONTEXT_GALLERY_SORT_CAPABILITIES = new RawContextKey('gallerySortCapabilities', ''); +const CONTEXT_GALLERY_FILTER_CAPABILITIES = new RawContextKey('galleryFilterCapabilities', ''); +const CONTEXT_GALLERY_ALL_REPOSITORY_SIGNED = new RawContextKey('galleryAllRepositorySigned', false); async function runAction(action: IAction): Promise { try { @@ -560,7 +571,8 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi constructor( @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, @IExtensionGalleryService extensionGalleryService: IExtensionGalleryService, - @IContextKeyService contextKeyService: IContextKeyService, + @IExtensionGalleryManifestService extensionGalleryManifestService: IExtensionGalleryManifestService, + @IContextKeyService private readonly contextKeyService: IContextKeyService, @IViewsService private readonly viewsService: IViewsService, @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, @IWorkbenchExtensionEnablementService private readonly extensionEnablementService: IWorkbenchExtensionEnablementService, @@ -590,11 +602,22 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi hasWebServerContext.set(true); } + extensionGalleryManifestService.getExtensionGalleryManifest() + .then(extensionGalleryManifest => { + this.registerGalleryCapabilitiesContexts(extensionGalleryManifest); + this._register(extensionGalleryManifestService.onDidChangeExtensionGalleryManifest(extensionGalleryManifest => this.registerGalleryCapabilitiesContexts(extensionGalleryManifest))); + }); this.registerGlobalActions(); this.registerContextMenuActions(); this.registerQuickAccessProvider(); } + private async registerGalleryCapabilitiesContexts(extensionGalleryManifest: IExtensionGalleryManifest | null): Promise { + CONTEXT_GALLERY_SORT_CAPABILITIES.bindTo(this.contextKeyService).set(`_${extensionGalleryManifest?.capabilities.extensionQuery.sorting?.map(s => s.name)?.join('_')}_UpdateDate_`); + CONTEXT_GALLERY_FILTER_CAPABILITIES.bindTo(this.contextKeyService).set(`_${extensionGalleryManifest?.capabilities.extensionQuery.filtering?.map(s => s.name)?.join('_')}_`); + CONTEXT_GALLERY_ALL_REPOSITORY_SIGNED.bindTo(this.contextKeyService).set(!!extensionGalleryManifest?.capabilities?.signing?.allRepositorySigned); + } + private registerQuickAccessProvider(): void { if (this.extensionManagementServerService.localExtensionManagementServer || this.extensionManagementServerService.remoteExtensionManagementServer @@ -898,7 +921,8 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi if (requireReload) { notificationService.prompt( Severity.Info, - localize('InstallVSIXAction.successReload', "Completed installing extension from VSIX. Please reload Visual Studio Code to enable it."), + vsixs.length > 1 ? localize('InstallVSIXs.successReload', "Completed installing extensions. Please reload Visual Studio Code to enable them.") + : localize('InstallVSIXAction.successReload', "Completed installing extension. Please reload Visual Studio Code to enable it."), [{ label: localize('InstallVSIXAction.reloadNow', "Reload Now"), run: () => hostService.reload() @@ -908,7 +932,8 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi else if (requireRestart) { notificationService.prompt( Severity.Info, - localize('InstallVSIXAction.successRestart', "Completed installing extension from VSIX. Please restart extensions to enable it."), + vsixs.length > 1 ? localize('InstallVSIXs.successRestart', "Completed installing extensions. Please restart extensions to enable them.") + : localize('InstallVSIXAction.successRestart', "Completed installing extension. Please restart extensions to enable it."), [{ label: localize('InstallVSIXAction.restartExtensions', "Restart Extensions"), run: () => extensionsWorkbenchService.updateRunningExtensions() @@ -918,7 +943,7 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi else { notificationService.prompt( Severity.Info, - localize('InstallVSIXAction.successNoReload', "Completed installing extension."), + vsixs.length > 1 ? localize('InstallVSIXs.successNoReload', "Completed installing extensions.") : localize('InstallVSIXAction.successNoReload', "Completed installing extension."), [] ); } @@ -985,16 +1010,17 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi }); const showFeaturedExtensionsId = 'extensions.filter.featured'; + const featuresExtensionsWhenContext = ContextKeyExpr.and(CONTEXT_HAS_GALLERY, ContextKeyExpr.regex(CONTEXT_GALLERY_FILTER_CAPABILITIES.key, new RegExp(`_${FilterType.Featured}_`))); this.registerExtensionAction({ id: showFeaturedExtensionsId, title: localize2('showFeaturedExtensions', 'Show Featured Extensions'), category: ExtensionsLocalizedLabel, menu: [{ id: MenuId.CommandPalette, - when: CONTEXT_HAS_GALLERY + when: featuresExtensionsWhenContext }, { id: extensionsFilterSubMenu, - when: CONTEXT_HAS_GALLERY, + when: featuresExtensionsWhenContext, group: '1_predefined', order: 1, }], @@ -1065,7 +1091,7 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi MenuRegistry.appendMenuItem(extensionsFilterSubMenu, { submenu: extensionsCategoryFilterSubMenu, title: localize('filter by category', "Category"), - when: CONTEXT_HAS_GALLERY, + when: ContextKeyExpr.and(CONTEXT_HAS_GALLERY, ContextKeyExpr.regex(CONTEXT_GALLERY_FILTER_CAPABILITIES.key, new RegExp(`_${FilterType.Category}_`))), group: '2_categories', order: 1, }); @@ -1184,19 +1210,20 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi }); [ - { id: 'installs', title: localize('sort by installs', "Install Count"), precondition: BuiltInExtensionsContext.negate() }, - { id: 'rating', title: localize('sort by rating', "Rating"), precondition: BuiltInExtensionsContext.negate() }, - { id: 'name', title: localize('sort by name', "Name"), precondition: BuiltInExtensionsContext.negate() }, - { id: 'publishedDate', title: localize('sort by published date', "Published Date"), precondition: BuiltInExtensionsContext.negate() }, - { id: 'updateDate', title: localize('sort by update date', "Updated Date"), precondition: ContextKeyExpr.and(SearchMarketplaceExtensionsContext.negate(), RecommendedExtensionsContext.negate(), BuiltInExtensionsContext.negate()) }, - ].map(({ id, title, precondition }, index) => { + { id: 'installs', title: localize('sort by installs', "Install Count"), precondition: BuiltInExtensionsContext.negate(), sortCapability: SortBy.InstallCount }, + { id: 'rating', title: localize('sort by rating', "Rating"), precondition: BuiltInExtensionsContext.negate(), sortCapability: SortBy.WeightedRating }, + { id: 'name', title: localize('sort by name', "Name"), precondition: BuiltInExtensionsContext.negate(), sortCapability: SortBy.Title }, + { id: 'publishedDate', title: localize('sort by published date', "Published Date"), precondition: BuiltInExtensionsContext.negate(), sortCapability: SortBy.PublishedDate }, + { id: 'updateDate', title: localize('sort by update date', "Updated Date"), precondition: ContextKeyExpr.and(SearchMarketplaceExtensionsContext.negate(), RecommendedExtensionsContext.negate(), BuiltInExtensionsContext.negate()), sortCapability: 'UpdateDate' }, + ].map(({ id, title, precondition, sortCapability }, index) => { + const sortCapabilityContext = ContextKeyExpr.regex(CONTEXT_GALLERY_SORT_CAPABILITIES.key, new RegExp(`_${sortCapability}_`)); this.registerExtensionAction({ id: `extensions.sort.${id}`, title, - precondition: ContextKeyExpr.and(precondition, ContextKeyExpr.regex(ExtensionsSearchValueContext.key, /^@feature:/).negate()), + precondition: ContextKeyExpr.and(precondition, ContextKeyExpr.regex(ExtensionsSearchValueContext.key, /^@feature:/).negate(), sortCapabilityContext), menu: [{ id: extensionsSortSubMenu, - when: ContextKeyExpr.or(CONTEXT_HAS_GALLERY, DefaultViewsContext), + when: ContextKeyExpr.and(ContextKeyExpr.or(CONTEXT_HAS_GALLERY, DefaultViewsContext), sortCapabilityContext), order: index, }], toggled: ExtensionsSortByContext.isEqualTo(id), @@ -1512,7 +1539,7 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi menu: { id: MenuId.ExtensionContext, group: '0_install', - when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'uninstalled'), ContextKeyExpr.has('isGalleryExtension'), ContextKeyExpr.not('extensionDisallowInstall'), ContextKeyExpr.has('extensionIsUnsigned')), + when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'uninstalled'), ContextKeyExpr.has('isGalleryExtension'), ContextKeyExpr.not('extensionDisallowInstall'), ContextKeyExpr.has('extensionIsUnsigned'), CONTEXT_GALLERY_ALL_REPOSITORY_SIGNED), order: 1 }, run: async (accessor: ServicesAccessor, extensionId: string) => { @@ -1636,12 +1663,10 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi group: '1_copy', when: ContextKeyExpr.has('isGalleryExtension'), }, - run: async (accessor: ServicesAccessor, extensionId: string) => { + run: async (accessor: ServicesAccessor, _, extension: IExtensionArg) => { const clipboardService = accessor.get(IClipboardService); - const productService = accessor.get(IProductService); - if (productService.extensionsGallery?.itemUrl) { - const link = `${productService.extensionsGallery.itemUrl}?itemName=${extensionId}`; - await clipboardService.writeText(link); + if (extension.galleryLink) { + await clipboardService.writeText(extension.galleryLink); } } }); @@ -1663,7 +1688,7 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi title: localize('download VSIX', "Download VSIX"), menu: { id: MenuId.ExtensionContext, - when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'uninstalled'), ContextKeyExpr.has('isGalleryExtension')), + when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'uninstalled'), ContextKeyExpr.not('extensionDisallowInstall'), ContextKeyExpr.has('isGalleryExtension')), order: this.productService.quality === 'stable' ? 0 : 1 }, run: async (accessor: ServicesAccessor, extensionId: string) => { @@ -1676,7 +1701,7 @@ class ExtensionsContributions extends Disposable implements IWorkbenchContributi title: localize('download pre-release', "Download Pre-Release VSIX"), menu: { id: MenuId.ExtensionContext, - when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'uninstalled'), ContextKeyExpr.has('isGalleryExtension'), ContextKeyExpr.has('extensionHasPreReleaseVersion')), + when: ContextKeyExpr.and(ContextKeyExpr.equals('extensionStatus', 'uninstalled'), ContextKeyExpr.not('extensionDisallowInstall'), ContextKeyExpr.has('isGalleryExtension'), ContextKeyExpr.has('extensionHasPreReleaseVersion')), order: this.productService.quality === 'stable' ? 1 : 0 }, run: async (accessor: ServicesAccessor, extensionId: string) => { diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts index 8b062d1d47d6..7b344ef9fb67 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsActions.ts @@ -72,6 +72,7 @@ import { Registry } from '../../../../platform/registry/common/platform.js'; import { IUpdateService } from '../../../../platform/update/common/update.js'; import { ActionWithDropdownActionViewItem, IActionWithDropdownActionViewItemOptions } from '../../../../base/browser/ui/dropdown/dropdownActionViewItem.js'; import { IAuthenticationUsageService } from '../../../services/authentication/browser/authenticationUsageService.js'; +import { IExtensionGalleryManifestService } from '../../../../platform/extensionManagement/common/extensionGalleryManifest.js'; export class PromptExtensionInstallFailureAction extends Action { @@ -212,9 +213,6 @@ export class PromptExtensionInstallFailureAction extends Action { if (!this.extension.gallery) { return undefined; } - if (!this.productService.extensionsGallery) { - return undefined; - } if (!this.extensionManagementServerService.localExtensionManagementServer && !this.extensionManagementServerService.remoteExtensionManagementServer) { return undefined; } @@ -233,7 +231,18 @@ export class PromptExtensionInstallFailureAction extends Action { if (targetPlatform === TargetPlatform.UNKNOWN) { return undefined; } - return URI.parse(`${this.productService.extensionsGallery.serviceUrl}/publishers/${this.extension.publisher}/vsextensions/${this.extension.name}/${this.version}/vspackage${targetPlatform !== TargetPlatform.UNDEFINED ? `?targetPlatform=${targetPlatform}` : ''}`); + + const [extension] = await this.galleryService.getExtensions([{ + ...this.extension.identifier, + version: this.version + }], { + targetPlatform + }, CancellationToken.None); + + if (!extension) { + return undefined; + } + return URI.parse(extension.assets.download.uri); } } @@ -412,6 +421,7 @@ export class InstallAction extends ExtensionAction { @ITelemetryService private readonly telemetryService: ITelemetryService, @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, @IAllowedExtensionsService private readonly allowedExtensionsService: IAllowedExtensionsService, + @IExtensionGalleryManifestService private readonly extensionGalleryManifestService: IExtensionGalleryManifestService, ) { super('extensions.install', localize('install', "Install"), InstallAction.CLASS, false); this.hideOnDisabled = false; @@ -460,7 +470,7 @@ export class InstallAction extends ExtensionAction { return; } - if (this.extension.gallery && !this.extension.gallery.isSigned) { + if (this.extension.gallery && !this.extension.gallery.isSigned && (await this.extensionGalleryManifestService.getExtensionGalleryManifest())?.capabilities.signing?.allRepositorySigned) { const { result } = await this.dialogService.prompt({ type: Severity.Warning, message: localize('not signed', "'{0}' is an extension from an unknown source. Are you sure you want to install?", this.extension.displayName), @@ -1250,7 +1260,7 @@ async function getContextMenuActionsGroups(extension: IExtension | undefined | n cksOverlay.push(['galleryExtensionHasPreReleaseVersion', extension.gallery?.hasPreReleaseVersion]); cksOverlay.push(['extensionHasPreReleaseVersion', extension.hasPreReleaseVersion]); cksOverlay.push(['extensionHasReleaseVersion', extension.hasReleaseVersion]); - cksOverlay.push(['extensionDisallowInstall', !!extension.deprecationInfo?.disallowInstall]); + cksOverlay.push(['extensionDisallowInstall', extension.isMalicious || extension.deprecationInfo?.disallowInstall]); cksOverlay.push(['isExtensionAllowed', allowedExtensionsService.isAllowed({ id: extension.identifier.id, publisherDisplayName: extension.publisherDisplayName }) === true]); cksOverlay.push(['isPreReleaseExtensionAllowed', allowedExtensionsService.isAllowed({ id: extension.identifier.id, publisherDisplayName: extension.publisherDisplayName, prerelease: true }) === true]); cksOverlay.push(['extensionIsUnsigned', extension.gallery && !extension.gallery.isSigned]); @@ -1438,7 +1448,8 @@ export class MenuItemExtensionAction extends ExtensionAction { const extensionArg: IExtensionArg = { id: this.extension.identifier.id, version: this.extension.version, - location: this.extension.local?.location + location: this.extension.local?.location, + galleryLink: this.extension.url }; await this.action.run(id, extensionArg); } @@ -2514,6 +2525,7 @@ export class ExtensionStatusAction extends ExtensionAction { @IAllowedExtensionsService private readonly allowedExtensionsService: IAllowedExtensionsService, @IWorkbenchExtensionEnablementService private readonly workbenchExtensionEnablementService: IWorkbenchExtensionEnablementService, @IExtensionFeaturesManagementService private readonly extensionFeaturesManagementService: IExtensionFeaturesManagementService, + @IExtensionGalleryManifestService private readonly extensionGalleryManifestService: IExtensionGalleryManifestService, ) { super('extensions.status', '', `${ExtensionStatusAction.CLASS} hide`, false); this._register(this.labelService.onDidChangeFormatters(() => this.update(), this)); @@ -2540,7 +2552,7 @@ export class ExtensionStatusAction extends ExtensionAction { return; } - if (this.extension.state === ExtensionState.Uninstalled && this.extension.gallery && !this.extension.gallery.isSigned) { + if (this.extension.state === ExtensionState.Uninstalled && this.extension.gallery && !this.extension.gallery.isSigned && (await this.extensionGalleryManifestService.getExtensionGalleryManifest())?.capabilities.signing?.allRepositorySigned) { this.updateStatus({ icon: warningIcon, message: new MarkdownString(localize('not signed tooltip', "This extension is not signed by the Extension Marketplace.")) }, true); return; } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsIcons.ts b/src/vs/workbench/contrib/extensions/browser/extensionsIcons.ts index f7871ef4e2b7..ea1bc04ede84 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsIcons.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsIcons.ts @@ -23,6 +23,7 @@ export const syncEnabledIcon = registerIcon('extensions-sync-enabled', Codicon.s export const syncIgnoredIcon = registerIcon('extensions-sync-ignored', Codicon.syncIgnored, localize('syncIgnoredIcon', 'Icon to indicate that an extension is ignored when syncing.')); export const remoteIcon = registerIcon('extensions-remote', Codicon.remote, localize('remoteIcon', 'Icon to indicate that an extension is remote in the extensions view and editor.')); export const installCountIcon = registerIcon('extensions-install-count', Codicon.cloudDownload, localize('installCountIcon', 'Icon shown along with the install count in the extensions view and editor.')); +export const privateExtensionIcon = registerIcon('extensions-private', Codicon.lock, localize('lockIcon', 'Icon shown for private extensions in the extensions view and editor.')); export const ratingIcon = registerIcon('extensions-rating', Codicon.star, localize('ratingIcon', 'Icon shown along with the rating in the extensions view and editor.')); export const preReleaseIcon = registerIcon('extensions-pre-release', Codicon.versions, localize('preReleaseIcon', 'Icon shown for extensions having pre-release versions in extensions view and editor.')); export const sponsorIcon = registerIcon('extensions-sponsor', Codicon.heartFilled, localize('sponsorIcon', 'Icon used for sponsoring extensions in the extensions view and editor.')); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsList.ts b/src/vs/workbench/contrib/extensions/browser/extensionsList.ts index 8e2e5ab9691c..97be5462cf64 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsList.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsList.ts @@ -11,11 +11,10 @@ import { ActionBar } from '../../../../base/browser/ui/actionbar/actionbar.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { IListVirtualDelegate } from '../../../../base/browser/ui/list/list.js'; import { IPagedRenderer } from '../../../../base/browser/ui/list/listPaging.js'; -import { Event } from '../../../../base/common/event.js'; import { IExtension, ExtensionContainers, ExtensionState, IExtensionsWorkbenchService, IExtensionsViewState } from '../common/extensions.js'; import { ManageExtensionAction, ExtensionRuntimeStateAction, ExtensionStatusLabelAction, RemoteInstallAction, ExtensionStatusAction, LocalInstallAction, ButtonWithDropDownExtensionAction, InstallDropdownAction, InstallingLabelAction, ButtonWithDropdownExtensionActionViewItem, DropDownExtensionAction, WebInstallAction, MigrateDeprecatedExtensionAction, SetLanguageAction, ClearLanguageAction, UpdateAction } from './extensionsActions.js'; import { areSameExtensions } from '../../../../platform/extensionManagement/common/extensionManagementUtil.js'; -import { RatingsWidget, InstallCountWidget, RecommendationWidget, RemoteBadgeWidget, ExtensionPackCountWidget as ExtensionPackBadgeWidget, SyncIgnoredWidget, ExtensionHoverWidget, ExtensionRuntimeStatusWidget, PreReleaseBookmarkWidget, VerifiedPublisherWidget } from './extensionsWidgets.js'; +import { RatingsWidget, InstallCountWidget, RecommendationWidget, RemoteBadgeWidget, ExtensionPackCountWidget as ExtensionPackBadgeWidget, SyncIgnoredWidget, ExtensionHoverWidget, ExtensionRuntimeStatusWidget, PreReleaseBookmarkWidget, PublisherWidget, ExtensionKindIndicatorWidget } from './extensionsWidgets.js'; import { IExtensionService } from '../../../services/extensions/common/extensions.js'; import { IWorkbenchExtensionEnablementService } from '../../../services/extensionManagement/common/extensionManagement.js'; import { INotificationService } from '../../../../platform/notification/common/notification.js'; @@ -34,7 +33,6 @@ export interface ITemplateData { element: HTMLElement; icon: HTMLImageElement; name: HTMLElement; - publisherDisplayName: HTMLElement; description: HTMLElement; installCount: HTMLElement; ratings: HTMLElement; @@ -85,13 +83,12 @@ export class Renderer implements IPagedRenderer { const installCount = append(header, $('span.install-count')); const ratings = append(header, $('span.ratings')); const syncIgnore = append(header, $('span.sync-ignored')); + const extensionKindIndicator = append(header, $('span')); const activationStatus = append(header, $('span.activation-status')); const headerRemoteBadgeWidget = this.instantiationService.createInstance(RemoteBadgeWidget, header, false); const description = append(details, $('.description.ellipsis')); const footer = append(details, $('.footer')); - const publisher = append(footer, $('.author.ellipsis')); - const verifiedPublisherWidget = this.instantiationService.createInstance(VerifiedPublisherWidget, append(publisher, $(`.verified-publisher`)), true); - const publisherDisplayName = append(publisher, $('.publisher-name.ellipsis')); + const publisherWidget = this.instantiationService.createInstance(PublisherWidget, append(footer, $('.publisher-container')), true); const actionbar = new ActionBar(footer, { actionViewItemProvider: (action: IAction, options: IActionViewItemOptions) => { if (action instanceof ButtonWithDropDownExtensionAction) { @@ -140,12 +137,13 @@ export class Renderer implements IPagedRenderer { iconRemoteBadgeWidget, extensionPackBadgeWidget, headerRemoteBadgeWidget, - verifiedPublisherWidget, + publisherWidget, extensionHoverWidget, this.instantiationService.createInstance(SyncIgnoredWidget, syncIgnore), this.instantiationService.createInstance(ExtensionRuntimeStatusWidget, this.extensionViewState, activationStatus), this.instantiationService.createInstance(InstallCountWidget, installCount, true), this.instantiationService.createInstance(RatingsWidget, ratings, true), + this.instantiationService.createInstance(ExtensionKindIndicatorWidget, extensionKindIndicator, true), ]; const extensionContainers: ExtensionContainers = this.instantiationService.createInstance(ExtensionContainers, [...actions, ...widgets]); @@ -153,7 +151,7 @@ export class Renderer implements IPagedRenderer { const disposable = combinedDisposable(...actions, ...widgets, actionbar, actionBarListener, extensionContainers); return { - root, element, icon, name, installCount, ratings, description, publisherDisplayName, disposables: [disposable], actionbar, + root, element, icon, name, installCount, ratings, description, disposables: [disposable], actionbar, extensionDisposables: [], set extension(extension: IExtension) { extensionContainers.extension = extension; @@ -170,7 +168,6 @@ export class Renderer implements IPagedRenderer { data.icon.src = ''; data.name.textContent = ''; data.description.textContent = ''; - data.publisherDisplayName.textContent = ''; data.installCount.style.display = 'none'; data.ratings.style.display = 'none'; data.extension = null; @@ -209,12 +206,6 @@ export class Renderer implements IPagedRenderer { data.name.textContent = extension.displayName; data.description.textContent = extension.description; - const updatePublisher = () => { - data.publisherDisplayName.textContent = !extension.resourceExtension && extension.local?.source !== 'resource' ? extension.publisherDisplayName : ''; - }; - updatePublisher(); - Event.filter(this.extensionsWorkbenchService.onChange, e => !!e && areSameExtensions(e.identifier, extension.identifier))(() => updatePublisher(), this, data.extensionDisposables); - data.installCount.style.display = ''; data.ratings.style.display = ''; data.extension = extension; diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts index ee75bba1ed6f..9b840de9a61a 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViewlet.ts @@ -5,7 +5,7 @@ import './media/extensionsViewlet.css'; import { localize, localize2 } from '../../../../nls.js'; -import { timeout, Delayer, Promises } from '../../../../base/common/async.js'; +import { timeout, Delayer } from '../../../../base/common/async.js'; import { isCancellationError } from '../../../../base/common/errors.js'; import { createErrorWithActions } from '../../../../base/common/errorMessage.js'; import { IWorkbenchContribution } from '../../../common/contributions.js'; @@ -16,9 +16,9 @@ import { append, $, Dimension, hide, show, DragAndDropObserver, trackFocus, addD import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; import { IExtensionService } from '../../../services/extensions/common/extensions.js'; -import { IExtensionsWorkbenchService, IExtensionsViewPaneContainer, VIEWLET_ID, CloseExtensionDetailsOnViewChangeKey, INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID, WORKSPACE_RECOMMENDATIONS_VIEW_ID, AutoCheckUpdatesConfigurationKey, OUTDATED_EXTENSIONS_VIEW_ID, CONTEXT_HAS_GALLERY, extensionsSearchActionsMenu, AutoRestartConfigurationKey } from '../common/extensions.js'; +import { IExtensionsWorkbenchService, IExtensionsViewPaneContainer, VIEWLET_ID, CloseExtensionDetailsOnViewChangeKey, INSTALL_EXTENSION_FROM_VSIX_COMMAND_ID, WORKSPACE_RECOMMENDATIONS_VIEW_ID, AutoCheckUpdatesConfigurationKey, OUTDATED_EXTENSIONS_VIEW_ID, CONTEXT_HAS_GALLERY, extensionsSearchActionsMenu, AutoRestartConfigurationKey, ExtensionRuntimeActionType } from '../common/extensions.js'; import { InstallLocalExtensionsInRemoteAction, InstallRemoteExtensionsInLocalAction } from './extensionsActions.js'; -import { IExtensionManagementService } from '../../../../platform/extensionManagement/common/extensionManagement.js'; +import { IExtensionManagementService, ILocalExtension } from '../../../../platform/extensionManagement/common/extensionManagement.js'; import { IWorkbenchExtensionEnablementService, IExtensionManagementServerService, IExtensionManagementServer } from '../../../services/extensionManagement/common/extensionManagement.js'; import { ExtensionsInput } from '../common/extensionsInput.js'; import { ExtensionsListView, EnabledExtensionsView, DisabledExtensionsView, RecommendedExtensionsView, WorkspaceRecommendedExtensionsView, ServerInstalledExtensionsView, DefaultRecommendedExtensionsView, UntrustedWorkspaceUnsupportedExtensionsView, UntrustedWorkspacePartiallySupportedExtensionsView, VirtualWorkspaceUnsupportedExtensionsView, VirtualWorkspacePartiallySupportedExtensionsView, DefaultPopularExtensionsView, DeprecatedExtensionsView, SearchMarketplaceExtensionsView, RecentlyUpdatedExtensionsView, OutdatedExtensionsView, StaticQueryExtensionsView, NONE_CATEGORY } from './extensionsViews.js'; @@ -42,14 +42,13 @@ import { ViewPane } from '../../../browser/parts/views/viewPane.js'; import { Query } from '../common/extensionQuery.js'; import { SuggestEnabledInput } from '../../codeEditor/browser/suggestEnabledInput/suggestEnabledInput.js'; import { alert } from '../../../../base/browser/ui/aria/aria.js'; -import { EXTENSION_CATEGORIES, ExtensionType } from '../../../../platform/extensions/common/extensions.js'; +import { EXTENSION_CATEGORIES } from '../../../../platform/extensions/common/extensions.js'; import { Registry } from '../../../../platform/registry/common/platform.js'; import { ILabelService } from '../../../../platform/label/common/label.js'; import { MementoObject } from '../../../common/memento.js'; import { SyncDescriptor } from '../../../../platform/instantiation/common/descriptors.js'; import { IPreferencesService } from '../../../services/preferences/common/preferences.js'; import { SIDE_BAR_DRAG_AND_DROP_BACKGROUND } from '../../../common/theme.js'; -import { IWorkbenchEnvironmentService } from '../../../services/environment/common/environmentService.js'; import { VirtualWorkspaceContext, WorkbenchStateContext } from '../../../common/contextkeys.js'; import { ICommandService } from '../../../../platform/commands/common/commands.js'; import { installLocalInRemoteIcon } from './extensionsIcons.js'; @@ -59,12 +58,11 @@ import { IPaneCompositePartService } from '../../../services/panecomposite/brows import { coalesce } from '../../../../base/common/arrays.js'; import { extractEditorsAndFilesDropData } from '../../../../platform/dnd/browser/dnd.js'; import { extname } from '../../../../base/common/resources.js'; -import { isMalicious } from '../../../../platform/extensionManagement/common/extensionManagementUtil.js'; import { ILocalizedString } from '../../../../platform/action/common/action.js'; import { registerNavigableContainer } from '../../../browser/actions/widgetNavigationCommands.js'; import { MenuWorkbenchToolBar } from '../../../../platform/actions/browser/toolbar.js'; import { createActionViewItem } from '../../../../platform/actions/browser/menuEntryActionViewItem.js'; -import { SeverityIcon } from '../../../../platform/severityIcon/browser/severityIcon.js'; +import { SeverityIcon } from '../../../../base/browser/ui/severityIcon/severityIcon.js'; import { StandardKeyboardEvent } from '../../../../base/browser/keyboardEvent.js'; import { KeyCode } from '../../../../base/common/keyCodes.js'; import { ThemeIcon } from '../../../../base/common/themables.js'; @@ -972,14 +970,12 @@ export class MaliciousExtensionChecker implements IWorkbenchContribution { constructor( @IExtensionManagementService private readonly extensionsManagementService: IExtensionManagementService, + @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, @IHostService private readonly hostService: IHostService, @ILogService private readonly logService: ILogService, @INotificationService private readonly notificationService: INotificationService, - @IWorkbenchEnvironmentService private readonly environmentService: IWorkbenchEnvironmentService ) { - if (!this.environmentService.disableExtensions) { - this.loopCheckForMaliciousExtensions(); - } + this.loopCheckForMaliciousExtensions(); } private loopCheckForMaliciousExtensions(): void { @@ -988,31 +984,36 @@ export class MaliciousExtensionChecker implements IWorkbenchContribution { .then(() => this.loopCheckForMaliciousExtensions()); } - private checkForMaliciousExtensions(): Promise { - return this.extensionsManagementService.getExtensionsControlManifest().then(extensionsControlManifest => { - - return this.extensionsManagementService.getInstalled(ExtensionType.User).then(installed => { - const maliciousExtensions = installed.filter(e => isMalicious(e.identifier, extensionsControlManifest)); - - if (maliciousExtensions.length) { - return Promises.settled(maliciousExtensions.map(e => this.extensionsManagementService.uninstall(e).then(() => { - this.notificationService.prompt( - Severity.Warning, - localize('malicious warning', "We have uninstalled '{0}' which was reported to be problematic.", e.identifier.id), - [{ - label: localize('reloadNow', "Reload Now"), - run: () => this.hostService.reload() - }], - { - sticky: true, - priority: NotificationPriority.URGENT - } - ); - }))); - } else { - return Promise.resolve(undefined); + private async checkForMaliciousExtensions(): Promise { + try { + const maliciousExtensions: ILocalExtension[] = []; + let shouldRestartExtensions = false; + let shouldReloadWindow = false; + for (const extension of this.extensionsWorkbenchService.installed) { + if (extension.isMalicious && extension.local) { + maliciousExtensions.push(extension.local); + shouldRestartExtensions = shouldRestartExtensions || extension.runtimeState?.action === ExtensionRuntimeActionType.RestartExtensions; + shouldReloadWindow = shouldReloadWindow || extension.runtimeState?.action === ExtensionRuntimeActionType.ReloadWindow; } - }).then(() => undefined); - }, err => this.logService.error(err)); + } + if (maliciousExtensions.length) { + await this.extensionsManagementService.uninstallExtensions(maliciousExtensions.map(e => ({ extension: e, options: { remove: true } }))); + this.notificationService.prompt( + Severity.Warning, + localize('malicious warning', "The following extensions were found to be problematic and have been uninstalled: {0}", maliciousExtensions.map(e => e.identifier.id).join(', ')), + shouldRestartExtensions || shouldReloadWindow ? [{ + label: shouldRestartExtensions ? localize('restartNow', "Restart Extensions") : localize('reloadNow', "Reload Now"), + run: () => shouldRestartExtensions ? this.extensionsWorkbenchService.updateRunningExtensions() : this.hostService.reload() + }] : [], + { + sticky: true, + priority: NotificationPriority.URGENT + } + ); + } + + } catch (err) { + this.logService.error(err); + } } } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts index f970b351e4e7..dd378644f9ca 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsViews.ts @@ -39,7 +39,7 @@ import { IAction, Action, Separator, ActionRunner } from '../../../../base/commo import { ExtensionIdentifier, ExtensionIdentifierMap, ExtensionUntrustedWorkspaceSupportType, ExtensionVirtualWorkspaceSupportType, IExtensionDescription, IExtensionIdentifier, isLanguagePackExtension } from '../../../../platform/extensions/common/extensions.js'; import { CancelablePromise, createCancelablePromise, ThrottledDelayer } from '../../../../base/common/async.js'; import { IProductService } from '../../../../platform/product/common/productService.js'; -import { SeverityIcon } from '../../../../platform/severityIcon/browser/severityIcon.js'; +import { SeverityIcon } from '../../../../base/browser/ui/severityIcon/severityIcon.js'; import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { IViewDescriptorService, ViewContainerLocation } from '../../../common/views.js'; import { IOpenerService } from '../../../../platform/opener/common/opener.js'; @@ -144,7 +144,7 @@ export class ExtensionsListView extends ViewPane { @IExtensionService private readonly extensionService: IExtensionService, @IExtensionsWorkbenchService protected extensionsWorkbenchService: IExtensionsWorkbenchService, @IExtensionRecommendationsService protected extensionRecommendationsService: IExtensionRecommendationsService, - @ITelemetryService telemetryService: ITelemetryService, + @ITelemetryService protected readonly telemetryService: ITelemetryService, @IHoverService hoverService: IHoverService, @IConfigurationService configurationService: IConfigurationService, @IWorkspaceContextService protected contextService: IWorkspaceContextService, @@ -169,7 +169,7 @@ export class ExtensionsListView extends ViewPane { ...(viewletViewOptions as IViewPaneOptions), showActions: ViewPaneShowActions.Always, maximumBodySize: options.flexibleHeight ? (storageService.getNumber(`${viewletViewOptions.id}.size`, StorageScope.PROFILE, 0) ? undefined : 0) : undefined - }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService, hoverService); + }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, hoverService); if (this.options.onDidChangeTitle) { this._register(this.options.onDidChangeTitle(title => this.updateTitle(title))); } diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts index 17bba8cc4fa6..20325ef00d3f 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWidgets.ts @@ -22,7 +22,7 @@ import { IInstantiationService } from '../../../../platform/instantiation/common import { CountBadge } from '../../../../base/browser/ui/countBadge/countBadge.js'; import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { IUserDataSyncEnablementService } from '../../../../platform/userDataSync/common/userDataSync.js'; -import { activationTimeIcon, errorIcon, infoIcon, installCountIcon, preReleaseIcon, ratingIcon, remoteIcon, sponsorIcon, starEmptyIcon, starFullIcon, starHalfIcon, syncIgnoredIcon, warningIcon } from './extensionsIcons.js'; +import { activationTimeIcon, errorIcon, infoIcon, installCountIcon, preReleaseIcon, privateExtensionIcon, ratingIcon, remoteIcon, sponsorIcon, starEmptyIcon, starFullIcon, starHalfIcon, syncIgnoredIcon, warningIcon } from './extensionsIcons.js'; import { registerColor, textLinkForeground } from '../../../../platform/theme/common/colorRegistry.js'; import { IHoverService } from '../../../../platform/hover/browser/hover.js'; import { HoverPosition } from '../../../../base/browser/ui/hover/hoverWidget.js'; @@ -46,6 +46,10 @@ import { Registry } from '../../../../platform/registry/common/platform.js'; import { Extensions, IExtensionFeaturesManagementService, IExtensionFeaturesRegistry } from '../../../services/extensionManagement/common/extensionFeatures.js'; import { ExtensionIdentifier } from '../../../../platform/extensions/common/extensions.js'; import { extensionVerifiedPublisherIconColor, verifiedPublisherIcon } from '../../../services/extensionManagement/common/extensionsIcons.js'; +import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js'; +import { IExplorerService } from '../../files/browser/files.js'; +import { IViewsService } from '../../../services/views/common/viewsService.js'; +import { VIEW_ID as EXPLORER_VIEW_ID } from '../../files/common/files.js'; export abstract class ExtensionWidget extends Disposable implements IExtensionContainer { private _extension: IExtension | null = null; @@ -71,17 +75,26 @@ export function onClick(element: HTMLElement, callback: () => void): IDisposable export class InstallCountWidget extends ExtensionWidget { + private readonly disposables = this._register(new DisposableStore()); + constructor( - private container: HTMLElement, + readonly container: HTMLElement, private small: boolean, + @IHoverService private readonly hoverService: IHoverService, ) { super(); - container.classList.add('extension-install-count'); this.render(); + + this._register(toDisposable(() => this.clear())); } - render(): void { + private clear(): void { this.container.innerText = ''; + this.disposables.clear(); + } + + render(): void { + this.clear(); if (!this.extension) { return; @@ -96,9 +109,18 @@ export class InstallCountWidget extends ExtensionWidget { return; } - append(this.container, $('span' + ThemeIcon.asCSSSelector(installCountIcon))); - const count = append(this.container, $('span.count')); + if (!this.small && !this.extension.url) { + return; + } + + const parent = this.small ? this.container : append(this.container, $('span.install', { tabIndex: 0 })); + append(parent, $('span' + ThemeIcon.asCSSSelector(installCountIcon))); + const count = append(parent, $('span.count')); count.textContent = installLabel; + + if (!this.small) { + this.disposables.add(this.hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), this.container, localize('install count', "Install count"))); + } } static getInstallLabel(extension: IExtension, small: boolean): string | undefined { @@ -129,12 +151,14 @@ export class InstallCountWidget extends ExtensionWidget { export class RatingsWidget extends ExtensionWidget { - private readonly containerHover: IManagedHover; + private containerHover: IManagedHover | undefined; + private readonly disposables = this._register(new DisposableStore()); constructor( - private container: HTMLElement, + readonly container: HTMLElement, private small: boolean, - @IHoverService hoverService: IHoverService + @IHoverService private readonly hoverService: IHoverService, + @IOpenerService private readonly openerService: IOpenerService, ) { super(); container.classList.add('extension-ratings'); @@ -143,13 +167,17 @@ export class RatingsWidget extends ExtensionWidget { container.classList.add('small'); } - this.containerHover = this._register(hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), container, '')); - this.render(); + this._register(toDisposable(() => this.clear())); } - render(): void { + private clear(): void { this.container.innerText = ''; + this.disposables.clear(); + } + + render(): void { + this.clear(); if (!this.extension) { return; @@ -167,51 +195,71 @@ export class RatingsWidget extends ExtensionWidget { return; } + if (!this.extension.url) { + return; + } + const rating = Math.round(this.extension.rating * 2) / 2; - this.containerHover.update(localize('ratedLabel', "Average rating: {0} out of 5", rating)); if (this.small) { append(this.container, $('span' + ThemeIcon.asCSSSelector(starFullIcon))); const count = append(this.container, $('span.count')); count.textContent = String(rating); } else { + const element = append(this.container, $('span.rating.clickable', { tabIndex: 0 })); for (let i = 1; i <= 5; i++) { if (rating >= i) { - append(this.container, $('span' + ThemeIcon.asCSSSelector(starFullIcon))); + append(element, $('span' + ThemeIcon.asCSSSelector(starFullIcon))); } else if (rating >= i - 0.5) { - append(this.container, $('span' + ThemeIcon.asCSSSelector(starHalfIcon))); + append(element, $('span' + ThemeIcon.asCSSSelector(starHalfIcon))); } else { - append(this.container, $('span' + ThemeIcon.asCSSSelector(starEmptyIcon))); + append(element, $('span' + ThemeIcon.asCSSSelector(starEmptyIcon))); } } if (this.extension.ratingCount) { - const ratingCountElemet = append(this.container, $('span', undefined, ` (${this.extension.ratingCount})`)); + const ratingCountElemet = append(element, $('span', undefined, ` (${this.extension.ratingCount})`)); ratingCountElemet.style.paddingLeft = '1px'; } + + this.containerHover = this._register(this.hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), element, '')); + this.containerHover.update(localize('ratedLabel', "Average rating: {0} out of 5", rating)); + element.setAttribute('role', 'link'); + if (this.extension.ratingUrl) { + this.disposables.add(onClick(element, () => this.openerService.open(URI.parse(this.extension!.ratingUrl!)))); + } } } + } -export class VerifiedPublisherWidget extends ExtensionWidget { +export class PublisherWidget extends ExtensionWidget { + + private element: HTMLElement | undefined; + private containerHover: IManagedHover | undefined; private readonly disposables = this._register(new DisposableStore()); - private readonly containerHover: IManagedHover; constructor( - private container: HTMLElement, + readonly container: HTMLElement, private small: boolean, - @IHoverService hoverService: IHoverService, + @IExtensionsWorkbenchService private readonly extensionsWorkbenchService: IExtensionsWorkbenchService, + @IHoverService private readonly hoverService: IHoverService, @IOpenerService private readonly openerService: IOpenerService, ) { super(); - this.containerHover = this._register(hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), container, '')); + this.render(); + this._register(toDisposable(() => this.clear())); } - render(): void { - reset(this.container); + private clear(): void { + this.element?.remove(); this.disposables.clear(); - if (!this.extension?.publisherDomain?.verified) { + } + + render(): void { + this.clear(); + if (!this.extension) { return; } @@ -223,20 +271,45 @@ export class VerifiedPublisherWidget extends ExtensionWidget { return; } - const publisherDomainLink = URI.parse(this.extension.publisherDomain.link); - const verifiedPublisher = append(this.container, $('span.extension-verified-publisher.clickable')); - append(verifiedPublisher, renderIcon(verifiedPublisherIcon)); + this.element = append(this.container, $('.publisher')); + const publisherDisplayName = $('.publisher-name.ellipsis'); + publisherDisplayName.textContent = this.extension.publisherDisplayName; - if (!this.small) { - verifiedPublisher.tabIndex = 0; - this.containerHover.update(`Verified Domain: ${this.extension.publisherDomain.link}`); - verifiedPublisher.setAttribute('role', 'link'); + const verifiedPublisher = $('.verified-publisher'); + append(verifiedPublisher, $('span.extension-verified-publisher.clickable'), renderIcon(verifiedPublisherIcon)); + + if (this.small) { + if (this.extension.publisherDomain) { + append(this.element, verifiedPublisher); + } + append(this.element, publisherDisplayName); + } else { + this.element.classList.toggle('clickable', !!this.extension.url); + this.element.setAttribute('role', 'button'); + this.element.tabIndex = 0; + + this.containerHover = this.disposables.add(this.hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), this.element, localize('publisher', "Publisher ({0})", this.extension.publisherDisplayName))); + append(this.element, publisherDisplayName); + + if (this.extension.publisherDomain) { + append(this.element, verifiedPublisher); + const publisherDomainLink = URI.parse(this.extension.publisherDomain.link); + verifiedPublisher.tabIndex = 0; + verifiedPublisher.setAttribute('role', 'button'); + this.containerHover.update(localize('verified publisher', "This publisher has verified ownership of {0}", this.extension.publisherDomain.link)); + verifiedPublisher.setAttribute('role', 'link'); + + append(verifiedPublisher, $('span.extension-verified-publisher-domain', undefined, publisherDomainLink.authority.startsWith('www.') ? publisherDomainLink.authority.substring(4) : publisherDomainLink.authority)); + this.disposables.add(onClick(verifiedPublisher, () => this.openerService.open(publisherDomainLink))); + } - append(verifiedPublisher, $('span.extension-verified-publisher-domain', undefined, publisherDomainLink.authority.startsWith('www.') ? publisherDomainLink.authority.substring(4) : publisherDomainLink.authority)); - this.disposables.add(onClick(verifiedPublisher, () => this.openerService.open(publisherDomainLink))); + if (this.extension.url) { + this.disposables.add(onClick(this.element, () => this.extensionsWorkbenchService.openSearch(`publisher:"${this.extension?.publisherDisplayName}"`))); + } } } + } export class SponsorWidget extends ExtensionWidget { @@ -244,7 +317,7 @@ export class SponsorWidget extends ExtensionWidget { private readonly disposables = this._register(new DisposableStore()); constructor( - private container: HTMLElement, + readonly container: HTMLElement, @IHoverService private readonly hoverService: IHoverService, @IOpenerService private readonly openerService: IOpenerService, ) { @@ -445,6 +518,73 @@ export class ExtensionPackCountWidget extends ExtensionWidget { } } +export class ExtensionKindIndicatorWidget extends ExtensionWidget { + + private element: HTMLElement | undefined; + + private readonly disposables = this._register(new DisposableStore()); + + constructor( + readonly container: HTMLElement, + private small: boolean, + @IHoverService private readonly hoverService: IHoverService, + @IWorkspaceContextService private readonly contextService: IWorkspaceContextService, + @IUriIdentityService private readonly uriIdentityService: IUriIdentityService, + @IExplorerService private readonly explorerService: IExplorerService, + @IViewsService private readonly viewsService: IViewsService, + ) { + super(); + this.render(); + this._register(toDisposable(() => this.clear())); + } + + private clear(): void { + this.element?.remove(); + this.disposables.clear(); + } + + render(): void { + this.clear(); + + if (this.small) { + return; + } + + if (!this.extension) { + return; + } + + if (this.extension?.private) { + this.element = append(this.container, $('.extension-kind-indicator')); + append(this.element, $('span' + ThemeIcon.asCSSSelector(privateExtensionIcon))); + if (!this.small) { + append(this.element, $('span.private-extension-label', undefined, localize('privateExtension', "Private Extension"))); + } + return; + } + + const location = this.extension.resourceExtension?.location ?? (this.extension.local?.source === 'resource' ? this.extension.local?.location : undefined); + if (!location) { + return; + } + + this.element = append(this.container, $('.extension-kind-indicator')); + const workspaceFolder = this.contextService.getWorkspaceFolder(location); + if (workspaceFolder && this.extension.isWorkspaceScoped) { + this.element.textContent = localize('workspace extension', "Workspace Extension"); + this.element.classList.add('clickable'); + this.element.setAttribute('role', 'button'); + this.disposables.add(this.hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), this.element, this.uriIdentityService.extUri.relativePath(workspaceFolder.uri, location))); + this.disposables.add(onClick(this.element, () => { + this.viewsService.openView(EXPLORER_VIEW_ID, true).then(() => this.explorerService.select(location, true)); + })); + } else { + this.disposables.add(this.hoverService.setupManagedHover(getDefaultHoverDelegate('mouse'), this.element, location.path)); + this.element.textContent = localize('local extension', "Local Extension"); + } + } +} + export class SyncIgnoredWidget extends ExtensionWidget { private readonly disposables = this._register(new DisposableStore()); @@ -555,7 +695,7 @@ export class ExtensionHoverWidget extends ExtensionWidget { this.hover.value = this.hoverService.setupManagedHover({ delay: this.configurationService.getValue('workbench.hover.delay'), showHover: (options, focus) => { - return this.hoverService.showHover({ + return this.hoverService.showInstantHover({ ...options, additionalClasses: ['extension-hover'], position: { @@ -595,8 +735,12 @@ export class ExtensionHoverWidget extends ExtensionWidget { } markdown.appendText(`\n`); + let addSeparator = false; + if (this.extension.private) { + markdown.appendMarkdown(`$(${privateExtensionIcon.id}) ${localize('privateExtension', "Private Extension")}`); + addSeparator = true; + } if (this.extension.state === ExtensionState.Installed) { - let addSeparator = false; const installLabel = InstallCountWidget.getInstallLabel(this.extension, true); if (installLabel) { if (addSeparator) { @@ -620,9 +764,9 @@ export class ExtensionHoverWidget extends ExtensionWidget { markdown.appendMarkdown(`$(${sponsorIcon.id}) [${localize('sponsor', "Sponsor")}](${this.extension.publisherSponsorLink})`); addSeparator = true; } - if (addSeparator) { - markdown.appendText(`\n`); - } + } + if (addSeparator) { + markdown.appendText(`\n`); } const location = this.extension.resourceExtension?.location ?? (this.extension.local?.source === 'resource' ? this.extension.local?.location : undefined); @@ -862,6 +1006,7 @@ export class ExtensionRecommendationWidget extends ExtensionWidget { export const extensionRatingIconColor = registerColor('extensionIcon.starForeground', { light: '#DF6100', dark: '#FF8E00', hcDark: '#FF8E00', hcLight: textLinkForeground }, localize('extensionIconStarForeground', "The icon color for extension ratings."), false); export const extensionPreReleaseIconColor = registerColor('extensionIcon.preReleaseForeground', { dark: '#1d9271', light: '#1d9271', hcDark: '#1d9271', hcLight: textLinkForeground }, localize('extensionPreReleaseForeground', "The icon color for pre-release extension."), false); export const extensionSponsorIconColor = registerColor('extensionIcon.sponsorForeground', { light: '#B51E78', dark: '#D758B3', hcDark: null, hcLight: '#B51E78' }, localize('extensionIcon.sponsorForeground', "The icon color for extension sponsor."), false); +export const extensionPrivateBadgeBackground = registerColor('extensionIcon.privateForeground', { dark: '#ffffff60', light: '#00000060', hcDark: '#ffffff60', hcLight: '#00000060' }, localize('extensionIcon.private', "The icon color for private extensions.")); registerThemingParticipant((theme, collector) => { const extensionRatingIcon = theme.getColor(extensionRatingIconColor); @@ -877,4 +1022,9 @@ registerThemingParticipant((theme, collector) => { collector.addRule(`.monaco-hover.extension-hover .markdown-hover .hover-contents ${ThemeIcon.asCSSSelector(sponsorIcon)} { color: var(--vscode-extensionIcon-sponsorForeground); }`); collector.addRule(`.extension-editor > .header > .details > .subtitle .sponsor ${ThemeIcon.asCSSSelector(sponsorIcon)} { color: var(--vscode-extensionIcon-sponsorForeground); }`); + + const privateBadgeBackground = theme.getColor(extensionPrivateBadgeBackground); + if (privateBadgeBackground) { + collector.addRule(`.extension-private-badge { color: ${privateBadgeBackground}; }`); + } }); diff --git a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts index 41bcd531cfaf..efd8a2732f13 100644 --- a/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts +++ b/src/vs/workbench/contrib/extensions/browser/extensionsWorkbenchService.ts @@ -63,11 +63,10 @@ import { areApiProposalsCompatible, isEngineValid } from '../../../../platform/e import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js'; import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; import { ShowCurrentReleaseNotesActionId } from '../../update/common/update.js'; -import { Registry } from '../../../../platform/registry/common/platform.js'; -import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from '../../../../platform/configuration/common/configurationRegistry.js'; import { IViewsService } from '../../../services/views/common/viewsService.js'; import { IQuickInputService } from '../../../../platform/quickinput/common/quickInput.js'; import { IMarkdownString, MarkdownString } from '../../../../base/common/htmlContent.js'; +import { IExtensionGalleryManifestService } from '../../../../platform/extensionManagement/common/extensionGalleryManifest.js'; interface IExtensionStateProvider { (extension: Extension): T; @@ -198,11 +197,7 @@ export class Extension implements IExtension { } get publisherUrl(): URI | undefined { - if (!this.productService.extensionsGallery || !this.gallery) { - return undefined; - } - - return resources.joinPath(URI.parse(this.productService.extensionsGallery.publisherUrl), this.publisher); + return this.gallery?.publisherLink ? URI.parse(this.gallery.publisherLink) : undefined; } get publisherDomain(): { link: string; verified: boolean } | undefined { @@ -217,6 +212,10 @@ export class Extension implements IExtension { return this.local ? this.local.manifest.version : this.latestVersion; } + get private(): boolean { + return this.local ? this.local.private : this.gallery ? this.gallery.private : false; + } + get pinned(): boolean { return !!this.local?.pinned; } @@ -230,11 +229,7 @@ export class Extension implements IExtension { } get url(): string | undefined { - if (!this.productService.extensionsGallery || !this.gallery) { - return undefined; - } - - return `${this.productService.extensionsGallery.itemUrl}?itemName=${this.publisher}.${this.name}`; + return this.gallery?.detailsLink; } get iconUrl(): string { @@ -297,7 +292,11 @@ export class Extension implements IExtension { return this.stateProvider(this); } - public isMalicious: boolean = false; + private malicious: boolean = false; + public get isMalicious(): boolean { + return this.malicious || this.enablementState === EnablementState.DisabledByMalicious; + } + public deprecationInfo: IDeprecationInfo | undefined; get installCount(): number | undefined { @@ -312,6 +311,10 @@ export class Extension implements IExtension { return this.gallery ? this.gallery.ratingCount : undefined; } + get ratingUrl(): string | undefined { + return this.gallery?.ratingLink; + } + get outdated(): boolean { try { if (!this.gallery || !this.local) { @@ -551,7 +554,7 @@ ${this.description} } setExtensionsControlManifest(extensionsControlManifest: IExtensionsControlManifest): void { - this.isMalicious = isMalicious(this.identifier, extensionsControlManifest); + this.malicious = isMalicious(this.identifier, extensionsControlManifest.malicious); this.deprecationInfo = extensionsControlManifest.deprecated ? extensionsControlManifest.deprecated[this.identifier.id.toLowerCase()] : undefined; this._extensionEnabledWithPreRelease = extensionsControlManifest?.extensionsEnabledWithPreRelease?.includes(this.identifier.id.toLowerCase()); } @@ -628,8 +631,8 @@ class Extensions extends Disposable { } } - private _local: IExtension[] | undefined; - get local(): IExtension[] { + private _local: Extension[] | undefined; + get local(): Extension[] { if (!this._local) { this._local = []; for (const extension of this.installed) { @@ -884,8 +887,8 @@ class Extensions extends Disposable { if (extension.local) { const enablementState = this.extensionEnablementService.getEnablementState(extension.local); if (enablementState !== extension.enablementState) { - (extension as Extension).enablementState = enablementState; - this._onChange.fire({ extension: extension as Extension }); + extension.enablementState = enablementState; + this._onChange.fire({ extension }); } } } @@ -940,6 +943,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension @IEditorService private readonly editorService: IEditorService, @IWorkbenchExtensionManagementService private readonly extensionManagementService: IWorkbenchExtensionManagementService, @IExtensionGalleryService private readonly galleryService: IExtensionGalleryService, + @IExtensionGalleryManifestService private readonly extensionGalleryManifestService: IExtensionGalleryManifestService, @IConfigurationService private readonly configurationService: IConfigurationService, @ITelemetryService private readonly telemetryService: ITelemetryService, @INotificationService private readonly notificationService: INotificationService, @@ -1020,30 +1024,9 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension urlService.registerHandler(this); - if (this.productService.quality !== 'stable') { - this.registerAutoRestartConfig(); - } - this.whenInitialized = this.initialize(); } - private registerAutoRestartConfig(): void { - Registry.as(ConfigurationExtensions.Configuration) - .registerConfiguration({ - id: 'extensions', - order: 30, - title: nls.localize('extensionsConfigurationTitle', "Extensions"), - type: 'object', - properties: { - [AutoRestartConfigurationKey]: { - type: 'boolean', - description: nls.localize('autoRestart', "If activated, extensions will automatically restart following an update if the window is not in focus. There can be a data loss if you have open Notebooks or Custom Editors."), - default: false, - } - } - }); - } - private async initialize(): Promise { // initialize local extensions await Promise.all([this.queryLocal(), this.extensionService.whenInstalledExtensionsRegistered()]); @@ -2301,7 +2284,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension } if (extension.gallery) { - if (!extension.gallery.isSigned) { + if (!extension.gallery.isSigned && (await this.extensionGalleryManifestService.getExtensionGalleryManifest())?.capabilities.signing?.allRepositorySigned) { return new MarkdownString().appendText(nls.localize('not signed', "This extension is not signed.")); } @@ -2333,6 +2316,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension async install(arg: string | URI | IExtension, installOptions: InstallExtensionOptions = {}, progressLocation?: ProgressLocation | string): Promise { let installable: URI | IGalleryExtension | IResourceExtension | undefined; let extension: IExtension | undefined; + let servers: IExtensionManagementServer[] | undefined; if (arg instanceof URI) { installable = arg; @@ -2379,11 +2363,11 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension // If requested to install everywhere // then install the extension in all the servers where it is not installed if (installOptions.installEverywhere) { - installOptions.servers = []; + servers = []; const installableServers = await this.extensionManagementService.getInstallableServers(gallery); for (const extensionsServer of this.extensionsServers) { if (installableServers.includes(extensionsServer.server) && !extensionsServer.local.find(e => areSameExtensions(e.identifier, gallery.identifier))) { - installOptions.servers.push(extensionsServer.server); + servers.push(extensionsServer.server); } } } @@ -2391,17 +2375,17 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension // Check if the extension is disabled because of extension kind // If so, install the extension in the server that is compatible. else if (installOptions.enable && extension?.local) { - installOptions.servers = []; + servers = []; if (extension.enablementState === EnablementState.DisabledByExtensionKind) { const [installableServer] = await this.extensionManagementService.getInstallableServers(gallery); if (installableServer) { - installOptions.servers.push(installableServer); + servers.push(installableServer); } } } } - if (!installOptions.servers || installOptions.servers.length) { + if (!servers || servers.length) { if (!installable) { if (!gallery) { const id = isString(arg) ? arg : (arg).identifier.id; @@ -2458,7 +2442,7 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension if (extension.resourceExtension) { extension = await this.doInstall(extension, () => this.extensionManagementService.installResourceExtension(installable as IResourceExtension, installOptions), progressLocation); } else { - extension = await this.doInstall(extension, () => this.installFromGallery(extension!, installable as IGalleryExtension, installOptions), progressLocation); + extension = await this.doInstall(extension, () => this.installFromGallery(extension!, installable as IGalleryExtension, installOptions, servers), progressLocation); } } } @@ -2762,15 +2746,15 @@ export class ExtensionsWorkbenchService extends Disposable implements IExtension return this.extensionManagementService.installVSIX(vsix, manifest, installOptions); } - private installFromGallery(extension: IExtension, gallery: IGalleryExtension, installOptions?: InstallExtensionOptions): Promise { + private installFromGallery(extension: IExtension, gallery: IGalleryExtension, installOptions: InstallExtensionOptions, servers: IExtensionManagementServer[] | undefined): Promise { installOptions = installOptions ?? {}; installOptions.pinned = extension.local?.pinned || !this.shouldAutoUpdateExtension(extension); - if (extension.local && !installOptions.servers) { + if (extension.local && !servers) { installOptions.productVersion = this.getProductVersion(); installOptions.operation = InstallOperation.Update; return this.extensionManagementService.updateFromGallery(gallery, extension.local, installOptions); } else { - return this.extensionManagementService.installFromGallery(gallery, installOptions); + return this.extensionManagementService.installFromGallery(gallery, installOptions, servers); } } diff --git a/src/vs/workbench/contrib/extensions/browser/media/extension.css b/src/vs/workbench/contrib/extensions/browser/media/extension.css index 30bbba041adb..df7aaffb5e90 100644 --- a/src/vs/workbench/contrib/extensions/browser/media/extension.css +++ b/src/vs/workbench/contrib/extensions/browser/media/extension.css @@ -92,11 +92,17 @@ .extension-list-item > .details > .header-container > .header > .activation-status, .extension-list-item > .details > .header-container > .header > .install-count, +.extension-list-item > .details > .header-container > .header .extension-kind-indicator, .extension-list-item > .details > .header-container > .header > .ratings { display: flex; align-items: center; } +.extension-list-item > .details > .header-container > .header .extension-kind-indicator { + font-size: 80%; + margin-left: 2px; +} + .extension-list-item > .details > .header-container > .header > .install-count:not(:empty) { font-size: 80%; margin: 0 6px; @@ -179,24 +185,27 @@ align-items: center; } -.extension-list-item > .details > .footer > .author { +.extension-list-item > .details > .footer > .publisher-container { flex: 1; + line-height: 24px; +} + +.extension-list-item > .details > .footer .publisher { display: flex; align-items: center; - line-height: 24px; } -.extension-list-item > .details > .footer > .author > .publisher-name { +.extension-list-item > .details > .footer .publisher > .publisher-name { font-size: 90%; color: var(--vscode-descriptionForeground); font-weight: 600; } -.monaco-list-row.selected .extension-list-item > .details > .footer > .author > .publisher-name{ +.monaco-list-row.selected .extension-list-item > .details > .footer .publisher > .publisher-name{ color: unset; } -.extension-list-item > .details > .footer > .author > .publisher-name:not(:first-child) { +.extension-list-item > .details > .footer .publisher > .publisher-name:not(:first-child) { padding-left: 1px; } @@ -227,7 +236,7 @@ min-width: 0; } -.monaco-list-row.disabled:not(.selected) .extension-list-item > .details > .footer > .author > .publisher-name { +.monaco-list-row.disabled:not(.selected) .extension-list-item > .details > .footer .publisher > .publisher-name { color: var(--vscode-disabledForeground); } diff --git a/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css b/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css index 03023ddf5fef..62be7a0b84a1 100644 --- a/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css +++ b/src/vs/workbench/contrib/extensions/browser/media/extensionEditor.css @@ -148,6 +148,7 @@ } .extension-editor > .header > .details > .subtitle, +.extension-editor > .header > .details > .subtitle .extension-kind-indicator, .extension-editor > .header > .details > .subtitle .install, .extension-editor > .header > .details > .subtitle .rating, .extension-editor > .header > .details > .subtitle .sponsor { @@ -163,10 +164,14 @@ margin-left: 6px; } -.extension-editor > .header > .details > .subtitle > div:not(:first-child):not(:empty):not(.resource) { - border-left: 1px solid rgba(128, 128, 128, 0.7); - margin-left: 14px; - padding-left: 14px; +.extension-editor > .header > .details > .subtitle .extension-kind-indicator > .codicon { + margin-right: 6px; +} + +.extension-editor > .header > .details > .subtitle > .subtitle-entry:not(:empty):not(.last-non-empty) { + border-right: 1px solid rgba(128, 128, 128, 0.7); + margin-right: 14px; + padding-right: 14px; } .extension-editor > .header > .details > .description { @@ -734,7 +739,7 @@ padding-top: 5px; } -.extension-editor .subcontent .monaco-list-row .extension > .details > .footer > .author { +.extension-editor .subcontent .monaco-list-row .extension > .details > .footer .publisher { font-size: 90%; font-weight: 600; opacity: 0.6; diff --git a/src/vs/workbench/contrib/extensions/browser/media/extensionsWidgets.css b/src/vs/workbench/contrib/extensions/browser/media/extensionsWidgets.css index 6a7ab84a23a0..c5d81e6c9526 100644 --- a/src/vs/workbench/contrib/extensions/browser/media/extensionsWidgets.css +++ b/src/vs/workbench/contrib/extensions/browser/media/extensionsWidgets.css @@ -31,12 +31,12 @@ opacity: .75; } -.extension-verified-publisher { +.verified-publisher { display: flex; align-items: center; } -.extension-verified-publisher > .extension-verified-publisher-domain { +.verified-publisher > .extension-verified-publisher-domain { padding-left: 2px; color: var(--vscode-extensionIcon-verifiedForeground); text-decoration: var(--text-link-decoration); diff --git a/src/vs/workbench/contrib/extensions/common/extensions.ts b/src/vs/workbench/contrib/extensions/common/extensions.ts index e8f26f634cab..4a232d2d5603 100644 --- a/src/vs/workbench/contrib/extensions/common/extensions.ts +++ b/src/vs/workbench/contrib/extensions/common/extensions.ts @@ -6,8 +6,8 @@ import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; import { Event } from '../../../../base/common/event.js'; import { IPager } from '../../../../base/common/paging.js'; -import { IQueryOptions, ILocalExtension, IGalleryExtension, IExtensionIdentifier, IExtensionInfo, IExtensionQueryOptions, IDeprecationInfo, InstallExtensionResult } from '../../../../platform/extensionManagement/common/extensionManagement.js'; -import { EnablementState, IExtensionManagementServer, IResourceExtension, IWorkbenchInstallOptions } from '../../../services/extensionManagement/common/extensionManagement.js'; +import { IQueryOptions, ILocalExtension, IGalleryExtension, IExtensionIdentifier, IExtensionInfo, IExtensionQueryOptions, IDeprecationInfo, InstallExtensionResult, InstallOptions } from '../../../../platform/extensionManagement/common/extensionManagement.js'; +import { EnablementState, IExtensionManagementServer, IResourceExtension } from '../../../services/extensionManagement/common/extensionManagement.js'; import { CancellationToken } from '../../../../base/common/cancellation.js'; import { Disposable, IDisposable } from '../../../../base/common/lifecycle.js'; import { areSameExtensions } from '../../../../platform/extensionManagement/common/extensionManagementUtil.js'; @@ -68,6 +68,7 @@ export interface IExtension { readonly publisherSponsorLink?: URI; readonly pinned: boolean; readonly version: string; + readonly private: boolean; readonly latestVersion: string; readonly preRelease: boolean; readonly isPreReleaseVersion: boolean; @@ -83,6 +84,7 @@ export interface IExtension { readonly installCount?: number; readonly rating?: number; readonly ratingCount?: number; + readonly ratingUrl?: string; readonly outdated: boolean; readonly outdatedTargetPlatform: boolean; readonly runtimeState: ExtensionRuntimeState | undefined; @@ -108,7 +110,7 @@ export interface IExtension { export const IExtensionsWorkbenchService = createDecorator('extensionsWorkbenchService'); -export interface InstallExtensionOptions extends IWorkbenchInstallOptions { +export interface InstallExtensionOptions extends InstallOptions { version?: string; justification?: string | { reason: string; action: string }; enable?: boolean; @@ -263,4 +265,5 @@ export interface IExtensionArg { id: string; version: string; location: URI | undefined; + galleryLink: string | undefined; } diff --git a/src/vs/workbench/contrib/extensions/electron-sandbox/extensions.contribution.ts b/src/vs/workbench/contrib/extensions/electron-sandbox/extensions.contribution.ts index 9f793d53124e..e78c2df7839d 100644 --- a/src/vs/workbench/contrib/extensions/electron-sandbox/extensions.contribution.ts +++ b/src/vs/workbench/contrib/extensions/electron-sandbox/extensions.contribution.ts @@ -23,8 +23,7 @@ import { DebugExtensionHostAction, DebugExtensionsContribution } from './debugEx import { ExtensionHostProfileService } from './extensionProfileService.js'; import { CleanUpExtensionsFolderAction, OpenExtensionsFolderAction } from './extensionsActions.js'; import { ExtensionsAutoProfiler } from './extensionsAutoProfiler.js'; -import { InstallFailedExtensions } from './installFailedExtensions.js'; -import { RemoteExtensionsInitializerContribution } from './remoteExtensionsInit.js'; +import { InstallRemoteExtensionsContribution, RemoteExtensionsInitializerContribution } from './remoteExtensionsInit.js'; import { IExtensionHostProfileService, OpenExtensionHostProfileACtion, RuntimeExtensionsEditor, SaveExtensionHostProfileAction, StartExtensionHostProfileAction, StopExtensionHostProfileAction } from './runtimeExtensionsEditor.js'; // Singletons @@ -72,8 +71,8 @@ const workbenchRegistry = Registry.as(Workbench workbenchRegistry.registerWorkbenchContribution(ExtensionsContributions, LifecyclePhase.Restored); workbenchRegistry.registerWorkbenchContribution(ExtensionsAutoProfiler, LifecyclePhase.Eventually); workbenchRegistry.registerWorkbenchContribution(RemoteExtensionsInitializerContribution, LifecyclePhase.Restored); +workbenchRegistry.registerWorkbenchContribution(InstallRemoteExtensionsContribution, LifecyclePhase.Restored); workbenchRegistry.registerWorkbenchContribution(DebugExtensionsContribution, LifecyclePhase.Restored); -workbenchRegistry.registerWorkbenchContribution(InstallFailedExtensions, LifecyclePhase.Restored); // Register Commands @@ -82,4 +81,3 @@ registerAction2(StartExtensionHostProfileAction); registerAction2(StopExtensionHostProfileAction); registerAction2(SaveExtensionHostProfileAction); registerAction2(OpenExtensionHostProfileACtion); - diff --git a/src/vs/workbench/contrib/extensions/electron-sandbox/installFailedExtensions.ts b/src/vs/workbench/contrib/extensions/electron-sandbox/installFailedExtensions.ts deleted file mode 100644 index 3ec70f645d82..000000000000 --- a/src/vs/workbench/contrib/extensions/electron-sandbox/installFailedExtensions.ts +++ /dev/null @@ -1,49 +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 { CancellationToken } from '../../../../base/common/cancellation.js'; -import { Disposable } from '../../../../base/common/lifecycle.js'; -import { IExtensionGalleryService, InstallExtensionInfo } from '../../../../platform/extensionManagement/common/extensionManagement.js'; -import { ILogService } from '../../../../platform/log/common/log.js'; -import { IRemoteExtensionsScannerService } from '../../../../platform/remote/common/remoteExtensionsScanner.js'; -import { IWorkbenchContribution } from '../../../common/contributions.js'; -import { areSameExtensions } from '../../../../platform/extensionManagement/common/extensionManagementUtil.js'; -import { IExtensionManagementServerService } from '../../../services/extensionManagement/common/extensionManagement.js'; -export class InstallFailedExtensions extends Disposable implements IWorkbenchContribution { - constructor( - @IRemoteExtensionsScannerService remoteExtensionsScannerService: IRemoteExtensionsScannerService, - @IExtensionGalleryService private readonly _extensionGalleryService: IExtensionGalleryService, - @IExtensionManagementServerService private readonly _extensionManagementServerService: IExtensionManagementServerService, - @ILogService logService: ILogService - ) { - super(); - remoteExtensionsScannerService.whenExtensionsReady() - .then(async ({ failed }) => { - if (failed.length === 0) { - logService.trace('No failed extensions relayed from server'); - return; - } - - if (!this._extensionManagementServerService.remoteExtensionManagementServer) { - logService.error('No remote extension management server available'); - return; - } - - const extensionManagementService = this._extensionManagementServerService.remoteExtensionManagementServer.extensionManagementService; - const galleryExtensions = await this._extensionGalleryService.getExtensions(failed, CancellationToken.None); - // Join back with its installOptions - const installExtensionInfo: InstallExtensionInfo[] = galleryExtensions.map(ext => { - return { - extension: ext, - options: failed.find(f => areSameExtensions(f, ext.identifier))?.installOptions || {} - }; - }); - - await extensionManagementService.installGalleryExtensions(installExtensionInfo); - }).catch(e => { - logService.error(e); - }); - } -} diff --git a/src/vs/workbench/contrib/extensions/electron-sandbox/remoteExtensionsInit.ts b/src/vs/workbench/contrib/extensions/electron-sandbox/remoteExtensionsInit.ts index 203212373a6e..1b0fadda14f0 100644 --- a/src/vs/workbench/contrib/extensions/electron-sandbox/remoteExtensionsInit.ts +++ b/src/vs/workbench/contrib/extensions/electron-sandbox/remoteExtensionsInit.ts @@ -4,14 +4,19 @@ *--------------------------------------------------------------------------------------------*/ import { CancellationToken } from '../../../../base/common/cancellation.js'; +import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { IEnvironmentService } from '../../../../platform/environment/common/environment.js'; -import { IExtensionGalleryService, IExtensionManagementService } from '../../../../platform/extensionManagement/common/extensionManagement.js'; +import { EXTENSION_INSTALL_SKIP_PUBLISHER_TRUST_CONTEXT, IExtensionGalleryService, IExtensionManagementService, InstallExtensionInfo } from '../../../../platform/extensionManagement/common/extensionManagement.js'; import { areSameExtensions } from '../../../../platform/extensionManagement/common/extensionManagementUtil.js'; +import { ExtensionType } from '../../../../platform/extensions/common/extensions.js'; import { IFileService } from '../../../../platform/files/common/files.js'; import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { ServiceCollection } from '../../../../platform/instantiation/common/serviceCollection.js'; import { ILogService } from '../../../../platform/log/common/log.js'; +import { IProductService } from '../../../../platform/product/common/productService.js'; +import { REMOTE_DEFAULT_IF_LOCAL_EXTENSIONS } from '../../../../platform/remote/common/remote.js'; import { IRemoteAuthorityResolverService } from '../../../../platform/remote/common/remoteAuthorityResolver.js'; +import { IRemoteExtensionsScannerService } from '../../../../platform/remote/common/remoteExtensionsScanner.js'; import { IStorageService, IS_NEW_KEY, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js'; import { IUserDataProfilesService } from '../../../../platform/userDataProfile/common/userDataProfile.js'; @@ -25,6 +30,125 @@ import { IExtensionManagementServerService } from '../../../services/extensionMa import { IExtensionManifestPropertiesService } from '../../../services/extensions/common/extensionManifestPropertiesService.js'; import { IRemoteAgentService } from '../../../services/remote/common/remoteAgentService.js'; +export class InstallRemoteExtensionsContribution implements IWorkbenchContribution { + constructor( + @IRemoteAgentService private readonly remoteAgentService: IRemoteAgentService, + @IRemoteExtensionsScannerService private readonly remoteExtensionsScannerService: IRemoteExtensionsScannerService, + @IExtensionGalleryService private readonly extensionGalleryService: IExtensionGalleryService, + @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, + @ILogService private readonly logService: ILogService, + @IConfigurationService private readonly configurationService: IConfigurationService, + @IProductService private readonly productService: IProductService, + ) { + this.installDefaultRemoteExtensions(); + this.installFailedRemoteExtensions(); + } + + private async installDefaultRemoteExtensions(): Promise { + if (!this.remoteAgentService.getConnection()) { + return; + } + + if (!this.extensionManagementServerService.remoteExtensionManagementServer) { + this.logService.error('No remote extension management server available'); + return; + } + + if (!this.extensionManagementServerService.localExtensionManagementServer) { + this.logService.error('No local extension management server available'); + return; + } + + const settingValue = this.configurationService.getValue(REMOTE_DEFAULT_IF_LOCAL_EXTENSIONS); + if (!settingValue?.length) { + return; + } + + this.logService.info(`Installing '${settingValue.length}' default remote extensions`); + + const preferPrerelease = this.productService.quality !== 'stable'; + const galleryExtensions = await this.extensionGalleryService.getExtensions(settingValue.map((id) => ({ id })), CancellationToken.None); + const alreadyInstalledInRemote = await this.extensionManagementServerService.remoteExtensionManagementServer.extensionManagementService.getInstalled(ExtensionType.User); + const alreadyInstalledLocally = await this.extensionManagementServerService.localExtensionManagementServer.extensionManagementService.getInstalled(ExtensionType.User); + + const prereleaseExtensionInfo: InstallExtensionInfo[] = []; + const extensionInfo: InstallExtensionInfo[] = []; + for (const id of settingValue) { + const alreadyInstalled = alreadyInstalledInRemote.some(e => areSameExtensions(e.identifier, { id })); + if (alreadyInstalled) { + this.logService.trace(`Default remote extension '${id}' is already installed`); + continue; + } + + const installedLocally = alreadyInstalledLocally.some(e => areSameExtensions(e.identifier, { id })); + if (!installedLocally) { + this.logService.trace(`Default remote extension '${id}' is not already installed locally`); + continue; + } + + const extension = galleryExtensions.find(e => areSameExtensions(e.identifier, { id })); + if (!extension) { + this.logService.warn(`Default remote extension '${id}' is not found`); + continue; + } + + const installPreReleaseVersion = preferPrerelease && extension.hasPreReleaseVersion; + (installPreReleaseVersion ? prereleaseExtensionInfo : extensionInfo).push({ + extension, options: { installPreReleaseVersion }, + }); + } + + // Install pre-release extensions first to avoid a situation where: + // An extension without a pre-release (A) is installed first and depends on an extension that has a pre-release version (B) + // If this happens, the extension A may result in the installation of the stable version of B + // A real life example of this is GitHub.copilot and GitHub.copilot-chat + if (prereleaseExtensionInfo.length) { + await Promise.allSettled(prereleaseExtensionInfo.map(e => this.extensionManagementServerService.remoteExtensionManagementServer!.extensionManagementService.installFromGallery(e.extension, e.options))); + } + if (extensionInfo.length) { + await Promise.allSettled(extensionInfo.map(e => this.extensionManagementServerService.remoteExtensionManagementServer!.extensionManagementService.installFromGallery(e.extension, e.options))); + } + } + + private async installFailedRemoteExtensions(): Promise { + if (!this.remoteAgentService.getConnection()) { + return; + } + + const { failed } = await this.remoteExtensionsScannerService.whenExtensionsReady(); + if (failed.length === 0) { + this.logService.trace('No extensions relayed from server'); + return; + } + + if (!this.extensionManagementServerService.remoteExtensionManagementServer) { + this.logService.error('No remote extension management server available'); + return; + } + + this.logService.info(`Installing '${failed.length}' extensions relayed from server`); + const galleryExtensions = await this.extensionGalleryService.getExtensions(failed.map(({ id }) => ({ id })), CancellationToken.None); + const installExtensionInfo: InstallExtensionInfo[] = []; + for (const { id, installOptions } of failed) { + const extension = galleryExtensions.find(e => areSameExtensions(e.identifier, { id })); + if (extension) { + installExtensionInfo.push({ + extension, options: { + ...installOptions, + downloadExtensionsLocally: true, + } + }); + } else { + this.logService.warn(`Relayed failed extension '${id}' from server is not found in the gallery`); + } + } + + if (installExtensionInfo.length) { + await Promise.allSettled(installExtensionInfo.map(e => this.extensionManagementServerService.remoteExtensionManagementServer!.extensionManagementService.installFromGallery(e.extension, e.options))); + } + } +} + export class RemoteExtensionsInitializerContribution implements IWorkbenchContribution { constructor( @IExtensionManagementServerService private readonly extensionManagementServerService: IExtensionManagementServerService, @@ -135,7 +259,7 @@ class RemoteExtensionsInitializer extends AbstractExtensionsInitializer { const manifest = await this.extensionGalleryService.getManifest(e, CancellationToken.None); if (manifest && this.extensionManifestPropertiesService.canExecuteOnWorkspace(manifest)) { const syncedExtension = remoteExtensions.find(e => areSameExtensions(e.identifier, e.identifier)); - await this.extensionManagementService.installFromGallery(e, { installPreReleaseVersion: syncedExtension?.preRelease, donotIncludePackAndDependencies: true }); + await this.extensionManagementService.installFromGallery(e, { installPreReleaseVersion: syncedExtension?.preRelease, donotIncludePackAndDependencies: true, context: { [EXTENSION_INSTALL_SKIP_PUBLISHER_TRUST_CONTEXT]: true } }); } })); } diff --git a/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts b/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts index addaf29046f9..c8c1f5e33148 100644 --- a/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts +++ b/src/vs/workbench/contrib/files/browser/editors/textFileSaveErrorHandler.ts @@ -16,7 +16,7 @@ import { IWorkbenchContribution } from '../../../../common/contributions.js'; import { ITextModelService } from '../../../../../editor/common/services/resolverService.js'; import { ResourceMap } from '../../../../../base/common/map.js'; import { DiffEditorInput } from '../../../../common/editor/diffEditorInput.js'; -import { IContextKeyService, RawContextKey } from '../../../../../platform/contextkey/common/contextkey.js'; +import { IContextKey, IContextKeyService, RawContextKey } from '../../../../../platform/contextkey/common/contextkey.js'; import { TextFileContentProvider } from '../../common/files.js'; import { FileEditorInput } from './fileEditorInput.js'; import { SAVE_FILE_AS_LABEL } from '../fileConstants.js'; @@ -45,13 +45,13 @@ export class TextFileSaveErrorHandler extends Disposable implements ISaveErrorHa static readonly ID = 'workbench.contrib.textFileSaveErrorHandler'; private readonly messages = new ResourceMap(); - private readonly conflictResolutionContext = new RawContextKey(CONFLICT_RESOLUTION_CONTEXT, false, true).bindTo(this.contextKeyService); + private readonly conflictResolutionContext: IContextKey; private activeConflictResolutionResource: URI | undefined = undefined; constructor( @INotificationService private readonly notificationService: INotificationService, @ITextFileService private readonly textFileService: ITextFileService, - @IContextKeyService private contextKeyService: IContextKeyService, + @IContextKeyService contextKeyService: IContextKeyService, @IEditorService private readonly editorService: IEditorService, @ITextModelService textModelService: ITextModelService, @IInstantiationService private readonly instantiationService: IInstantiationService, @@ -59,6 +59,8 @@ export class TextFileSaveErrorHandler extends Disposable implements ISaveErrorHa ) { super(); + this.conflictResolutionContext = new RawContextKey(CONFLICT_RESOLUTION_CONTEXT, false, true).bindTo(contextKeyService); + const provider = this._register(instantiationService.createInstance(TextFileContentProvider)); this._register(textModelService.registerTextModelContentProvider(CONFLICT_RESOLUTION_SCHEME, provider)); diff --git a/src/vs/workbench/contrib/files/browser/explorerService.ts b/src/vs/workbench/contrib/files/browser/explorerService.ts index cb60e6d4a34b..61f0ac0a8ffa 100644 --- a/src/vs/workbench/contrib/files/browser/explorerService.ts +++ b/src/vs/workbench/contrib/files/browser/explorerService.ts @@ -26,7 +26,6 @@ import { IHostService } from '../../../services/host/browser/host.js'; import { IExpression } from '../../../../base/common/glob.js'; import { ResourceGlobMatcher } from '../../../common/resources.js'; import { IFilesConfigurationService } from '../../../services/filesConfiguration/common/filesConfigurationService.js'; -import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; export const UNDO_REDO_SOURCE = new UndoRedoSource(); @@ -55,8 +54,7 @@ export class ExplorerService implements IExplorerService { @IBulkEditService private readonly bulkEditService: IBulkEditService, @IProgressService private readonly progressService: IProgressService, @IHostService hostService: IHostService, - @IFilesConfigurationService private readonly filesConfigurationService: IFilesConfigurationService, - @ITelemetryService private readonly telemetryService: ITelemetryService + @IFilesConfigurationService private readonly filesConfigurationService: IFilesConfigurationService ) { this.config = this.configurationService.getValue('explorer'); @@ -245,46 +243,6 @@ export class ExplorerService implements IExplorerService { try { await this.view.setEditable(stat, isEditing); } catch { - const parent = stat.parent; - type ExplorerViewEditableErrorData = { - parentIsDirectory: boolean | undefined; - isDirectory: boolean | undefined; - isReadonly: boolean | undefined; - parentIsReadonly: boolean | undefined; - parentIsExcluded: boolean | undefined; - isExcluded: boolean | undefined; - parentIsRoot: boolean | undefined; - isRoot: boolean | undefined; - parentHasNests: boolean | undefined; - hasNests: boolean | undefined; - }; - type ExplorerViewEditableErrorClassification = { - owner: 'lramos15'; - comment: 'Helps gain a broard understanding of why users are unable to edit files in the explorer'; - parentIsDirectory: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Whether the parent of the editable element is a directory' }; - isDirectory: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Whether the editable element is a directory' }; - isReadonly: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Whether the editable element is readonly' }; - parentIsReadonly: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Whether the parent of the editable element is readonly' }; - parentIsExcluded: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Whether the parent of the editable element is excluded from being shown in the explorer' }; - isExcluded: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Whether the editable element is excluded from being shown in the explorer' }; - parentIsRoot: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Whether the parent of the editable element is a root' }; - isRoot: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Whether the editable element is a root' }; - parentHasNests: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Whether the parent of the editable element has nested children' }; - hasNests: { classification: 'SystemMetaData'; purpose: 'PerformanceAndHealth'; comment: 'Whether the editable element has nested children' }; - }; - const errorData = { - parentIsDirectory: parent?.isDirectory, - isDirectory: stat.isDirectory, - isReadonly: !!stat.isReadonly, - parentIsReadonly: !!parent?.isReadonly, - parentIsExcluded: parent?.isExcluded, - isExcluded: stat.isExcluded, - parentIsRoot: parent?.isRoot, - isRoot: stat.isRoot, - parentHasNests: parent?.hasNests, - hasNests: stat.hasNests, - }; - this.telemetryService.publicLogError2('explorerView.setEditableError', errorData); return; } diff --git a/src/vs/workbench/contrib/files/browser/fileActions.ts b/src/vs/workbench/contrib/files/browser/fileActions.ts index 50f786ea2f7e..bc7f9549458a 100644 --- a/src/vs/workbench/contrib/files/browser/fileActions.ts +++ b/src/vs/workbench/contrib/files/browser/fileActions.ts @@ -756,7 +756,7 @@ export function validateFileName(pathService: IPathService, item: ExplorerItem, // Check for invalid file name. if (names.some(folderName => !pathService.hasValidBasename(item.resource, os, folderName))) { // Escape * characters - const escapedName = name.replace(/\*/g, '\\*'); // CodeQL [SM02383] This only processes filenames which are enforced against having backslashes in them farther up in the stack. + const escapedName = name.replace(/\\/g, '\\\\').replace(/\*/g, '\\*'); // Escapes backslashes first, then asterisks. return { content: nls.localize('invalidFileNameError', "The name **{0}** is not valid as a file or folder name. Please choose a different name.", trimLongName(escapedName)), severity: Severity.Error diff --git a/src/vs/workbench/contrib/files/browser/views/emptyView.ts b/src/vs/workbench/contrib/files/browser/views/emptyView.ts index a70269a9cbaf..ea0b6a96276f 100644 --- a/src/vs/workbench/contrib/files/browser/views/emptyView.ts +++ b/src/vs/workbench/contrib/files/browser/views/emptyView.ts @@ -18,7 +18,6 @@ import { ILabelService } from '../../../../../platform/label/common/label.js'; import { IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js'; import { IViewDescriptorService } from '../../../../common/views.js'; import { IOpenerService } from '../../../../../platform/opener/common/opener.js'; -import { ITelemetryService } from '../../../../../platform/telemetry/common/telemetry.js'; import { isWeb } from '../../../../../base/common/platform.js'; import { DragAndDropObserver, getWindow } from '../../../../../base/browser/dom.js'; import { ILocalizedString } from '../../../../../platform/action/common/action.js'; @@ -42,10 +41,9 @@ export class EmptyView extends ViewPane { @ILabelService private labelService: ILabelService, @IContextKeyService contextKeyService: IContextKeyService, @IOpenerService openerService: IOpenerService, - @ITelemetryService telemetryService: ITelemetryService, @IHoverService hoverService: IHoverService, ) { - super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService, hoverService); + super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, hoverService); this._register(this.contextService.onDidChangeWorkbenchState(() => this.refreshTitle())); this._register(this.labelService.onDidChangeFormatters(() => this.refreshTitle())); diff --git a/src/vs/workbench/contrib/files/browser/views/explorerView.ts b/src/vs/workbench/contrib/files/browser/views/explorerView.ts index f91029d58d5e..c7056e38868e 100644 --- a/src/vs/workbench/contrib/files/browser/views/explorerView.ts +++ b/src/vs/workbench/contrib/files/browser/views/explorerView.ts @@ -201,7 +201,7 @@ export class ExplorerView extends ViewPane implements IExplorerView { @IDecorationsService private readonly decorationService: IDecorationsService, @ILabelService private readonly labelService: ILabelService, @IThemeService themeService: IWorkbenchThemeService, - @ITelemetryService telemetryService: ITelemetryService, + @ITelemetryService private readonly telemetryService: ITelemetryService, @IHoverService hoverService: IHoverService, @IExplorerService private readonly explorerService: IExplorerService, @IStorageService private readonly storageService: IStorageService, @@ -211,7 +211,7 @@ export class ExplorerView extends ViewPane implements IExplorerView { @ICommandService private readonly commandService: ICommandService, @IOpenerService openerService: IOpenerService ) { - super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService, hoverService); + super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, hoverService); this.delegate = options.delegate; this.resourceContext = instantiationService.createInstance(ResourceContextKey); diff --git a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts index dbc04e88561b..9446c1ddd459 100644 --- a/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts +++ b/src/vs/workbench/contrib/files/browser/views/openEditorsView.ts @@ -56,7 +56,6 @@ import { mainWindow } from '../../../../../base/browser/window.js'; import { EditorGroupView } from '../../../../browser/parts/editor/editorGroupView.js'; import { IHoverService } from '../../../../../platform/hover/browser/hover.js'; import { IFileService } from '../../../../../platform/files/common/files.js'; -import { IPreferencesService } from '../../../../services/preferences/common/preferences.js'; const $ = dom.$; @@ -88,14 +87,14 @@ export class OpenEditorsView extends ViewPane { @IKeybindingService keybindingService: IKeybindingService, @IContextKeyService contextKeyService: IContextKeyService, @IThemeService themeService: IThemeService, - @ITelemetryService telemetryService: ITelemetryService, + @ITelemetryService private readonly telemetryService: ITelemetryService, @IHoverService hoverService: IHoverService, @IWorkingCopyService private readonly workingCopyService: IWorkingCopyService, @IFilesConfigurationService private readonly filesConfigurationService: IFilesConfigurationService, @IOpenerService openerService: IOpenerService, @IFileService private readonly fileService: IFileService ) { - super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService, hoverService); + super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, hoverService); this.structuralRefreshDelay = 0; this.sortOrder = configurationService.getValue('explorer.openEditors.sortOrder'); @@ -944,24 +943,3 @@ registerAction2(class extends Action2 { await commandService.executeCommand(NEW_UNTITLED_FILE_COMMAND_ID); } }); - -registerAction2(class extends Action2 { - constructor() { - super({ - id: 'openEditors.configure', - title: nls.localize('configureOpenEditorsView', 'Configure \'{0}\'', OpenEditorsView.NAME.value), - f1: false, - icon: Codicon.gear, - menu: { - id: MenuId.ViewTitle, - group: '9_configure', - when: ContextKeyExpr.equals('view', OpenEditorsView.ID), - order: 10 - } - }); - } - async run(accessor: ServicesAccessor): Promise { - const preferencesService = accessor.get(IPreferencesService); - preferencesService.openSettings({ jsonEditor: false, query: '@feature:explorer openEditors' }); - } -}); diff --git a/src/vs/workbench/contrib/format/browser/formatActionsMultiple.ts b/src/vs/workbench/contrib/format/browser/formatActionsMultiple.ts index a80963b7f626..2eec08fd35a4 100644 --- a/src/vs/workbench/contrib/format/browser/formatActionsMultiple.ts +++ b/src/vs/workbench/contrib/format/browser/formatActionsMultiple.ts @@ -14,7 +14,6 @@ import { CancellationToken, CancellationTokenSource } from '../../../../base/com import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; import { formatDocumentRangesWithProvider, formatDocumentWithProvider, getRealAndSyntheticDocumentFormattersOrdered, FormattingConflicts, FormattingMode, FormattingKind } from '../../../../editor/contrib/format/browser/format.js'; import { Range } from '../../../../editor/common/core/range.js'; -import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; import { ExtensionIdentifier } from '../../../../platform/extensions/common/extensions.js'; import { Registry } from '../../../../platform/registry/common/platform.js'; import { IConfigurationRegistry, Extensions as ConfigurationExtensions } from '../../../../platform/configuration/common/configurationRegistry.js'; @@ -263,28 +262,6 @@ interface IIndexedPick extends IQuickPickItem { index: number; } -function logFormatterTelemetry(telemetryService: ITelemetryService, mode: 'document' | 'range', options: T[], pick?: T) { - type FormatterPicks = { - mode: 'document' | 'range'; - extensions: string[]; - pick: string; - }; - type FormatterPicksClassification = { - owner: 'jrieken'; - comment: 'Information about resolving formatter conflicts'; - mode: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Formatting mode: whole document or a range/selection' }; - extensions: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The extension that got picked' }; - pick: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'The possible extensions to pick' }; - }; - function extKey(obj: T): string { - return obj.extensionId ? ExtensionIdentifier.toKey(obj.extensionId) : 'unknown'; - } - telemetryService.publicLog2('formatterpick', { - mode, - extensions: options.map(extKey), - pick: pick ? extKey(pick) : 'none' - }); -} async function showFormatterPick(accessor: ServicesAccessor, model: ITextModel, formatters: FormattingEditProvider[]): Promise { const quickPickService = accessor.get(IQuickInputService); @@ -362,7 +339,6 @@ registerEditorAction(class FormatDocumentMultipleAction extends EditorAction { return; } const instaService = accessor.get(IInstantiationService); - const telemetryService = accessor.get(ITelemetryService); const languageFeaturesService = accessor.get(ILanguageFeaturesService); const model = editor.getModel(); const provider = getRealAndSyntheticDocumentFormattersOrdered(languageFeaturesService.documentFormattingEditProvider, languageFeaturesService.documentRangeFormattingEditProvider, model); @@ -370,7 +346,6 @@ registerEditorAction(class FormatDocumentMultipleAction extends EditorAction { if (typeof pick === 'number') { await instaService.invokeFunction(formatDocumentWithProvider, provider[pick], editor, FormattingMode.Explicit, CancellationToken.None); } - logFormatterTelemetry(telemetryService, 'document', provider, typeof pick === 'number' && provider[pick] || undefined); } }); @@ -396,7 +371,6 @@ registerEditorAction(class FormatSelectionMultipleAction extends EditorAction { } const instaService = accessor.get(IInstantiationService); const languageFeaturesService = accessor.get(ILanguageFeaturesService); - const telemetryService = accessor.get(ITelemetryService); const model = editor.getModel(); let range: Range = editor.getSelection(); @@ -409,7 +383,5 @@ registerEditorAction(class FormatSelectionMultipleAction extends EditorAction { if (typeof pick === 'number') { await instaService.invokeFunction(formatDocumentRangesWithProvider, provider[pick], editor, range, CancellationToken.None, true); } - - logFormatterTelemetry(telemetryService, 'range', provider, typeof pick === 'number' && provider[pick] || undefined); } }); diff --git a/src/vs/workbench/contrib/inlayHints/browser/inlayHintsAccessibilty.ts b/src/vs/workbench/contrib/inlayHints/browser/inlayHintsAccessibilty.ts index 0494623f078e..ab3d1dabf53e 100644 --- a/src/vs/workbench/contrib/inlayHints/browser/inlayHintsAccessibilty.ts +++ b/src/vs/workbench/contrib/inlayHints/browser/inlayHintsAccessibilty.ts @@ -174,7 +174,7 @@ registerAction2(class StartReadHints extends EditorAction2 { constructor() { super({ id: 'inlayHints.startReadingLineWithHint', - title: localize2('read.title', "Read Line With Inline Hints"), + title: localize2('read.title', "Read Line with Inline Hints"), precondition: EditorContextKeys.hasInlayHintsProvider, f1: true }); diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts index 8949a6257519..434bffb469db 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatActions.ts @@ -11,7 +11,7 @@ import { EmbeddedDiffEditorWidget } from '../../../../editor/browser/widget/diff import { EmbeddedCodeEditorWidget } from '../../../../editor/browser/widget/codeEditor/embeddedCodeEditorWidget.js'; import { EditorContextKeys } from '../../../../editor/common/editorContextKeys.js'; import { InlineChatController, InlineChatController1, InlineChatController2, InlineChatRunOptions } from './inlineChatController.js'; -import { ACTION_ACCEPT_CHANGES, CTX_INLINE_CHAT_HAS_AGENT, CTX_INLINE_CHAT_HAS_STASHED_SESSION, CTX_INLINE_CHAT_FOCUSED, CTX_INLINE_CHAT_INNER_CURSOR_FIRST, CTX_INLINE_CHAT_INNER_CURSOR_LAST, CTX_INLINE_CHAT_VISIBLE, CTX_INLINE_CHAT_OUTER_CURSOR_POSITION, MENU_INLINE_CHAT_WIDGET_STATUS, CTX_INLINE_CHAT_REQUEST_IN_PROGRESS, CTX_INLINE_CHAT_RESPONSE_TYPE, InlineChatResponseType, ACTION_REGENERATE_RESPONSE, ACTION_VIEW_IN_CHAT, ACTION_TOGGLE_DIFF, CTX_INLINE_CHAT_CHANGE_HAS_DIFF, CTX_INLINE_CHAT_CHANGE_SHOWS_DIFF, MENU_INLINE_CHAT_ZONE, ACTION_DISCARD_CHANGES, CTX_INLINE_CHAT_POSSIBLE, ACTION_START, CTX_INLINE_CHAT_HAS_AGENT2 } from '../common/inlineChat.js'; +import { ACTION_ACCEPT_CHANGES, CTX_INLINE_CHAT_HAS_AGENT, CTX_INLINE_CHAT_HAS_STASHED_SESSION, CTX_INLINE_CHAT_FOCUSED, CTX_INLINE_CHAT_INNER_CURSOR_FIRST, CTX_INLINE_CHAT_INNER_CURSOR_LAST, CTX_INLINE_CHAT_VISIBLE, CTX_INLINE_CHAT_OUTER_CURSOR_POSITION, MENU_INLINE_CHAT_WIDGET_STATUS, CTX_INLINE_CHAT_REQUEST_IN_PROGRESS, CTX_INLINE_CHAT_RESPONSE_TYPE, InlineChatResponseType, ACTION_REGENERATE_RESPONSE, ACTION_VIEW_IN_CHAT, ACTION_TOGGLE_DIFF, CTX_INLINE_CHAT_CHANGE_HAS_DIFF, CTX_INLINE_CHAT_CHANGE_SHOWS_DIFF, MENU_INLINE_CHAT_ZONE, ACTION_DISCARD_CHANGES, CTX_INLINE_CHAT_POSSIBLE, ACTION_START, CTX_INLINE_CHAT_HAS_AGENT2, MENU_INLINE_CHAT_SIDE } from '../common/inlineChat.js'; import { ctxIsGlobalEditingSession, ctxRequestCount } from '../../chat/browser/chatEditing/chatEditingEditorContextKeys.js'; import { localize, localize2 } from '../../../../nls.js'; import { Action2, IAction2Options, MenuId } from '../../../../platform/actions/common/actions.js'; @@ -70,8 +70,8 @@ export class StartSessionAction extends Action2 { icon: START_INLINE_CHAT, menu: { id: MenuId.ChatTitleBarMenu, - group: 'd_inlineChat', - order: 10, + group: 'a_open', + order: 3, } }); } @@ -386,8 +386,7 @@ export class CloseAction extends AbstractInline1ChatAction { group: '0_main', order: 1, when: ContextKeyExpr.and( - CTX_INLINE_CHAT_REQUEST_IN_PROGRESS.negate(), - CTX_INLINE_CHAT_RESPONSE_TYPE.isEqualTo(InlineChatResponseType.Messages) + CTX_INLINE_CHAT_REQUEST_IN_PROGRESS.negate() ), }] }); @@ -578,20 +577,26 @@ abstract class AbstractInline2ChatAction extends EditorAction2 { } export class StopSessionAction2 extends AbstractInline2ChatAction { + constructor() { super({ id: 'inlineChat2.stop', - title: localize2('stop', "Stop"), + title: localize2('stop', "Undo & Close"), f1: true, + icon: Codicon.close, precondition: CTX_INLINE_CHAT_VISIBLE, keybinding: [{ - weight: KeybindingWeight.WorkbenchContrib, - primary: KeyCode.Escape, - }, { when: ctxRequestCount.isEqualTo(0), weight: KeybindingWeight.WorkbenchContrib, primary: KeyMod.CtrlCmd | KeyCode.KeyI, + }, { + weight: KeybindingWeight.WorkbenchContrib, + primary: KeyCode.Escape, }], + menu: { + id: MENU_INLINE_CHAT_SIDE, + group: 'navigation', + } }); } diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts index 34a1d40b4c2e..9f2458018bd7 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -12,13 +12,14 @@ import { Emitter, Event } from '../../../../base/common/event.js'; import { Lazy } from '../../../../base/common/lazy.js'; import { DisposableStore, MutableDisposable, toDisposable } from '../../../../base/common/lifecycle.js'; import { MovingAverage } from '../../../../base/common/numbers.js'; -import { autorun, autorunWithStore, derived, IObservable, observableFromEvent, observableSignalFromEvent, observableValue, transaction } from '../../../../base/common/observable.js'; +import { autorun, autorunWithStore, derived, IObservable, observableFromEvent, observableSignalFromEvent, observableValue, transaction, waitForState } from '../../../../base/common/observable.js'; import { isEqual } from '../../../../base/common/resources.js'; import { StopWatch } from '../../../../base/common/stopwatch.js'; import { assertType } from '../../../../base/common/types.js'; import { generateUuid } from '../../../../base/common/uuid.js'; import { ICodeEditor, isCodeEditor } from '../../../../editor/browser/editorBrowser.js'; import { observableCodeEditor } from '../../../../editor/browser/observableCodeEditor.js'; +import { ICodeEditorService } from '../../../../editor/browser/services/codeEditorService.js'; import { EditorOption } from '../../../../editor/common/config/editorOptions.js'; import { IPosition, Position } from '../../../../editor/common/core/position.js'; import { IRange, Range } from '../../../../editor/common/core/range.js'; @@ -40,9 +41,6 @@ import { IEditorService, SIDE_GROUP } from '../../../services/editor/common/edit import { IViewsService } from '../../../services/views/common/viewsService.js'; import { showChatView } from '../../chat/browser/chat.js'; import { IChatWidgetLocationOptions } from '../../chat/browser/chatWidget.js'; -import { ChatAgentLocation } from '../../chat/common/chatAgents.js'; -import { ChatContextKeys } from '../../chat/common/chatContextKeys.js'; -import { IChatEditingService, WorkingSetEntryState } from '../../chat/common/chatEditingService.js'; import { ChatModel, ChatRequestRemovalReason, IChatRequestModel, IChatTextEditGroup, IChatTextEditGroupState, IResponse } from '../../chat/common/chatModel.js'; import { IChatService } from '../../chat/common/chatService.js'; import { INotebookEditorService } from '../../notebook/browser/services/notebookEditorService.js'; @@ -53,6 +51,9 @@ import { InlineChatError } from './inlineChatSessionServiceImpl.js'; import { HunkAction, IEditObserver, LiveStrategy, ProgressingEditsOptions } from './inlineChatStrategies.js'; import { EditorBasedInlineChatWidget } from './inlineChatWidget.js'; import { InlineChatZoneWidget } from './inlineChatZoneWidget.js'; +import { ChatAgentLocation } from '../../chat/common/constants.js'; +import { ChatContextKeys } from '../../chat/common/chatContextKeys.js'; +import { IChatEditingService, WorkingSetEntryState } from '../../chat/common/chatEditingService.js'; export const enum State { CREATE_SESSION = 'CREATE_SESSION', @@ -493,12 +494,42 @@ export class InlineChatController1 implements IEditorContribution { } })); + let ignoreRemoveEvents = false; this._sessionStore.add(this._session.chatModel.onDidChange(async e => { - if (e.kind === 'removeRequest') { - // TODO@jrieken there is still some work left for when a request "in the middle" - // is removed. We will undo all changes till that point but not remove those - // later request - await this._session!.undoChangesUntil(e.requestId); + if (e.kind === 'removeRequest' && !ignoreRemoveEvents) { + assertType(this._session); + assertType(this._strategy); + + // when truely removing a request, we also remove all following requests + if (e.reason === ChatRequestRemovalReason.Removal) { + try { + ignoreRemoveEvents = true; + const requests = this._session.chatModel.getRequests().slice(); + const idx = requests.findIndex(candidate => candidate.id === e.requestId); + for (let i = idx + 1; i < requests.length; i++) { + await this._chatService.removeRequest(this._session.chatModel.sessionId, requests[i].id); + } + } finally { + ignoreRemoveEvents = false; + } + } + + await this._session.undoChangesUntil(e.requestId); + + // recompute hunks! + const requests = this._session.chatModel.getRequests(); + let editState: IChatTextEditGroupState | undefined; + for (let i = requests.length - 1; i >= 0; i--) { + const edits = requests[i].response?.response.value.find(item => item.kind === 'textEditGroup' && isEqual(item.uri, this._session?.textModelN.uri)); + if (edits?.state) { + editState = edits.state; + break; + } + } + if (editState) { + this._session.hunkData.recompute(editState); + await this._strategy.renderChanges(); + } } })); @@ -801,6 +832,18 @@ export class InlineChatController1 implements IEditorContribution { this._ctxRequestInProgress.set(false); + return next; + } + + private async[State.SHOW_RESPONSE](): Promise { + assertType(this._session); + assertType(this._strategy); + + if (!this._session.lastExchange) { + return State.WAIT_FOR_INPUT; + } + + const { response } = this._session.lastExchange; let newPosition: Position | undefined; @@ -1195,6 +1238,7 @@ export class InlineChatController2 implements IEditorContribution { @IInstantiationService private readonly _instaService: IInstantiationService, @INotebookEditorService private readonly _notebookEditorService: INotebookEditorService, @IInlineChatSessionService private readonly _inlineChatSessions: IInlineChatSessionService, + @ICodeEditorService codeEditorService: ICodeEditorService, @IContextKeyService contextKeyService: IContextKeyService, ) { @@ -1260,11 +1304,21 @@ export class InlineChatController2 implements IEditorContribution { }); - this._store.add(autorunWithStore((r, store) => { + this._store.add(autorun(r => { const session = this._currentSession.read(r); - if (!session) { this._isActiveController.set(false, undefined); + return; + } + let foundOne = false; + for (const editor of codeEditorService.listCodeEditors()) { + if (Boolean(InlineChatController2.get(editor)?._isActiveController.get())) { + foundOne = true; + break; + } + } + if (!foundOne && _editor.hasWidgetFocus()) { + this._isActiveController.set(true, undefined); } })); @@ -1354,7 +1408,6 @@ export class InlineChatController2 implements IEditorContribution { this._showWidgetOverrideObs.set(!value, undefined); } - getWidgetPosition(): Position | undefined { return this._zone.rawValue?.position; } @@ -1370,11 +1423,12 @@ export class InlineChatController2 implements IEditorContribution { async run(arg?: InlineChatRunOptions): Promise { assertType(this._editor.hasModel()); - const uri = this._editor.getModel().uri; - const session = await this._inlineChatSessions.createSession2(this._editor, uri, CancellationToken.None); - this.markActiveController(); + const uri = this._editor.getModel().uri; + const session = this._inlineChatSessions.getSession2(uri) + ?? await this._inlineChatSessions.createSession2(this._editor, uri, CancellationToken.None); + if (arg && InlineChatRunOptions.isInlineChatRunOptions(arg)) { if (arg.initialRange) { this._editor.revealRange(arg.initialRange); @@ -1438,14 +1492,17 @@ export async function reviewEdits(accessor: ServicesAccessor, editor: ICodeEdito chatRequest.response.complete(); } - const whenDecided = new Promise(resolve => { - store.add(autorun(r => { - if (!editSession.entries.read(r).some(e => e.state.read(r) === WorkingSetEntryState.Modified)) { - resolve(undefined); - } - })); + const isSettled = derived(r => { + const entry = editSession.readEntry(uri, r); + if (!entry) { + return false; + } + const state = entry.state.read(r); + return state === WorkingSetEntryState.Accepted || state === WorkingSetEntryState.Rejected; }); + const whenDecided = waitForState(isSettled, Boolean); + await raceCancellation(whenDecided, token); store.dispose(); diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatCurrentLine.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatCurrentLine.ts index e4143ceff4a3..530120c5d973 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatCurrentLine.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatCurrentLine.ts @@ -20,14 +20,14 @@ import { IValidEditOperation, TrackedRangeStickiness } from '../../../../editor/ import { URI } from '../../../../base/common/uri.js'; import { isEqual } from '../../../../base/common/resources.js'; import { StandardTokenType } from '../../../../editor/common/encodedTokenAttributes.js'; -import { autorun, derived, observableFromEvent, observableValue } from '../../../../base/common/observable.js'; +import { autorun, derivedWithStore, observableFromEvent, observableValue } from '../../../../base/common/observable.js'; import { KeyChord, KeyCode, KeyMod } from '../../../../base/common/keyCodes.js'; import { KeybindingWeight } from '../../../../platform/keybinding/common/keybindingsRegistry.js'; import './media/inlineChat.css'; import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; import { ICommandService } from '../../../../platform/commands/common/commands.js'; import { InlineCompletionsController } from '../../../../editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.js'; -import { ChatAgentLocation, IChatAgentService } from '../../chat/common/chatAgents.js'; +import { IChatAgentService } from '../../chat/common/chatAgents.js'; import { IMarkerDecorationsService } from '../../../../editor/common/services/markerDecorations.js'; import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js'; import { toAction } from '../../../../base/common/actions.js'; @@ -38,6 +38,8 @@ import { PLAINTEXT_LANGUAGE_ID } from '../../../../editor/common/languages/modes import { createStyleSheet2 } from '../../../../base/browser/domStylesheets.js'; import { stringValue } from '../../../../base/browser/cssValue.js'; import { observableConfigValue } from '../../../../platform/observable/common/platformObservableUtils.js'; +import { Emitter } from '../../../../base/common/event.js'; +import { ChatAgentLocation } from '../../chat/common/constants.js'; export const CTX_INLINE_CHAT_SHOWING_HINT = new RawContextKey('inlineChatShowingHint', false, localize('inlineChatShowingHint', "Whether inline chat shows a contextual hint")); @@ -222,7 +224,7 @@ export class InlineChatHintsController extends Disposable implements IEditorCont const configHintEmpty = observableConfigValue(InlineChatConfigKeys.LineEmptyHint, false, this._configurationService); const configHintNL = observableConfigValue(InlineChatConfigKeys.LineNLHint, false, this._configurationService); - const showDataObs = derived(r => { + const showDataObs = derivedWithStore((r, store) => { const ghostState = ghostCtrl?.model.read(r)?.state.read(r); const textFocus = editorObs.isTextFocused.read(r); @@ -239,6 +241,12 @@ export class InlineChatHintsController extends Disposable implements IEditorCont return undefined; } + // DEBT - I cannot use `model.onDidChangeContent` directly here + // https://github.com/microsoft/vscode/issues/242059 + const emitter = store.add(new Emitter()); + store.add(model.onDidChangeContent(() => emitter.fire())); + observableFromEvent(emitter.event, () => model.getVersionId()).read(r); + const visible = this._visibilityObs.read(r); const isEol = model.getLineMaxColumn(position.lineNumber) === position.column; const isWhitespace = model.getLineLastNonWhitespaceColumn(position.lineNumber) === 0 && model.getValueLength() > 0 && position.column > 1; diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts index c153bb89559e..994a3a10a3cd 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSession.ts @@ -179,9 +179,8 @@ export class Session { } async undoChangesUntil(requestId: string): Promise { - - const targetAltVersion = this._versionByRequest.get(requestId); - if (targetAltVersion === undefined) { + const idx = this._exchanges.findIndex(candidate => candidate.response.chatResponse.requestId === requestId); + if (idx < 0) { return false; } // undo till this point @@ -193,6 +192,8 @@ export class Session { } finally { this.hunkData.ignoreTextModelNChanges = false; } + // remove this and following exchanges + this._exchanges.length = idx; return true; } @@ -236,8 +237,113 @@ export class Session { this._teldata.endTime = new Date().toISOString(); return this._teldata; } + + asRecording(): Recording { + const result: Recording = { + session: this.chatModel.sessionId, + when: this._startTime, + exchanges: [] + }; + for (const exchange of this._exchanges) { + const response = exchange.response; + if (response instanceof ReplyResponse) { + result.exchanges.push({ prompt: exchange.prompt.value, res: response.chatResponse }); + } + } + return result; + } +} + + +export class SessionPrompt { + + readonly value: string; + + constructor( + readonly request: IChatRequestModel, + readonly modelAltVersionId: number, + ) { + this.value = request.message.text; + } +} + +export class SessionExchange { + + constructor( + readonly prompt: SessionPrompt, + readonly response: ReplyResponse | EmptyResponse | ErrorResponse + ) { } +} + +export class EmptyResponse { + + constructor( + readonly chatResponse: IChatResponseModel + ) { } +} + +export class ErrorResponse { + + readonly message: string; + readonly isCancellation: boolean; + + constructor( + readonly chatResponse: IChatResponseModel, + readonly error: any + ) { + this.message = toErrorMessage(error, false); + this.isCancellation = isCancellationError(error); + } } +export class ReplyResponse { + + readonly untitledTextModel: IUntitledTextEditorModel | undefined; + + constructor( + localUri: URI, + readonly chatRequest: IChatRequestModel, + readonly chatResponse: IChatResponseModel, + @ITextFileService private readonly _textFileService: ITextFileService, + @ILanguageService private readonly _languageService: ILanguageService, + ) { + + const editsMap = new ResourceMap(); + + for (const item of chatResponse.response.value) { + if (item.kind === 'textEditGroup') { + const array = editsMap.get(item.uri); + for (const group of item.edits) { + if (array) { + array.push(group); + } else { + editsMap.set(item.uri, [group]); + } + } + } + } + + for (const [uri, edits] of editsMap) { + + const flatEdits = edits.flat(); + if (flatEdits.length === 0) { + editsMap.delete(uri); + continue; + } + + const isLocalUri = isEqual(uri, localUri); + if (uri.scheme === Schemas.untitled && !isLocalUri && !this.untitledTextModel) { //TODO@jrieken the first untitled model WINS + const langSelection = this._languageService.createByFilepathOrFirstLine(uri, undefined); + const untitledTextModel = this._textFileService.untitled.create({ + associatedResource: uri, + languageId: langSelection.languageId + }); + this.untitledTextModel = untitledTextModel; + untitledTextModel.resolve(); + } + } + } +} export class StashedSession { @@ -449,11 +555,12 @@ export class HunkData { diff ??= await this._editorWorkerService.computeDiff(this._textModel0.uri, this._textModelN.uri, { ignoreTrimWhitespace: false, maxComputationTimeMs: Number.MAX_SAFE_INTEGER, computeMoves: false }, 'advanced'); - let mergedChanges: DetailedLineRangeMapping[] = []; + let hunks: RawHunk[] = []; if (diff && diff.changes.length > 0) { + // merge changes neighboring changes - mergedChanges = [diff.changes[0]]; + const mergedChanges = [diff.changes[0]]; for (let i = 1; i < diff.changes.length; i++) { const lastChange = mergedChanges[mergedChanges.length - 1]; const thisChange = diff.changes[i]; @@ -467,9 +574,9 @@ export class HunkData { mergedChanges.push(thisChange); } } - } - const hunks = mergedChanges.map(change => new RawHunk(change.original, change.modified, change.innerChanges ?? [])); + hunks = mergedChanges.map(change => new RawHunk(change.original, change.modified, change.innerChanges ?? [])); + } editState.applied = hunks.length; diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl.ts index 5f06c2dacb4d..b8eefc1c2924 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatSessionServiceImpl.ts @@ -20,7 +20,7 @@ import { IInstantiationService } from '../../../../platform/instantiation/common import { ILogService } from '../../../../platform/log/common/log.js'; import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; import { DEFAULT_EDITOR_ASSOCIATION } from '../../../common/editor.js'; -import { ChatAgentLocation, IChatAgentService } from '../../chat/common/chatAgents.js'; +import { IChatAgentService } from '../../chat/common/chatAgents.js'; import { IChatService } from '../../chat/common/chatService.js'; import { CTX_INLINE_CHAT_HAS_AGENT, CTX_INLINE_CHAT_HAS_AGENT2, CTX_INLINE_CHAT_POSSIBLE } from '../common/inlineChat.js'; import { IEditorService } from '../../../services/editor/common/editorService.js'; @@ -34,6 +34,8 @@ import { IChatEditingService, WorkingSetEntryState } from '../../chat/common/cha import { assertType } from '../../../../base/common/types.js'; import { autorun } from '../../../../base/common/observable.js'; import { ResourceMap } from '../../../../base/common/map.js'; +import { IChatWidgetService } from '../../chat/browser/chat.js'; +import { ChatAgentLocation } from '../../chat/common/constants.js'; type SessionData = { @@ -85,6 +87,7 @@ export class InlineChatSessionServiceImpl implements IInlineChatSessionService { @IChatService private readonly _chatService: IChatService, @IChatAgentService private readonly _chatAgentService: IChatAgentService, @IChatEditingService private readonly _chatEditingService: IChatEditingService, + @IChatWidgetService private readonly _chatWidgetService: IChatWidgetService, ) { } dispose() { @@ -142,18 +145,31 @@ export class InlineChatSessionServiceImpl implements IInlineChatSessionService { lastResponseListener.clear(); // ONCE - // special handling for untitled files - for (const part of response.response.value) { - if (part.kind !== 'textEditGroup' || part.uri.scheme !== Schemas.untitled || isEqual(part.uri, session.textModelN.uri)) { - continue; - } - const langSelection = this._languageService.createByFilepathOrFirstLine(part.uri, undefined); - const untitledTextModel = this._textFileService.untitled.create({ - associatedResource: part.uri, - languageId: langSelection.languageId - }); - untitledTextModel.resolve(); - this._textModelService.createModelReference(part.uri).then(ref => { + let inlineResponse: ErrorResponse | EmptyResponse | ReplyResponse; + + // make an response from the ChatResponseModel + if (response.isCanceled) { + // error: cancelled + inlineResponse = new ErrorResponse(response, new CancellationError()); + } else if (response.result?.errorDetails) { + // error: "real" error + inlineResponse = new ErrorResponse(response, new Error(response.result.errorDetails.message)); + } else if (response.response.value.length === 0) { + // epmty response + inlineResponse = new EmptyResponse(response); + } else { + inlineResponse = this._instaService.createInstance( + ReplyResponse, + session.textModelN.uri, + e.request, + response + ); + } + + session.addExchange(new SessionExchange(session.lastInput!, inlineResponse)); + + if (inlineResponse instanceof ReplyResponse && inlineResponse.untitledTextModel) { + this._textModelService.createModelReference(inlineResponse.untitledTextModel.resource).then(ref => { store.add(ref); }); } @@ -338,7 +354,8 @@ export class InlineChatSessionServiceImpl implements IInlineChatSessionService { const chatModel = this._chatService.startSession(ChatAgentLocation.EditingSession, token); const editingSession = await this._chatEditingService.createEditingSession(chatModel.sessionId); - editingSession.addFileToWorkingSet(uri); + const widget = this._chatWidgetService.getWidgetBySessionId(chatModel.sessionId); + widget?.attachmentModel.addFile(uri); const store = new DisposableStore(); store.add(toDisposable(() => { @@ -406,10 +423,10 @@ export class InlineChatEnabler { const updateAgent = () => { const agent = chatAgentService.getDefaultAgent(ChatAgentLocation.Editor); - if (agent?.locations.length === 1) { + if (agent?.id === 'github.copilot.editor') { this._ctxHasProvider.set(true); this._ctxHasProvider2.reset(); - } else if (agent?.locations.includes(ChatAgentLocation.EditingSession)) { + } else if (agent?.id === 'github.copilot.editingSessionEditor') { this._ctxHasProvider.reset(); this._ctxHasProvider2.set(true); } else { diff --git a/src/vs/workbench/contrib/inlineChat/browser/inlineChatZoneWidget.ts b/src/vs/workbench/contrib/inlineChat/browser/inlineChatZoneWidget.ts index 138b51910c44..838360429399 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatZoneWidget.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatZoneWidget.ts @@ -22,7 +22,7 @@ import { ILogService } from '../../../../platform/log/common/log.js'; import { IChatWidgetViewOptions } from '../../chat/browser/chat.js'; import { IChatWidgetLocationOptions } from '../../chat/browser/chatWidget.js'; import { isResponseVM } from '../../chat/common/chatViewModel.js'; -import { ACTION_REGENERATE_RESPONSE, ACTION_REPORT_ISSUE, ACTION_TOGGLE_DIFF, CTX_INLINE_CHAT_OUTER_CURSOR_POSITION, MENU_INLINE_CHAT_WIDGET_SECONDARY, MENU_INLINE_CHAT_WIDGET_STATUS } from '../common/inlineChat.js'; +import { ACTION_REGENERATE_RESPONSE, ACTION_REPORT_ISSUE, ACTION_TOGGLE_DIFF, CTX_INLINE_CHAT_OUTER_CURSOR_POSITION, MENU_INLINE_CHAT_SIDE, MENU_INLINE_CHAT_WIDGET_SECONDARY, MENU_INLINE_CHAT_WIDGET_STATUS } from '../common/inlineChat.js'; import { EditorBasedInlineChatWidget } from './inlineChatWidget.js'; export class InlineChatZoneWidget extends ZoneWidget { @@ -81,6 +81,7 @@ export class InlineChatZoneWidget extends ZoneWidget { chatWidgetViewOptions: { menus: { telemetrySource: 'interactiveEditorWidget-toolbar', + inputSideToolbar: MENU_INLINE_CHAT_SIDE }, ...options, rendererOptions: { diff --git a/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts b/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts index d2a96c1d2cd8..3b6979b8f10b 100644 --- a/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts +++ b/src/vs/workbench/contrib/inlineChat/common/inlineChat.ts @@ -106,6 +106,8 @@ export const MENU_INLINE_CHAT_WIDGET_STATUS = MenuId.for('inlineChatWidget.statu export const MENU_INLINE_CHAT_WIDGET_SECONDARY = MenuId.for('inlineChatWidget.secondary'); export const MENU_INLINE_CHAT_ZONE = MenuId.for('inlineChatWidget.changesZone'); +export const MENU_INLINE_CHAT_SIDE = MenuId.for('inlineChatWidget.side'); + // --- colors diff --git a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts index 185476fb7f84..49aa40744eeb 100644 --- a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts +++ b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatController.test.ts @@ -31,7 +31,7 @@ import { IView, IViewDescriptorService } from '../../../../common/views.js'; import { AccessibilityVerbositySettingId } from '../../../accessibility/browser/accessibilityConfiguration.js'; import { IAccessibleViewService } from '../../../../../platform/accessibility/browser/accessibleView.js'; import { IChatAccessibilityService, IChatWidget, IChatWidgetService } from '../../../chat/browser/chat.js'; -import { ChatAgentLocation, ChatAgentService, IChatAgentData, IChatAgentNameService, IChatAgentService } from '../../../chat/common/chatAgents.js'; +import { ChatAgentService, IChatAgentData, IChatAgentNameService, IChatAgentService } from '../../../chat/common/chatAgents.js'; import { IChatResponseViewModel } from '../../../chat/common/chatViewModel.js'; import { InlineChatController1, State } from '../../browser/inlineChatController.js'; import { CTX_INLINE_CHAT_RESPONSE_TYPE, InlineChatConfigKeys, InlineChatResponseType } from '../../common/inlineChat.js'; @@ -71,6 +71,7 @@ import { ChatInputBoxContentProvider } from '../../../chat/browser/chatEdinputIn import { constObservable, IObservable } from '../../../../../base/common/observable.js'; import { ILanguageModelToolsService } from '../../../chat/common/languageModelToolsService.js'; import { MockLanguageModelToolsService } from '../../../chat/test/common/mockLanguageModelToolsService.js'; +import { ChatAgentLocation } from '../../../chat/common/constants.js'; suite('InlineChatController', function () { diff --git a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatSession.test.ts b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatSession.test.ts index f2d8cc86365b..5e2024222e7f 100644 --- a/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatSession.test.ts +++ b/src/vs/workbench/contrib/inlineChat/test/browser/inlineChatSession.test.ts @@ -49,7 +49,7 @@ import { IChatVariablesService } from '../../../chat/common/chatVariables.js'; import { IChatWidgetHistoryService, ChatWidgetHistoryService } from '../../../chat/common/chatWidgetHistoryService.js'; import { IViewsService } from '../../../../services/views/common/viewsService.js'; import { TestExtensionService, TestContextService } from '../../../../test/common/workbenchTestServices.js'; -import { IChatAgentService, ChatAgentService, ChatAgentLocation } from '../../../chat/common/chatAgents.js'; +import { IChatAgentService, ChatAgentService } from '../../../chat/common/chatAgents.js'; import { ChatVariablesService } from '../../../chat/browser/chatVariables.js'; import { ICommandService } from '../../../../../platform/commands/common/commands.js'; import { TestCommandService } from '../../../../../editor/test/browser/editorTestServices.js'; @@ -62,6 +62,7 @@ import { IChatRequestModel } from '../../../chat/common/chatModel.js'; import { assertSnapshot } from '../../../../../base/test/common/snapshot.js'; import { IObservable, constObservable } from '../../../../../base/common/observable.js'; import { IChatEditingService, IChatEditingSession } from '../../../chat/common/chatEditingService.js'; +import { ChatAgentLocation } from '../../../chat/common/constants.js'; suite('InlineChatSession', function () { diff --git a/src/vs/workbench/contrib/inlineCompletions/browser/inlineCompletionLanguageStatusBarContribution.ts b/src/vs/workbench/contrib/inlineCompletions/browser/inlineCompletionLanguageStatusBarContribution.ts index 55b9bea7f578..75930b380dd8 100644 --- a/src/vs/workbench/contrib/inlineCompletions/browser/inlineCompletionLanguageStatusBarContribution.ts +++ b/src/vs/workbench/contrib/inlineCompletions/browser/inlineCompletionLanguageStatusBarContribution.ts @@ -3,25 +3,31 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { localize } from '../../../../nls.js'; import { createHotClass } from '../../../../base/common/hotReloadHelpers.js'; -import { Disposable } from '../../../../base/common/lifecycle.js'; +import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.js'; import { autorunWithStore, derived } from '../../../../base/common/observable.js'; import { debouncedObservable } from '../../../../base/common/observableInternal/utils.js'; import Severity from '../../../../base/common/severity.js'; import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js'; import { InlineCompletionsController } from '../../../../editor/contrib/inlineCompletions/browser/controller/inlineCompletionsController.js'; import { ILanguageStatusService } from '../../../services/languageStatus/common/languageStatusService.js'; +import { observableCodeEditor } from '../../../../editor/browser/observableCodeEditor.js'; export class InlineCompletionLanguageStatusBarContribution extends Disposable { public static readonly hot = createHotClass(InlineCompletionLanguageStatusBarContribution); public static Id = 'vs.editor.contrib.inlineCompletionLanguageStatusBarContribution'; + public static readonly languageStatusBarDisposables = new Set(); private readonly _c = InlineCompletionsController.get(this._editor); private readonly _state = derived(this, reader => { const model = this._c?.model.read(reader); if (!model) { return undefined; } + if (!observableCodeEditor(this._editor).isFocused.read(reader)) { + return undefined; + } return { model, @@ -44,20 +50,29 @@ export class InlineCompletionLanguageStatusBarContribution extends Disposable { const status = state.status.read(reader); const statusMap: Record = { - loading: { shortLabel: '', label: 'Loading', loading: true, }, - ghostText: { shortLabel: '$(lightbulb)', label: 'Inline Completion available', loading: false, }, - inlineEdit: { shortLabel: '$(lightbulb-sparkle)', label: 'Inline Edit available', loading: false, }, - noSuggestion: { shortLabel: '$(circle-slash)', label: 'No inline suggestion available', loading: false, }, + loading: { shortLabel: '', label: localize('inlineSuggestionLoading', "Loading..."), loading: true, }, + ghostText: { shortLabel: '$(lightbulb)', label: '$(copilot) ' + localize('inlineCompletionAvailable', "Inline completion available"), loading: false, }, + inlineEdit: { shortLabel: '$(lightbulb-sparkle)', label: '$(copilot) ' + localize('inlineEditAvailable', "Inline edit available"), loading: false, }, + noSuggestion: { shortLabel: '$(circle-slash)', label: '$(copilot) ' + localize('noInlineSuggestionAvailable', "No inline suggestion available"), loading: false, }, }; + // Make sure previous status is cleared before the new is registered. This works, but is a bit hacky. + // TODO: Use a workbench contribution to get singleton behavior. + InlineCompletionLanguageStatusBarContribution.languageStatusBarDisposables.forEach(d => d.clear()); + + InlineCompletionLanguageStatusBarContribution.languageStatusBarDisposables.add(store); + store.add({ + dispose: () => InlineCompletionLanguageStatusBarContribution.languageStatusBarDisposables.delete(store) + }); + store.add(this._languageStatusService.addStatus({ accessibilityInfo: undefined, busy: statusMap[status].loading, command: undefined, - detail: 'Inline Suggestions', + detail: localize('inlineSuggestionsSmall', "Inline suggestions"), id: 'inlineSuggestions', label: { value: statusMap[status].label, shortValue: statusMap[status].shortLabel }, - name: 'Inline Suggestions', + name: localize('inlineSuggestions', "Inline Suggestions"), selector: { pattern: state.model.textModel.uri.fsPath }, severity: Severity.Info, source: 'inlineSuggestions', diff --git a/src/vs/workbench/contrib/issue/browser/baseIssueReporterService.ts b/src/vs/workbench/contrib/issue/browser/baseIssueReporterService.ts index 38da042c4095..c5074ca006ae 100644 --- a/src/vs/workbench/contrib/issue/browser/baseIssueReporterService.ts +++ b/src/vs/workbench/contrib/issue/browser/baseIssueReporterService.ts @@ -65,6 +65,8 @@ export class BaseIssueReporterService extends Disposable { public delayedSubmit = new Delayer(300); public previewButton!: Button; public nonGitHubIssueUrl = false; + public needsUpdate = false; + public acknowledged = false; constructor( public disableExtensions: boolean, @@ -103,7 +105,7 @@ export class BaseIssueReporterService extends Disposable { //TODO: Handle case where extension is not activated const issueReporterElement = this.getElementById('issue-reporter'); if (issueReporterElement) { - this.previewButton = new Button(issueReporterElement, unthemedButtonStyles); + this.previewButton = this._register(new Button(issueReporterElement, unthemedButtonStyles)); const issueRepoName = document.createElement('a'); issueReporterElement.appendChild(issueRepoName); issueRepoName.id = 'show-repo-name'; @@ -141,7 +143,7 @@ export class BaseIssueReporterService extends Disposable { } const delayer = new RunOnceScheduler(updateAll, 0); - iconsStyleSheet.onDidChange(() => delayer.schedule()); + this._register(iconsStyleSheet.onDidChange(() => delayer.schedule())); delayer.schedule(); this.handleExtensionData(data.enabledExtensions); @@ -387,6 +389,14 @@ export class BaseIssueReporterService extends Disposable { } } + private updateAcknowledgementState() { + const acknowledgementCheckbox = this.getElementById('includeAcknowledgement'); + if (acknowledgementCheckbox) { + this.acknowledged = acknowledgementCheckbox.checked; + this.updatePreviewButtonState(); + } + } + public setEventHandlers(): void { (['includeSystemInfo', 'includeProcessInfo', 'includeWorkspaceInfo', 'includeExtensions', 'includeExperiments', 'includeExtensionData'] as const).forEach(elementId => { this.addEventListener(elementId, 'click', (event: Event) => { @@ -395,6 +405,11 @@ export class BaseIssueReporterService extends Disposable { }); }); + this.addEventListener('includeAcknowledgement', 'click', (event: Event) => { + event.stopPropagation(); + this.updateAcknowledgementState(); + }); + const showInfoElements = this.window.document.getElementsByClassName('showInfo'); for (let i = 0; i < showInfoElements.length; i++) { const showInfo = showInfoElements.item(i)!; @@ -490,11 +505,11 @@ export class BaseIssueReporterService extends Disposable { this.searchIssues(title, fileOnExtension, fileOnMarketplace); }); - this.previewButton.onDidClick(async () => { + this._register(this.previewButton.onDidClick(async () => { this.delayedSubmit.trigger(async () => { this.createIssue(); }); - }); + })); this.addEventListener('disableExtensions', 'click', () => { this.issueFormService.reloadWithExtensionsDisabled(); @@ -561,7 +576,11 @@ export class BaseIssueReporterService extends Disposable { } public updatePreviewButtonState() { - if (this.isPreviewEnabled()) { + + if (!this.acknowledged && this.needsUpdate) { + this.previewButton.label = localize('acknowledge', "Confirm Version Acknowledgement"); + this.previewButton.enabled = false; + } else if (this.isPreviewEnabled()) { if (this.data.githubAccessToken) { this.previewButton.label = localize('createOnGitHub', "Create on GitHub"); } else { diff --git a/src/vs/workbench/contrib/issue/browser/issueFormService.ts b/src/vs/workbench/contrib/issue/browser/issueFormService.ts index bf42337b69e5..c587880a09c6 100644 --- a/src/vs/workbench/contrib/issue/browser/issueFormService.ts +++ b/src/vs/workbench/contrib/issue/browser/issueFormService.ts @@ -98,11 +98,13 @@ export class IssueFormService implements IIssueFormService { this.issueReporterWindow = auxiliaryWindow.window; } else { console.error('Failed to open auxiliary window'); + disposables.dispose(); } // handle closing issue reporter this.issueReporterWindow?.addEventListener('beforeunload', () => { auxiliaryWindow.window.close(); + disposables.dispose(); this.issueReporterWindow = null; }); } diff --git a/src/vs/workbench/contrib/issue/browser/issueReporterPage.ts b/src/vs/workbench/contrib/issue/browser/issueReporterPage.ts index fbd7fbb3fb8d..e739f44817f7 100644 --- a/src/vs/workbench/contrib/issue/browser/issueReporterPage.ts +++ b/src/vs/workbench/contrib/issue/browser/issueReporterPage.ts @@ -12,6 +12,7 @@ const sendWorkspaceInfoLabel = escape(localize('sendWorkspaceInfo', "Include my const sendExtensionsLabel = escape(localize('sendExtensions', "Include my enabled extensions")); const sendExperimentsLabel = escape(localize('sendExperiments', "Include A/B experiment info")); const sendExtensionData = escape(localize('sendExtensionData', "Include additional extension info")); +const acknowledgementsLabel = escape(localize('acknowledgements', "I acknowledge that my VS Code version is not updated and this issue may be closed.")); const reviewGuidanceLabel = localize( // intentionally not escaped because of its embedded tags { key: 'reviewGuidanceLabel', @@ -20,10 +21,15 @@ const reviewGuidanceLabel = localize( // intentionally not escaped because of it '{Locked=""}' ] }, - 'Before you report an issue here please review the guidance we provide.' + 'Before you report an issue here please review the guidance we provide. Please complete the form in English.' ); export default (): string => ` +
@@ -154,5 +160,11 @@ export default (): string => `
+
`; diff --git a/src/vs/workbench/contrib/issue/browser/media/issueReporter.css b/src/vs/workbench/contrib/issue/browser/media/issueReporter.css index 14ee09bebd1c..23f3be9ab74b 100644 --- a/src/vs/workbench/contrib/issue/browser/media/issueReporter.css +++ b/src/vs/workbench/contrib/issue/browser/media/issueReporter.css @@ -97,7 +97,6 @@ line-height: 1.5; color: #495057; background-color: #fff; - border: none; } .issue-reporter * { @@ -184,7 +183,7 @@ .issue-reporter body { margin: 0; - overflow-y: scroll; + overflow-y: auto; height: 100%; } @@ -213,12 +212,11 @@ padding-bottom: 2em; display: flex; flex-direction: column; - min-height: 100%; overflow: visible; } .issue-reporter .description-section { - flex-grow: 1; + flex-grow: 0; display: flex; flex-direction: column; flex-shrink: 0; @@ -226,12 +224,13 @@ .issue-reporter textarea { flex-grow: 1; - min-height: 150px; + height: 200px; } .issue-reporter .block-info-text { display: flex; - flex-grow: 1; + flex-grow: 0; + flex-direction: column; } .issue-reporter #github-submit-btn { @@ -320,7 +319,6 @@ .issue-reporter select, .issue-reporter textarea { background-color: #3c3c3c; - border: none; color: #cccccc; } @@ -456,3 +454,4 @@ .issue-reporter a { color: var(--vscode-textLink-foreground); } + diff --git a/src/vs/workbench/contrib/issue/common/issue.contribution.ts b/src/vs/workbench/contrib/issue/common/issue.contribution.ts index a2805613d961..43be68be59eb 100644 --- a/src/vs/workbench/contrib/issue/common/issue.contribution.ts +++ b/src/vs/workbench/contrib/issue/common/issue.contribution.ts @@ -3,16 +3,17 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { Disposable } from '../../../../base/common/lifecycle.js'; import { localize, localize2 } from '../../../../nls.js'; import { ICommandAction } from '../../../../platform/action/common/action.js'; import { Categories } from '../../../../platform/action/common/actionCommonCategories.js'; import { MenuId, MenuRegistry } from '../../../../platform/actions/common/actions.js'; import { CommandsRegistry, ICommandMetadata } from '../../../../platform/commands/common/commands.js'; +import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; +import { INotificationService } from '../../../../platform/notification/common/notification.js'; import { IProductService } from '../../../../platform/product/common/productService.js'; import { IWorkbenchContribution } from '../../../common/contributions.js'; -import { IWorkbenchIssueService, IssueReporterData } from './issue.js'; -import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; -import { Disposable } from '../../../../base/common/lifecycle.js'; +import { IssueReporterData, IWorkbenchIssueService } from './issue.js'; const OpenIssueReporterActionId = 'workbench.action.openIssueReporter'; const OpenIssueReporterApiId = 'vscode.openIssueReporter'; @@ -65,6 +66,18 @@ export class BaseIssueContribution extends Disposable implements IWorkbenchContr ) { super(); + if (configurationService.getValue('telemetry.disableFeedback')) { + this._register(CommandsRegistry.registerCommand({ + id: 'workbench.action.openIssueReporter', + handler: function (accessor) { + const data = accessor.get(INotificationService); + data.info('Feedback is disabled.'); + + }, + })); + return; + } + if (!productService.reportIssueUrl) { return; } diff --git a/src/vs/workbench/contrib/issue/electron-sandbox/issue.contribution.ts b/src/vs/workbench/contrib/issue/electron-sandbox/issue.contribution.ts index 4ff3e642e27b..9d0bcff49594 100644 --- a/src/vs/workbench/contrib/issue/electron-sandbox/issue.contribution.ts +++ b/src/vs/workbench/contrib/issue/electron-sandbox/issue.contribution.ts @@ -3,25 +3,25 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ +import { IDisposable } from '../../../../base/common/lifecycle.js'; import { localize, localize2 } from '../../../../nls.js'; -import { registerAction2, Action2 } from '../../../../platform/actions/common/actions.js'; -import { IWorkbenchIssueService, IssueType, IIssueFormService } from '../common/issue.js'; -import { BaseIssueContribution } from '../common/issue.contribution.js'; +import { Categories } from '../../../../platform/action/common/actionCommonCategories.js'; +import { Action2, registerAction2 } from '../../../../platform/actions/common/actions.js'; +import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; +import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; +import { ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; import { IProductService } from '../../../../platform/product/common/productService.js'; +import { IQuickAccessRegistry, Extensions as QuickAccessExtensions } from '../../../../platform/quickinput/common/quickAccess.js'; import { Registry } from '../../../../platform/registry/common/platform.js'; import { Extensions, IWorkbenchContributionsRegistry } from '../../../common/contributions.js'; import { LifecyclePhase } from '../../../services/lifecycle/common/lifecycle.js'; -import { Categories } from '../../../../platform/action/common/actionCommonCategories.js'; -import { ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; -import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; -import { IDisposable } from '../../../../base/common/lifecycle.js'; -import { IQuickAccessRegistry, Extensions as QuickAccessExtensions } from '../../../../platform/quickinput/common/quickAccess.js'; import { IssueQuickAccess } from '../browser/issueQuickAccess.js'; -import { registerSingleton, InstantiationType } from '../../../../platform/instantiation/common/extensions.js'; -import { NativeIssueService } from './issueService.js'; -import './processMainService.js'; import '../browser/issueTroubleshoot.js'; +import { BaseIssueContribution } from '../common/issue.contribution.js'; +import { IIssueFormService, IWorkbenchIssueService, IssueType } from '../common/issue.js'; +import { NativeIssueService } from './issueService.js'; import { NativeIssueFormService } from './nativeIssueFormService.js'; +import './processMainService.js'; //#region Issue Contribution registerSingleton(IWorkbenchIssueService, NativeIssueService, InstantiationType.Delayed); @@ -35,6 +35,10 @@ class NativeIssueContribution extends BaseIssueContribution { ) { super(productService, configurationService); + if (configurationService.getValue('telemetry.disableFeedback')) { + return; + } + if (productService.reportIssueUrl) { this._register(registerAction2(ReportPerformanceIssueUsingReporterAction)); } diff --git a/src/vs/workbench/contrib/issue/electron-sandbox/issueReporterService.ts b/src/vs/workbench/contrib/issue/electron-sandbox/issueReporterService.ts index e6be6f94d322..bb27e35e45dd 100644 --- a/src/vs/workbench/contrib/issue/electron-sandbox/issueReporterService.ts +++ b/src/vs/workbench/contrib/issue/electron-sandbox/issueReporterService.ts @@ -16,6 +16,7 @@ import { IFileService } from '../../../../platform/files/common/files.js'; import { INativeHostService } from '../../../../platform/native/common/native.js'; import { IProcessMainService } from '../../../../platform/process/common/process.js'; import { IThemeService } from '../../../../platform/theme/common/themeService.js'; +import { IUpdateService, StateType } from '../../../../platform/update/common/update.js'; import { applyZoom } from '../../../../platform/window/electron-sandbox/window.js'; import { BaseIssueReporterService } from '../browser/baseIssueReporterService.js'; import { IssueReporterData as IssueReporterModelData } from '../browser/issueReporterModel.js'; @@ -47,7 +48,8 @@ export class IssueReporter extends BaseIssueReporterService { @IProcessMainService processMainService: IProcessMainService, @IThemeService themeService: IThemeService, @IFileService fileService: IFileService, - @IFileDialogService fileDialogService: IFileDialogService + @IFileDialogService fileDialogService: IFileDialogService, + @IUpdateService private readonly updateService: IUpdateService ) { super(disableExtensions, data, os, product, window, false, issueFormService, themeService, fileService, fileDialogService); this.processMainService = processMainService; @@ -64,6 +66,7 @@ export class IssueReporter extends BaseIssueReporterService { }); } + this.checkForUpdates(); this.setEventHandlers(); applyZoom(this.data.zoomLevel, this.window); this.updateExperimentsInfo(this.data.experiments); @@ -71,6 +74,20 @@ export class IssueReporter extends BaseIssueReporterService { this.updateUnsupportedMode(this.data.isUnsupported); } + private async checkForUpdates(): Promise { + const updateState = this.updateService.state; + if (updateState.type === StateType.Ready || updateState.type === StateType.Downloaded) { + this.needsUpdate = true; + const includeAcknowledgement = this.getElementById('version-acknowledgements'); + const updateBanner = this.getElementById('update-banner'); + if (updateBanner && includeAcknowledgement) { + includeAcknowledgement.classList.remove('hidden'); + updateBanner.classList.remove('hidden'); + updateBanner.textContent = localize('updateAvailable', "A new version of {0} is available.", this.product.nameLong); + } + } + } + public override setEventHandlers(): void { super.setEventHandlers(); diff --git a/src/vs/workbench/contrib/issue/electron-sandbox/media/issueReporter.css b/src/vs/workbench/contrib/issue/electron-sandbox/media/issueReporter.css index c88a95a95e02..aebbe9804280 100644 --- a/src/vs/workbench/contrib/issue/electron-sandbox/media/issueReporter.css +++ b/src/vs/workbench/contrib/issue/electron-sandbox/media/issueReporter.css @@ -175,7 +175,7 @@ body.issue-reporter-body { margin: 0 !important; - overflow-y: scroll !important; + overflow-y: auto !important; height: 100% !important; } @@ -204,25 +204,25 @@ body.issue-reporter-body { padding-bottom: 2em; display: flex; flex-direction: column; - min-height: 100%; overflow: visible; } .issue-reporter-body .description-section { - flex-grow: 1; + flex-grow: 0; display: flex; flex-direction: column; flex-shrink: 0; } .issue-reporter-body textarea { - flex-grow: 1; - min-height: 150px; + flex-grow: 0; + height: 200px; } .issue-reporter-body .block-info-text { display: flex; - flex-grow: 1; + flex-grow: 0; + flex-direction: column; } .issue-reporter-body #github-submit-btn { @@ -468,3 +468,13 @@ body.issue-reporter-body { .issue-reporter-body .issues-container > .issue .issue-icon { padding-top: 2px; } + +.issue-reporter-update-banner { + color: var(--vscode-button-foreground); + background-color: var(--vscode-button-background); + padding: 10px; + text-align: center; + position: sticky; + top: 0; + z-index: 1000; +} diff --git a/src/vs/workbench/contrib/issue/electron-sandbox/nativeIssueFormService.ts b/src/vs/workbench/contrib/issue/electron-sandbox/nativeIssueFormService.ts index c202234088a8..f6ae15199b8b 100644 --- a/src/vs/workbench/contrib/issue/electron-sandbox/nativeIssueFormService.ts +++ b/src/vs/workbench/contrib/issue/electron-sandbox/nativeIssueFormService.ts @@ -3,7 +3,7 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import './media/issueReporter.css'; +import { DisposableStore } from '../../../../base/common/lifecycle.js'; import { IMenuService } from '../../../../platform/actions/common/actions.js'; import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js'; @@ -12,13 +12,16 @@ import { IInstantiationService } from '../../../../platform/instantiation/common import { ILogService } from '../../../../platform/log/common/log.js'; import { INativeHostService } from '../../../../platform/native/common/native.js'; import product from '../../../../platform/product/common/product.js'; +import { IAuxiliaryWindowService } from '../../../services/auxiliaryWindow/browser/auxiliaryWindowService.js'; +import { IHostService } from '../../../services/host/browser/host.js'; import { IssueFormService } from '../browser/issueFormService.js'; import { IIssueFormService, IssueReporterData } from '../common/issue.js'; import { IssueReporter } from './issueReporterService.js'; -import { IAuxiliaryWindowService } from '../../../services/auxiliaryWindow/browser/auxiliaryWindowService.js'; -import { IHostService } from '../../../services/host/browser/host.js'; +import './media/issueReporter.css'; export class NativeIssueFormService extends IssueFormService implements IIssueFormService { + private readonly store = new DisposableStore(); + constructor( @IInstantiationService instantiationService: IInstantiationService, @IAuxiliaryWindowService auxiliaryWindowService: IAuxiliaryWindowService, @@ -53,8 +56,10 @@ export class NativeIssueFormService extends IssueFormService implements IIssueFo // create issue reporter and instantiate if (this.issueReporterWindow) { - const issueReporter = this.instantiationService.createInstance(IssueReporter, !!this.environmentService.disableExtensions, data, { type: this.type, arch: this.arch, release: this.release }, product, this.issueReporterWindow); + const issueReporter = this.store.add(this.instantiationService.createInstance(IssueReporter, !!this.environmentService.disableExtensions, data, { type: this.type, arch: this.arch, release: this.release }, product, this.issueReporterWindow)); issueReporter.render(); + } else { + this.store.dispose(); } } } diff --git a/src/vs/workbench/contrib/languageDetection/browser/languageDetection.contribution.ts b/src/vs/workbench/contrib/languageDetection/browser/languageDetection.contribution.ts index d32ed42432f7..729a6932d6ed 100644 --- a/src/vs/workbench/contrib/languageDetection/browser/languageDetection.contribution.ts +++ b/src/vs/workbench/contrib/languageDetection/browser/languageDetection.contribution.ts @@ -103,7 +103,7 @@ class LanguageDetectionStatusContribution implements IWorkbenchContribution { text: '$(lightbulb-autofix)', }; if (!this._combinedEntry) { - this._combinedEntry = this._statusBarService.addEntry(props, LanguageDetectionStatusContribution._id, StatusbarAlignment.RIGHT, { id: 'status.editor.mode', alignment: StatusbarAlignment.RIGHT, compact: true }); + this._combinedEntry = this._statusBarService.addEntry(props, LanguageDetectionStatusContribution._id, StatusbarAlignment.RIGHT, { location: { id: 'status.editor.mode', priority: 100.1 }, alignment: StatusbarAlignment.RIGHT, compact: true }); } else { this._combinedEntry.update(props); } diff --git a/src/vs/workbench/contrib/languageStatus/browser/languageStatus.ts b/src/vs/workbench/contrib/languageStatus/browser/languageStatus.ts index 142d518e68be..17bff692d742 100644 --- a/src/vs/workbench/contrib/languageStatus/browser/languageStatus.ts +++ b/src/vs/workbench/contrib/languageStatus/browser/languageStatus.ts @@ -224,7 +224,7 @@ class LanguageStatus { text: isOneBusy ? '$(loading~spin)' : text, }; if (!this._combinedEntry) { - this._combinedEntry = this._statusBarService.addEntry(props, LanguageStatus._id, StatusbarAlignment.RIGHT, { id: 'status.editor.mode', alignment: StatusbarAlignment.LEFT, compact: true }); + this._combinedEntry = this._statusBarService.addEntry(props, LanguageStatus._id, StatusbarAlignment.RIGHT, { location: { id: 'status.editor.mode', priority: 100.1 }, alignment: StatusbarAlignment.LEFT, compact: true }); } else { this._combinedEntry.update(props); } @@ -274,7 +274,7 @@ class LanguageStatus { const props = LanguageStatus._asStatusbarEntry(status); let entry = this._dedicatedEntries.get(status.id); if (!entry) { - entry = this._statusBarService.addEntry(props, status.id, StatusbarAlignment.RIGHT, { id: 'status.editor.mode', alignment: StatusbarAlignment.RIGHT }); + entry = this._statusBarService.addEntry(props, status.id, StatusbarAlignment.RIGHT, { location: { id: 'status.editor.mode', priority: 100.1 }, alignment: StatusbarAlignment.RIGHT }); } else { entry.update(props); this._dedicatedEntries.delete(status.id); diff --git a/src/vs/workbench/contrib/limitIndicator/browser/limitIndicator.contribution.ts b/src/vs/workbench/contrib/limitIndicator/browser/limitIndicator.contribution.ts index 538e67d5f0fb..0640e07026d3 100644 --- a/src/vs/workbench/contrib/limitIndicator/browser/limitIndicator.contribution.ts +++ b/src/vs/workbench/contrib/limitIndicator/browser/limitIndicator.contribution.ts @@ -75,7 +75,7 @@ interface LanguageFeatureAccessor { class ColorDecorationAccessor implements LanguageFeatureAccessor { readonly id = 'decoratorsLimitInfo'; readonly name = nls.localize('colorDecoratorsStatusItem.name', 'Color Decorator Status'); - readonly label = nls.localize('status.limitedColorDecorators.short', 'Color Decorators'); + readonly label = nls.localize('status.limitedColorDecorators.short', 'Color decorators'); readonly source = nls.localize('colorDecoratorsStatusItem.source', 'Color Decorators'); readonly settingsId = 'editor.colorDecoratorsLimit'; @@ -87,7 +87,7 @@ class ColorDecorationAccessor implements LanguageFeatureAccessor { class FoldingRangeAccessor implements LanguageFeatureAccessor { readonly id = 'foldingLimitInfo'; readonly name = nls.localize('foldingRangesStatusItem.name', 'Folding Status'); - readonly label = nls.localize('status.limitedFoldingRanges.short', 'Folding Ranges'); + readonly label = nls.localize('status.limitedFoldingRanges.short', 'Folding ranges'); readonly source = nls.localize('foldingRangesStatusItem.source', 'Folding'); readonly settingsId = 'editor.foldingMaximumRegions'; diff --git a/src/vs/workbench/contrib/markers/browser/markersTable.ts b/src/vs/workbench/contrib/markers/browser/markersTable.ts index af8e6eafa18a..3a10bafb3272 100644 --- a/src/vs/workbench/contrib/markers/browser/markersTable.ts +++ b/src/vs/workbench/contrib/markers/browser/markersTable.ts @@ -13,7 +13,7 @@ import { IOpenEvent, IWorkbenchTableOptions, WorkbenchTable } from '../../../../ import { HighlightedLabel } from '../../../../base/browser/ui/highlightedlabel/highlightedLabel.js'; import { compareMarkersByUri, Marker, MarkerTableItem, ResourceMarkers } from './markersModel.js'; import { MarkerSeverity } from '../../../../platform/markers/common/markers.js'; -import { SeverityIcon } from '../../../../platform/severityIcon/browser/severityIcon.js'; +import { SeverityIcon } from '../../../../base/browser/ui/severityIcon/severityIcon.js'; import { ActionBar } from '../../../../base/browser/ui/actionbar/actionbar.js'; import { ILabelService } from '../../../../platform/label/common/label.js'; import { FilterOptions } from './markersFilterOptions.js'; diff --git a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts index 6e639a98c133..4ed27e35584a 100644 --- a/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts +++ b/src/vs/workbench/contrib/markers/browser/markersTreeViewer.ts @@ -26,7 +26,7 @@ import { Event, Emitter } from '../../../../base/common/event.js'; import { IListAccessibilityProvider } from '../../../../base/browser/ui/list/listWidget.js'; import { isUndefinedOrNull } from '../../../../base/common/types.js'; import { URI } from '../../../../base/common/uri.js'; -import { Action, IAction } from '../../../../base/common/actions.js'; +import { Action, IAction, toAction } from '../../../../base/common/actions.js'; import { localize } from '../../../../nls.js'; import { CancelablePromise, createCancelablePromise, Delayer } from '../../../../base/common/async.js'; import { IModelService } from '../../../../editor/common/services/model.js'; @@ -35,7 +35,7 @@ import { applyCodeAction, ApplyCodeActionReason, getCodeActions } from '../../.. import { CodeActionKind, CodeActionSet, CodeActionTriggerSource } from '../../../../editor/contrib/codeAction/common/types.js'; import { ITextModel } from '../../../../editor/common/model.js'; import { IEditorService, ACTIVE_GROUP } from '../../../services/editor/common/editorService.js'; -import { SeverityIcon } from '../../../../platform/severityIcon/browser/severityIcon.js'; +import { SeverityIcon } from '../../../../base/browser/ui/severityIcon/severityIcon.js'; import { CodeActionTriggerType } from '../../../../editor/common/languages.js'; import { IOpenerService } from '../../../../platform/opener/common/opener.js'; import { Progress } from '../../../../platform/progress/common/progress.js'; @@ -645,15 +645,14 @@ export class MarkerViewModel extends Disposable { } private toActions(codeActions: CodeActionSet): IAction[] { - return codeActions.validActions.map(item => new Action( - item.action.command ? item.action.command.id : item.action.title, - item.action.title, - undefined, - true, - () => { - return this.openFileAtMarker(this.marker) - .then(() => this.instantiationService.invokeFunction(applyCodeAction, item, ApplyCodeActionReason.FromProblemsView)); - })); + return codeActions.validActions.map(item => toAction({ + id: item.action.command ? item.action.command.id : item.action.title, + label: item.action.title, + run: async () => { + await this.openFileAtMarker(this.marker); + return await this.instantiationService.invokeFunction(applyCodeAction, item, ApplyCodeActionReason.FromProblemsView); + } + })); } private openFileAtMarker(element: Marker): Promise { diff --git a/src/vs/workbench/contrib/markers/browser/markersView.ts b/src/vs/workbench/contrib/markers/browser/markersView.ts index 85ad02f34be6..d62dbd05f116 100644 --- a/src/vs/workbench/contrib/markers/browser/markersView.ts +++ b/src/vs/workbench/contrib/markers/browser/markersView.ts @@ -5,57 +5,58 @@ import './media/markers.css'; -import { URI } from '../../../../base/common/uri.js'; import * as dom from '../../../../base/browser/dom.js'; +import { IKeyboardEvent, StandardKeyboardEvent } from '../../../../base/browser/keyboardEvent.js'; +import { ActionViewItem } from '../../../../base/browser/ui/actionbar/actionViewItems.js'; +import { IIdentityProvider, IListVirtualDelegate } from '../../../../base/browser/ui/list/list.js'; +import { ITableContextMenuEvent, ITableEvent } from '../../../../base/browser/ui/table/table.js'; +import { ITreeContextMenuEvent, ITreeElement, ITreeEvent, ITreeNode, ITreeRenderer } from '../../../../base/browser/ui/tree/tree.js'; import { IAction, Separator } from '../../../../base/common/actions.js'; -import { ITelemetryService } from '../../../../platform/telemetry/common/telemetry.js'; -import { IEditorService, SIDE_GROUP, ACTIVE_GROUP } from '../../../services/editor/common/editorService.js'; -import { Marker, ResourceMarkers, RelatedInformation, MarkerChangesEvent, MarkersModel, compareMarkersByUri, MarkerElement, MarkerTableItem } from './markersModel.js'; -import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; -import { MarkersFilters, IMarkersFiltersChangeEvent } from './markersViewActions.js'; -import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; -import Messages from './messages.js'; -import { RangeHighlightDecorations } from '../../../browser/codeeditor.js'; -import { IThemeService } from '../../../../platform/theme/common/themeService.js'; +import { groupBy } from '../../../../base/common/arrays.js'; +import { Event, Relay } from '../../../../base/common/event.js'; +import { IExpression } from '../../../../base/common/glob.js'; +import { Iterable } from '../../../../base/common/iterator.js'; +import { KeyCode } from '../../../../base/common/keyCodes.js'; +import { DisposableStore, IDisposable, toDisposable } from '../../../../base/common/lifecycle.js'; +import { ResourceMap } from '../../../../base/common/map.js'; +import { deepClone } from '../../../../base/common/objects.js'; +import { isDefined } from '../../../../base/common/types.js'; +import { URI } from '../../../../base/common/uri.js'; import { ICodeEditor } from '../../../../editor/browser/editorBrowser.js'; -import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; import { localize } from '../../../../nls.js'; +import { MenuId } from '../../../../platform/actions/common/actions.js'; +import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { IContextKey, IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; -import { Iterable } from '../../../../base/common/iterator.js'; -import { ITreeElement, ITreeNode, ITreeContextMenuEvent, ITreeRenderer, ITreeEvent } from '../../../../base/browser/ui/tree/tree.js'; -import { Relay, Event } from '../../../../base/common/event.js'; -import { WorkbenchObjectTree, IListService, IWorkbenchObjectTreeOptions, IOpenEvent } from '../../../../platform/list/browser/listService.js'; -import { FilterOptions } from './markersFilterOptions.js'; -import { IExpression } from '../../../../base/common/glob.js'; -import { deepClone } from '../../../../base/common/objects.js'; -import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; -import { FilterData, Filter, VirtualDelegate, ResourceMarkersRenderer, MarkerRenderer, RelatedInformationRenderer, MarkersWidgetAccessibilityProvider, MarkersViewModel } from './markersTreeViewer.js'; import { IContextMenuService } from '../../../../platform/contextview/browser/contextView.js'; -import { MenuId } from '../../../../platform/actions/common/actions.js'; +import { fillInMarkersDragData, MarkerTransferData } from '../../../../platform/dnd/browser/dnd.js'; +import { IHoverService } from '../../../../platform/hover/browser/hover.js'; +import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; -import { StandardKeyboardEvent, IKeyboardEvent } from '../../../../base/browser/keyboardEvent.js'; -import { ResourceLabels } from '../../../browser/labels.js'; +import { ResultKind } from '../../../../platform/keybinding/common/keybindingResolver.js'; +import { IListService, IOpenEvent, IWorkbenchObjectTreeOptions, WorkbenchObjectTree } from '../../../../platform/list/browser/listService.js'; import { IMarkerService, MarkerSeverity } from '../../../../platform/markers/common/markers.js'; -import { MementoObject, Memento } from '../../../common/memento.js'; -import { IIdentityProvider, IListVirtualDelegate } from '../../../../base/browser/ui/list/list.js'; -import { KeyCode } from '../../../../base/common/keyCodes.js'; -import { IViewPaneOptions, FilterViewPane } from '../../../browser/parts/views/viewPane.js'; -import { IViewDescriptorService } from '../../../common/views.js'; import { IOpenerService, withSelection } from '../../../../platform/opener/common/opener.js'; -import { ActionViewItem } from '../../../../base/browser/ui/actionbar/actionViewItems.js'; +import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; +import { IThemeService } from '../../../../platform/theme/common/themeService.js'; import { IUriIdentityService } from '../../../../platform/uriIdentity/common/uriIdentity.js'; -import { DisposableStore, IDisposable, toDisposable } from '../../../../base/common/lifecycle.js'; -import { groupBy } from '../../../../base/common/arrays.js'; -import { ResourceMap } from '../../../../base/common/map.js'; +import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; +import { registerNavigableContainer } from '../../../browser/actions/widgetNavigationCommands.js'; +import { RangeHighlightDecorations } from '../../../browser/codeeditor.js'; +import { ResourceListDnDHandler } from '../../../browser/dnd.js'; +import { ResourceLabels } from '../../../browser/labels.js'; +import { FilterViewPane, IViewPaneOptions } from '../../../browser/parts/views/viewPane.js'; import { EditorResourceAccessor, SideBySideEditor } from '../../../common/editor.js'; +import { Memento, MementoObject } from '../../../common/memento.js'; +import { IViewDescriptorService } from '../../../common/views.js'; +import { ACTIVE_GROUP, IEditorService, SIDE_GROUP } from '../../../services/editor/common/editorService.js'; +import { Markers, MarkersContextKeys, MarkersViewMode } from '../common/markers.js'; import { IMarkersView } from './markers.js'; -import { ResourceListDnDHandler } from '../../../browser/dnd.js'; -import { ITableContextMenuEvent, ITableEvent } from '../../../../base/browser/ui/table/table.js'; +import { FilterOptions } from './markersFilterOptions.js'; +import { compareMarkersByUri, Marker, MarkerChangesEvent, MarkerElement, MarkersModel, MarkerTableItem, RelatedInformation, ResourceMarkers } from './markersModel.js'; import { MarkersTable } from './markersTable.js'; -import { Markers, MarkersContextKeys, MarkersViewMode } from '../common/markers.js'; -import { registerNavigableContainer } from '../../../browser/actions/widgetNavigationCommands.js'; -import { IHoverService } from '../../../../platform/hover/browser/hover.js'; -import { ResultKind } from '../../../../platform/keybinding/common/keybindingResolver.js'; +import { Filter, FilterData, MarkerRenderer, MarkersViewModel, MarkersWidgetAccessibilityProvider, RelatedInformationRenderer, ResourceMarkersRenderer, VirtualDelegate } from './markersTreeViewer.js'; +import { IMarkersFiltersChangeEvent, MarkersFilters } from './markersViewActions.js'; +import Messages from './messages.js'; function createResourceMarkersIterator(resourceMarkers: ResourceMarkers): Iterable> { return Iterable.map(resourceMarkers.markers, m => { @@ -130,7 +131,6 @@ export class MarkersView extends FilterViewPane implements IMarkersView { @IViewDescriptorService viewDescriptorService: IViewDescriptorService, @IEditorService private readonly editorService: IEditorService, @IConfigurationService configurationService: IConfigurationService, - @ITelemetryService telemetryService: ITelemetryService, @IMarkerService private readonly markerService: IMarkerService, @IContextKeyService contextKeyService: IContextKeyService, @IWorkspaceContextService private readonly workspaceContextService: IWorkspaceContextService, @@ -153,7 +153,7 @@ export class MarkersView extends FilterViewPane implements IMarkersView { text: panelState['filter'] || '', history: panelState['filterHistory'] || [] } - }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService, hoverService); + }, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, hoverService); this.memento = memento; this.panelState = panelState; @@ -495,18 +495,7 @@ export class MarkersView extends FilterViewPane implements IMarkersView { filter: this.filter, accessibilityProvider: this.widgetAccessibilityProvider, identityProvider: this.widgetIdentityProvider, - dnd: this.instantiationService.createInstance(ResourceListDnDHandler, (element) => { - if (element instanceof ResourceMarkers) { - return element.resource; - } - if (element instanceof Marker) { - return withSelection(element.resource, element.range); - } - if (element instanceof RelatedInformation) { - return withSelection(element.raw.resource, element.raw); - } - return null; - }), + dnd: this.instantiationService.createInstance(MarkersListDnDHandler), expandOnlyOnTwistieClick: (e: MarkerElement) => e instanceof Marker && e.relatedInformation.length > 0, overrideStyles: this.getLocationBasedColors().listOverrideStyles, selectionNavigation: true, @@ -1078,3 +1067,40 @@ class MarkersTree extends WorkbenchObjectTree impleme super.layout(height, width); } } + +class MarkersListDnDHandler extends ResourceListDnDHandler { + constructor( + @IInstantiationService instantiationService: IInstantiationService + ) { + super(element => { + if (element instanceof MarkerTableItem) { + return withSelection(element.resource, element.range); + } else if (element instanceof ResourceMarkers) { + return element.resource; + } else if (element instanceof Marker) { + return withSelection(element.resource, element.range); + } else if (element instanceof RelatedInformation) { + return withSelection(element.raw.resource, element.raw); + } + return null; + }, instantiationService); + } + + protected override onWillDragElements(elements: (MarkerElement | MarkerTableItem)[], originalEvent: DragEvent) { + const data = elements.map((e): MarkerTransferData | undefined => { + if (e instanceof RelatedInformation || e instanceof Marker) { + return e.marker; + } + if (e instanceof ResourceMarkers) { + return { uri: e.resource }; + } + return undefined; + }).filter(isDefined); + + if (!data.length) { + return; + } + + fillInMarkersDragData(data, originalEvent); + } +} diff --git a/src/vs/workbench/contrib/mcp/browser/mcp.contribution.ts b/src/vs/workbench/contrib/mcp/browser/mcp.contribution.ts new file mode 100644 index 000000000000..f886d782765c --- /dev/null +++ b/src/vs/workbench/contrib/mcp/browser/mcp.contribution.ts @@ -0,0 +1,45 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { registerAction2 } from '../../../../platform/actions/common/actions.js'; +import { SyncDescriptor } from '../../../../platform/instantiation/common/descriptors.js'; +import { InstantiationType, registerSingleton } from '../../../../platform/instantiation/common/extensions.js'; +import * as jsonContributionRegistry from '../../../../platform/jsonschemas/common/jsonContributionRegistry.js'; +import { Registry } from '../../../../platform/registry/common/platform.js'; +import { registerWorkbenchContribution2, WorkbenchPhase } from '../../../common/contributions.js'; +import { mcpSchemaId } from '../../../services/configuration/common/configuration.js'; +import { ConfigMcpDiscovery } from '../common/discovery/configMcpDiscovery.js'; +import { ExtensionMcpDiscovery } from '../common/discovery/extensionMcpDiscovery.js'; +import { mcpDiscoveryRegistry } from '../common/discovery/mcpDiscovery.js'; +import { RemoteNativeMpcDiscovery } from '../common/discovery/nativeMcpRemoteDiscovery.js'; +import { mcpServerSchema } from '../common/mcpConfiguration.js'; +import { McpContextKeysController } from '../common/mcpContextKeys.js'; +import { McpRegistry } from '../common/mcpRegistry.js'; +import { IMcpRegistry } from '../common/mcpRegistryTypes.js'; +import { McpService } from '../common/mcpService.js'; +import { IMcpService } from '../common/mcpTypes.js'; +import { AddConfigurationAction, ListMcpServerCommand, MCPServerActionRendering, McpServerOptionsCommand, ResetMcpCachedTools, ResetMcpTrustCommand } from './mcpCommands.js'; +import { McpDiscovery } from './mcpDiscovery.js'; + +registerSingleton(IMcpRegistry, McpRegistry, InstantiationType.Delayed); +registerSingleton(IMcpService, McpService, InstantiationType.Delayed); + +mcpDiscoveryRegistry.register(new SyncDescriptor(RemoteNativeMpcDiscovery)); +mcpDiscoveryRegistry.register(new SyncDescriptor(ConfigMcpDiscovery)); +mcpDiscoveryRegistry.register(new SyncDescriptor(ExtensionMcpDiscovery)); + +registerWorkbenchContribution2('mcpDiscovery', McpDiscovery, WorkbenchPhase.AfterRestored); +registerWorkbenchContribution2('mcpContextKeys', McpContextKeysController, WorkbenchPhase.BlockRestore); + +registerAction2(ListMcpServerCommand); +registerAction2(McpServerOptionsCommand); +registerAction2(ResetMcpTrustCommand); +registerAction2(ResetMcpCachedTools); +registerAction2(AddConfigurationAction); + +registerWorkbenchContribution2('mcpActionRendering', MCPServerActionRendering, WorkbenchPhase.BlockRestore); + +const jsonRegistry = Registry.as(jsonContributionRegistry.Extensions.JSONContribution); +jsonRegistry.registerSchema(mcpSchemaId, mcpServerSchema); diff --git a/src/vs/workbench/contrib/mcp/browser/mcpCommands.ts b/src/vs/workbench/contrib/mcp/browser/mcpCommands.ts new file mode 100644 index 000000000000..7e45d1506a46 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/browser/mcpCommands.ts @@ -0,0 +1,389 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { h } from '../../../../base/browser/dom.js'; +import { Codicon } from '../../../../base/common/codicons.js'; +import { groupBy } from '../../../../base/common/collections.js'; +import { Event } from '../../../../base/common/event.js'; +import { Disposable, DisposableStore } from '../../../../base/common/lifecycle.js'; +import { autorun, derived } from '../../../../base/common/observable.js'; +import { ThemeIcon } from '../../../../base/common/themables.js'; +import { ILocalizedString, localize, localize2 } from '../../../../nls.js'; +import { IActionViewItemService } from '../../../../platform/actions/browser/actionViewItemService.js'; +import { MenuEntryActionViewItem } from '../../../../platform/actions/browser/menuEntryActionViewItem.js'; +import { Action2, MenuId, MenuItemAction } from '../../../../platform/actions/common/actions.js'; +import { ICommandService } from '../../../../platform/commands/common/commands.js'; +import { ContextKeyExpr } from '../../../../platform/contextkey/common/contextkey.js'; +import { IInstantiationService, ServicesAccessor } from '../../../../platform/instantiation/common/instantiation.js'; +import { IQuickInputService, IQuickPickItem } from '../../../../platform/quickinput/common/quickInput.js'; +import { spinningLoading } from '../../../../platform/theme/common/iconRegistry.js'; +import { ActiveEditorContext, ResourceContextKey } from '../../../common/contextkeys.js'; +import { IWorkbenchContribution } from '../../../common/contributions.js'; +import { ChatContextKeys } from '../../chat/common/chatContextKeys.js'; +import { ChatMode } from '../../chat/common/constants.js'; +import { TEXT_FILE_EDITOR_ID } from '../../files/common/files.js'; +import { McpContextKeys } from '../common/mcpContextKeys.js'; +import { IMcpRegistry } from '../common/mcpRegistryTypes.js'; +import { IMcpServer, IMcpService, LazyCollectionState, McpConnectionState, McpServerToolsState } from '../common/mcpTypes.js'; +import { McpAddConfigurationCommand } from './mcpCommandsAddConfiguration.js'; + +// acroynms do not get localized +const category: ILocalizedString = { + original: 'MCP', + value: 'MCP', +}; + +export class ListMcpServerCommand extends Action2 { + public static readonly id = 'workbench.mcp.listServer'; + constructor() { + super({ + id: ListMcpServerCommand.id, + title: localize2('mcp.list', 'List Servers'), + icon: Codicon.server, + category, + f1: true, + menu: { + when: ContextKeyExpr.and( + ContextKeyExpr.or(McpContextKeys.hasUnknownTools, McpContextKeys.hasServersWithErrors), + ChatContextKeys.chatMode.isEqualTo(ChatMode.Agent) + ), + id: MenuId.ChatInputAttachmentToolbar, + group: 'navigation', + order: 0 + }, + }); + } + + override async run(accessor: ServicesAccessor) { + const mcpService = accessor.get(IMcpService); + const commandService = accessor.get(ICommandService); + const quickInput = accessor.get(IQuickInputService); + + type ItemType = { id: string } & IQuickPickItem; + + const store = new DisposableStore(); + const pick = quickInput.createQuickPick({ useSeparators: true }); + pick.title = localize('mcp.selectServer', 'Select an MCP Server'); + + store.add(pick); + store.add(autorun(reader => { + const servers = groupBy(mcpService.servers.read(reader).slice().sort((a, b) => (a.collection.presentation?.order || 0) - (b.collection.presentation?.order || 0)), s => s.collection.id); + pick.items = Object.values(servers).flatMap(servers => { + return [ + { type: 'separator', label: servers[0].collection.label, id: servers[0].collection.id }, + ...servers.map(server => ({ + id: server.definition.id, + label: server.definition.label, + description: McpConnectionState.toString(server.connectionState.read(reader)), + })), + ]; + }); + })); + + + const picked = await new Promise(resolve => { + store.add(pick.onDidAccept(() => { + resolve(pick.activeItems[0]); + })); + store.add(pick.onDidHide(() => { + resolve(undefined); + })); + pick.show(); + }); + + store.dispose(); + + if (picked) { + commandService.executeCommand(McpServerOptionsCommand.id, picked.id); + } + } +} + + +export class McpServerOptionsCommand extends Action2 { + + static readonly id = 'workbench.mcp.serverOptions'; + + constructor() { + super({ + id: McpServerOptionsCommand.id, + title: localize2('mcp.options', 'Server Options'), + category, + f1: false, + }); + } + + override async run(accessor: ServicesAccessor, id: string): Promise { + const mcpService = accessor.get(IMcpService); + const quickInputService = accessor.get(IQuickInputService); + const server = mcpService.servers.get().find(s => s.definition.id === id); + if (!server) { + return; + } + + interface ActionItem extends IQuickPickItem { + action: 'start' | 'stop' | 'restart' | 'showOutput'; + } + + const items: ActionItem[] = []; + const serverState = server.connectionState.get(); + + // Only show start when server is stopped or in error state + if (McpConnectionState.canBeStarted(serverState.state)) { + items.push({ + label: localize2('mcp.start', 'Start Server').value, + action: 'start' + }); + } else { + items.push({ + label: localize2('mcp.stop', 'Stop Server').value, + action: 'stop' + }); + items.push({ + label: localize2('mcp.restart', 'Restart Server').value, + action: 'restart' + }); + } + + items.push({ + label: localize2('mcp.showOutput', 'Show Output').value, + action: 'showOutput' + }); + + const pick = await quickInputService.pick(items, { + title: server.definition.label, + placeHolder: localize('mcp.selectAction', 'Select Server Action') + }); + + if (!pick) { + return; + } + + switch (pick.action) { + case 'start': + await server.start(true); + server.showOutput(); + break; + case 'stop': + await server.stop(); + break; + case 'restart': + await server.stop(); + await server.start(true); + break; + case 'showOutput': + server.showOutput(); + break; + } + } +} + + +export class MCPServerActionRendering extends Disposable implements IWorkbenchContribution { + public static readonly ID = 'workbench.contrib.mcp.discovery'; + + constructor( + @IActionViewItemService actionViewItemService: IActionViewItemService, + @IMcpService mcpService: IMcpService, + @IInstantiationService instaService: IInstantiationService, + @ICommandService commandService: ICommandService, + ) { + super(); + + const enum DisplayedState { + None, + NewTools, + Error, + Refreshing, + } + + + + const displayedState = derived((reader) => { + const servers = mcpService.servers.read(reader); + const serversPerState: IMcpServer[][] = []; + for (const server of servers) { + let thisState = DisplayedState.None; + switch (server.toolsState.read(reader)) { + case McpServerToolsState.Unknown: + if (server.trusted.read(reader) === false) { + thisState = DisplayedState.None; + } else { + thisState = server.connectionState.read(reader).state === McpConnectionState.Kind.Error ? DisplayedState.Error : DisplayedState.NewTools; + } + break; + case McpServerToolsState.RefreshingFromUnknown: + thisState = DisplayedState.Refreshing; + break; + default: + thisState = server.connectionState.read(reader).state === McpConnectionState.Kind.Error ? DisplayedState.Error : DisplayedState.None; + break; + } + + serversPerState[thisState] ??= []; + serversPerState[thisState].push(server); + } + + const unknownServerStates = mcpService.lazyCollectionState.read(reader); + if (unknownServerStates === LazyCollectionState.LoadingUnknown) { + serversPerState[DisplayedState.Refreshing] ??= []; + } else if (unknownServerStates === LazyCollectionState.HasUnknown) { + serversPerState[DisplayedState.NewTools] ??= []; + } + + const maxState = (serversPerState.length - 1) as DisplayedState; + return { state: maxState, servers: serversPerState[maxState] || [] }; + }); + + this._store.add(actionViewItemService.register(MenuId.ChatInputAttachmentToolbar, ListMcpServerCommand.id, (action, options) => { + if (!(action instanceof MenuItemAction)) { + return undefined; + } + + return instaService.createInstance(class extends MenuEntryActionViewItem { + + override render(container: HTMLElement): void { + + super.render(container); + container.classList.add('chat-mcp'); + + const action = h('button.chat-mcp-action', [h('span@icon')]); + + this._register(autorun(r => { + const { state } = displayedState.read(r); + const { root, icon } = action; + this.updateTooltip(); + container.classList.toggle('chat-mcp-has-action', state !== DisplayedState.None); + + if (!root.parentElement) { + container.appendChild(root); + } + + root.ariaLabel = this.getLabelForState(displayedState.read(r)); + root.className = 'chat-mcp-action'; + icon.className = ''; + if (state === DisplayedState.NewTools) { + root.classList.add('chat-mcp-action-new'); + icon.classList.add(...ThemeIcon.asClassNameArray(Codicon.refresh)); + } else if (state === DisplayedState.Error) { + root.classList.add('chat-mcp-action-error'); + icon.classList.add(...ThemeIcon.asClassNameArray(Codicon.warning)); + } else if (state === DisplayedState.Refreshing) { + root.classList.add('chat-mcp-action-refreshing'); + icon.classList.add(...ThemeIcon.asClassNameArray(spinningLoading)); + } else { + root.remove(); + } + })); + } + + override async onClick(e: MouseEvent): Promise { + e.preventDefault(); + e.stopPropagation(); + + const { state, servers } = displayedState.get(); + if (state === DisplayedState.NewTools) { + servers.forEach(server => server.start()); + mcpService.activateCollections(); + } else if (state === DisplayedState.Refreshing) { + servers.at(-1)?.showOutput(); + } else if (state === DisplayedState.Error) { + const server = servers.at(-1); + if (server) { + commandService.executeCommand(McpServerOptionsCommand.id, server.definition.id); + } + } else { + commandService.executeCommand(ListMcpServerCommand.id); + } + } + + protected override getTooltip(): string { + return this.getLabelForState() || super.getTooltip(); + } + + private getLabelForState({ state, servers } = displayedState.get()) { + if (state === DisplayedState.NewTools) { + return localize('mcp.newTools', "New tools available ({0})", servers.length || 1); + } else if (state === DisplayedState.Error) { + return localize('mcp.toolError', "Error loading {0} tool(s)", servers.length || 1); + } else if (state === DisplayedState.Refreshing) { + return localize('mcp.toolRefresh', "Discovering tools..."); + } else { + return null; + } + } + + + }, action, { ...options, keybindingNotRenderedWithLabel: true }); + + }, Event.fromObservable(displayedState))); + } +} + +export class ResetMcpTrustCommand extends Action2 { + static readonly ID = 'workbench.mcp.resetTrust'; + + constructor() { + super({ + id: ResetMcpTrustCommand.ID, + title: localize2('mcp.resetTrust', "Reset Trust"), + category, + f1: true, + precondition: McpContextKeys.toolsCount.greater(0), + }); + } + + run(accessor: ServicesAccessor): void { + const mcpService = accessor.get(IMcpRegistry); + mcpService.resetTrust(); + } +} + + +export class ResetMcpCachedTools extends Action2 { + static readonly ID = 'workbench.mcp.resetCachedTools'; + + constructor() { + super({ + id: ResetMcpCachedTools.ID, + title: localize2('mcp.resetCachedTools', "Reset Cached Tools"), + category, + f1: true, + precondition: McpContextKeys.toolsCount.greater(0), + }); + } + + run(accessor: ServicesAccessor): void { + const mcpService = accessor.get(IMcpService); + mcpService.resetCaches(); + } +} + +export class AddConfigurationAction extends Action2 { + static readonly ID = 'workbench.mcp.addConfiguration'; + + constructor() { + super({ + id: AddConfigurationAction.ID, + title: localize2('mcp.addConfiguration', "Add Server..."), + metadata: { + description: localize2('mcp.addConfiguration.description', "Installs a new Model Context protocol to the mcp.json settings"), + }, + category, + f1: true, + menu: { + id: MenuId.EditorContent, + when: ContextKeyExpr.and( + ContextKeyExpr.regex(ResourceContextKey.Path.key, /\.vscode[/\\]mcp\.json$/), + ActiveEditorContext.isEqualTo(TEXT_FILE_EDITOR_ID) + ) + } + }); + } + + async run(accessor: ServicesAccessor, configUri?: string): Promise { + return accessor.get(IInstantiationService).createInstance(McpAddConfigurationCommand, configUri).run(); + } +} diff --git a/src/vs/workbench/contrib/mcp/browser/mcpCommandsAddConfiguration.ts b/src/vs/workbench/contrib/mcp/browser/mcpCommandsAddConfiguration.ts new file mode 100644 index 000000000000..cdfb4803143e --- /dev/null +++ b/src/vs/workbench/contrib/mcp/browser/mcpCommandsAddConfiguration.ts @@ -0,0 +1,315 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { assertNever } from '../../../../base/common/assert.js'; +import { DisposableStore } from '../../../../base/common/lifecycle.js'; +import { URI } from '../../../../base/common/uri.js'; +import { generateUuid } from '../../../../base/common/uuid.js'; +import { localize } from '../../../../nls.js'; +import { ICommandService } from '../../../../platform/commands/common/commands.js'; +import { ConfigurationTarget, getConfigValueInTarget, IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; +import { IMcpConfiguration, IMcpConfigurationSSE, McpConfigurationServer } from '../../../../platform/mcp/common/mcpPlatformTypes.js'; +import { IQuickInputService, IQuickPickItem, QuickPickInput } from '../../../../platform/quickinput/common/quickInput.js'; +import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; +import { IJSONEditingService } from '../../../services/configuration/common/jsonEditing.js'; +import { IWorkbenchEnvironmentService } from '../../../services/environment/common/environmentService.js'; +import { IMcpConfigurationStdio, mcpStdioServerSchema } from '../common/mcpConfiguration.js'; + + +const enum AddConfigurationType { + Stdio, + SSE, + + NpmPackage, + PipPackage, +} + +const enum AddConfigurationCopilotCommand { + /** Returns whether MCP enhanced setup is enabled. */ + IsSupported = 'github.copilot.chat.mcp.setup.check', + + /** Takes an npm/pip package name, validates its owner. */ + ValidatePackage = 'github.copilot.chat.mcp.setup.validatePackage', + + /** Returns the resolved MCP configuration. */ + StartFlow = 'github.copilot.chat.mcp.setup.flow', +} + +type ValidatePackageResult = { state: 'ok'; publisher: string } | { state: 'error'; error: string }; + +export class McpAddConfigurationCommand { + constructor( + private readonly _explicitConfigUri: string | undefined, + @IQuickInputService private readonly _quickInputService: IQuickInputService, + @IConfigurationService private readonly _configurationService: IConfigurationService, + @IJSONEditingService private readonly _jsonEditingService: IJSONEditingService, + @IWorkspaceContextService private readonly _workspaceService: IWorkspaceContextService, + @IWorkbenchEnvironmentService private readonly _environmentService: IWorkbenchEnvironmentService, + @ICommandService private readonly _commandService: ICommandService, + ) { } + + private async getServerType(): Promise { + const items: QuickPickInput<{ kind: AddConfigurationType } & IQuickPickItem>[] = [ + { kind: AddConfigurationType.Stdio, label: localize('mcp.serverType.command', "Command (stdio)"), description: localize('mcp.serverType.command.description', "Run a local command that implements the MCP protocol") }, + { kind: AddConfigurationType.SSE, label: localize('mcp.serverType.http', "HTTP (server-sent events)"), description: localize('mcp.serverType.http.description', "Connect to a remote HTTP server that implements the MCP protocol") } + ]; + + let aiSupported: boolean | undefined; + try { + aiSupported = await this._commandService.executeCommand(AddConfigurationCopilotCommand.IsSupported); + } catch { + // ignored + } + + if (aiSupported) { + items.unshift({ type: 'separator', label: localize('mcp.serverType.manual', "Manual Install") }); + items.push( + { type: 'separator', label: localize('mcp.serverType.copilot', "Model-Assisted") }, + { kind: AddConfigurationType.NpmPackage, label: localize('mcp.serverType.npm', "NPM Package"), description: localize('mcp.serverType.npm.description', "Install from an NPM package name") }, + { kind: AddConfigurationType.PipPackage, label: localize('mcp.serverType.pip', "PIP Package"), description: localize('mcp.serverType.pip.description', "Install from a PIP package name") } + ); + } + + const result = await this._quickInputService.pick<{ kind: AddConfigurationType } & IQuickPickItem>(items, { + title: localize('mcp.serverType.title', "Select Server Type"), + placeHolder: localize('mcp.serverType.placeholder', "Choose the type of MCP server to add"), + }); + + return result?.kind; + } + + private async getStdioConfig(): Promise { + const command = await this._quickInputService.input({ + title: localize('mcp.command.title', "Enter Command"), + placeHolder: localize('mcp.command.placeholder', "Command to run (with optional arguments)"), + ignoreFocusLost: true, + }); + + if (!command) { + return undefined; + } + + // Split command into command and args, handling quotes + const parts = command.match(/(?:[^\s"]+|"[^"]*")+/g)!; + return { + type: 'stdio', + command: parts[0].replace(/"/g, ''), + + args: parts.slice(1).map(arg => arg.replace(/"/g, '')) + }; + } + + private async getSSEConfig(): Promise { + const url = await this._quickInputService.input({ + title: localize('mcp.url.title', "Enter Server URL"), + placeHolder: localize('mcp.url.placeholder', "URL of the MCP server (e.g., http://localhost:3000)"), + ignoreFocusLost: true, + }); + + if (!url) { + return undefined; + } + + return { + type: 'sse', + url + }; + } + + private async getServerId(suggestion = `my-mcp-server-${generateUuid().split('-')[0]}`): Promise { + const id = await this._quickInputService.input({ + title: localize('mcp.serverId.title', "Enter Server ID"), + placeHolder: localize('mcp.serverId.placeholder', "Unique identifier for this server"), + value: suggestion, + ignoreFocusLost: true, + }); + + return id; + } + + private async getConfigurationTarget(): Promise { + const options: (IQuickPickItem & { target: ConfigurationTarget })[] = [ + { target: ConfigurationTarget.USER, label: localize('mcp.target.user', "User Settings"), description: localize('mcp.target.user.description', "Available in all workspaces") } + ]; + + if (!!this._environmentService.remoteAuthority) { + options.push({ target: ConfigurationTarget.USER_REMOTE, label: localize('mcp.target.remote', "Remote Settings"), description: localize('mcp.target..remote.description', "Available on this remote machine") }); + } + + if (this._workspaceService.getWorkspace().folders.length > 0) { + options.push({ target: ConfigurationTarget.WORKSPACE, label: localize('mcp.target.workspace', "Workspace Settings"), description: localize('mcp.target.workspace.description', "Available in this workspace") }); + } + + if (options.length === 1) { + return options[0].target; + } + + + const targetPick = await this._quickInputService.pick(options, { + title: localize('mcp.target.title', "Choose where to save the configuration"), + }); + + return targetPick?.target; + } + + private async getAssistedConfig(type: AddConfigurationType): Promise<{ name: string; config: McpConfigurationServer } | undefined> { + const packageName = await this._quickInputService.input({ + ignoreFocusLost: true, + title: type === AddConfigurationType.NpmPackage + ? localize('mcp.npm.title', "Enter NPM Package Name") + : localize('mcp.pip.title', "Enter Pip Package Name"), + placeHolder: type === AddConfigurationType.NpmPackage + ? localize('mcp.npm.placeholder', "Package name (e.g., @org/package)") + : localize('mcp.pip.placeholder', "Package name (e.g., package-name)") + }); + + if (!packageName) { + return undefined; + } + + const enum LoadAction { + Retry = 'retry', + Cancel = 'cancel', + Allow = 'allow' + } + + const loadingQuickPickStore = new DisposableStore(); + const loadingQuickPick = loadingQuickPickStore.add(this._quickInputService.createQuickPick()); + loadingQuickPick.title = localize('mcp.loading.title', "Loading package details..."); + loadingQuickPick.busy = true; + loadingQuickPick.ignoreFocusOut = true; + + this._commandService.executeCommand( + AddConfigurationCopilotCommand.ValidatePackage, + { + type: type === AddConfigurationType.NpmPackage ? 'npm' : 'pip', + name: packageName, + targetConfig: { + ...mcpStdioServerSchema, + properties: { + ...mcpStdioServerSchema.properties, + name: { + type: 'string', + description: 'Suggested name of the server, alphanumeric and hyphen only', + } + }, + required: [...(mcpStdioServerSchema.required || []), 'name'], + }, + } + ).then(result => { + if (!result || result.state === 'error') { + loadingQuickPick.title = result?.error || 'Unknown error loading package'; + loadingQuickPick.items = [{ id: LoadAction.Retry, label: localize('mcp.error.retry', 'Try a different package') }, { id: LoadAction.Cancel, label: localize('cancel', 'Cancel') }]; + } else { + loadingQuickPick.title = localize('mcp.confirmPublish', 'Install {0} from {1}?', packageName, result.publisher); + loadingQuickPick.items = [ + { id: LoadAction.Allow, label: localize('allow', "Allow") }, + { id: LoadAction.Cancel, label: localize('cancel', 'Cancel') } + ]; + } + loadingQuickPick.busy = false; + }); + + const loadingAction = await new Promise(resolve => { + loadingQuickPick.onDidAccept(() => resolve(loadingQuickPick.selectedItems[0]?.id)); + loadingQuickPick.onDidHide(() => resolve(undefined)); + loadingQuickPick.show(); + }).finally(() => loadingQuickPick.dispose()); + + switch (loadingAction) { + case LoadAction.Retry: + return this.getAssistedConfig(type); + case LoadAction.Allow: + break; + case LoadAction.Cancel: + default: + return undefined; + } + + const configWithName = await this._commandService.executeCommand( + AddConfigurationCopilotCommand.StartFlow, + { name: packageName } + ); + + if (!configWithName) { + return undefined; + } + + const { name, ...config } = configWithName; + return { name, config }; + } + + public async run(): Promise { + // Step 1: Choose server type + const serverType = await this.getServerType(); + if (serverType === undefined) { + return; + } + + // Step 2: Get server details based on type + let serverConfig: McpConfigurationServer | undefined; + let suggestedName: string | undefined; + switch (serverType) { + case AddConfigurationType.Stdio: + serverConfig = await this.getStdioConfig(); + break; + case AddConfigurationType.SSE: + serverConfig = await this.getSSEConfig(); + break; + case AddConfigurationType.NpmPackage: { + const r = await this.getAssistedConfig(AddConfigurationType.NpmPackage); + serverConfig = r?.config; + suggestedName = r?.name; + break; + } + case AddConfigurationType.PipPackage: { + const r = await this.getAssistedConfig(AddConfigurationType.PipPackage); + serverConfig = r?.config; + suggestedName = r?.name; + break; + } + default: + assertNever(serverType); + } + + if (!serverConfig) { + return; + } + + // Step 3: Get server ID + const serverId = await this.getServerId(suggestedName); + if (!serverId) { + return; + } + + // Step 4: Choose configuration target if no configUri provided + let target: ConfigurationTarget | undefined; + const workspace = this._workspaceService.getWorkspace(); + if (!this._explicitConfigUri) { + target = await this.getConfigurationTarget(); + if (!target) { + return; + } + } + + // Step 5: Update configuration + const writeToUriDirect = this._explicitConfigUri + ? URI.parse(this._explicitConfigUri) + : target === ConfigurationTarget.WORKSPACE && workspace.folders.length === 1 + ? URI.joinPath(workspace.folders[0].uri, '.vscode', 'mcp.json') + : undefined; + + if (writeToUriDirect) { + await this._jsonEditingService.write(writeToUriDirect, [{ + path: ['servers', serverId], + value: serverConfig + }], true); + } else { + const settings: IMcpConfiguration = { ...getConfigValueInTarget(this._configurationService.inspect('mcp'), target!) }; + settings.servers = { ...settings.servers, [serverId]: serverConfig }; + await this._configurationService.updateValue('mcp', settings, target!); + } + } +} diff --git a/src/vs/workbench/contrib/mcp/browser/mcpDiscovery.ts b/src/vs/workbench/contrib/mcp/browser/mcpDiscovery.ts new file mode 100644 index 000000000000..f9d339b79626 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/browser/mcpDiscovery.ts @@ -0,0 +1,23 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable } from '../../../../base/common/lifecycle.js'; +import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; +import { IWorkbenchContribution } from '../../../common/contributions.js'; +import { mcpDiscoveryRegistry } from '../common/discovery/mcpDiscovery.js'; + +export class McpDiscovery extends Disposable implements IWorkbenchContribution { + public static readonly ID = 'workbench.contrib.mcp.discovery'; + + constructor( + @IInstantiationService instantiationService: IInstantiationService, + ) { + super(); + for (const discovery of mcpDiscoveryRegistry.getAll()) { + const inst = this._register(instantiationService.createInstance(discovery)); + inst.start(); + } + } +} diff --git a/src/vs/workbench/contrib/mcp/common/discovery/configMcpDiscovery.ts b/src/vs/workbench/contrib/mcp/common/discovery/configMcpDiscovery.ts new file mode 100644 index 000000000000..04931acfe3d7 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/discovery/configMcpDiscovery.ts @@ -0,0 +1,145 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { equals as arrayEquals } from '../../../../../base/common/arrays.js'; +import { Disposable, IDisposable, MutableDisposable } from '../../../../../base/common/lifecycle.js'; +import { Schemas } from '../../../../../base/common/network.js'; +import { ISettableObservable, observableValue } from '../../../../../base/common/observable.js'; +import { URI } from '../../../../../base/common/uri.js'; +import { localize } from '../../../../../nls.js'; +import { ConfigurationTarget, IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; +import { ILabelService } from '../../../../../platform/label/common/label.js'; +import { IProductService } from '../../../../../platform/product/common/productService.js'; +import { StorageScope } from '../../../../../platform/storage/common/storage.js'; +import { IWorkbenchEnvironmentService } from '../../../../services/environment/common/environmentService.js'; +import { IMcpConfiguration, mcpConfigurationSection } from '../mcpConfiguration.js'; +import { IMcpRegistry } from '../mcpRegistryTypes.js'; +import { McpCollectionSortOrder, McpServerDefinition, McpServerTransportType } from '../mcpTypes.js'; +import { IMcpDiscovery } from './mcpDiscovery.js'; + + +/** + * Discovers MCP servers based on various config sources. + */ +export class ConfigMcpDiscovery extends Disposable implements IMcpDiscovery { + private readonly configSources: { + key: 'userLocalValue' | 'userRemoteValue' | 'workspaceValue'; + label: string; + serverDefinitions: ISettableObservable; + scope: StorageScope; + target: ConfigurationTarget; + disposable: MutableDisposable; + order: number; + remoteAuthority?: string; + }[]; + + constructor( + @IConfigurationService private readonly _configurationService: IConfigurationService, + @IMcpRegistry private readonly _mcpRegistry: IMcpRegistry, + @IProductService productService: IProductService, + @ILabelService labelService: ILabelService, + @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService, + ) { + super(); + const remoteLabel = environmentService.remoteAuthority ? labelService.getHostLabel(Schemas.vscodeRemote, environmentService.remoteAuthority) : 'Remote'; + this.configSources = [ + { + key: 'userLocalValue', + target: ConfigurationTarget.USER_LOCAL, + label: localize('mcp.configuration.userLocalValue', 'Global in {0}', productService.nameShort), + serverDefinitions: observableValue(this, []), + scope: StorageScope.PROFILE, + disposable: this._register(new MutableDisposable()), + order: McpCollectionSortOrder.User, + }, + { + key: 'userRemoteValue', + target: ConfigurationTarget.USER_REMOTE, + label: localize('mcp.configuration.userRemoteValue', 'From {0}', remoteLabel), + serverDefinitions: observableValue(this, []), + scope: StorageScope.PROFILE, + disposable: this._register(new MutableDisposable()), + remoteAuthority: environmentService.remoteAuthority, + order: McpCollectionSortOrder.User + McpCollectionSortOrder.RemotePenalty, + }, + { + key: 'workspaceValue', + target: ConfigurationTarget.WORKSPACE, + label: localize('mcp.configuration.workspaceValue', 'From your workspace'), + serverDefinitions: observableValue(this, []), + scope: StorageScope.WORKSPACE, + disposable: this._register(new MutableDisposable()), + order: McpCollectionSortOrder.Workspace, + + }, + ]; + } + + public start() { + this._register(this._configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(mcpConfigurationSection)) { + this.sync(); + } + })); + + this.sync(); + } + + private sync() { + const configurationKey = this._configurationService.inspect(mcpConfigurationSection); + + for (const src of this.configSources) { + const collectionId = `mcp.config.${src.key}`; + let value = configurationKey[src.key]; + + // If we see there are MCP servers, migrate them automatically + if (value?.mcpServers) { + value = { ...value, servers: { ...value.servers, ...value.mcpServers }, mcpServers: undefined }; + this._configurationService.updateValue(mcpConfigurationSection, value, {}, src.target, { donotNotifyError: true }); + } + + const nextDefinitions = Object.entries(value?.servers || {}).map(([name, value]): McpServerDefinition => ({ + id: `${collectionId}.${name}`, + label: name, + launch: 'type' in value && value.type === 'sse' ? { + type: McpServerTransportType.SSE, + uri: URI.parse(value.url), + headers: Object.entries(value.headers || {}), + } : { + type: McpServerTransportType.Stdio, + args: value.args || [], + command: value.command, + env: value.env || {}, + cwd: undefined, + }, + variableReplacement: { + section: mcpConfigurationSection, + target: src.target, + } + })); + + if (arrayEquals(nextDefinitions, src.serverDefinitions.get(), McpServerDefinition.equals)) { + continue; + } + + + if (!nextDefinitions.length) { + src.disposable.clear(); + src.serverDefinitions.set(nextDefinitions, undefined); + } else { + src.serverDefinitions.set(nextDefinitions, undefined); + src.disposable.value ??= this._mcpRegistry.registerCollection({ + id: collectionId, + label: src.label, + presentation: { order: src.order }, + remoteAuthority: src.remoteAuthority || null, + serverDefinitions: src.serverDefinitions, + isTrustedByDefault: true, + scope: src.scope, + }); + } + } + } +} diff --git a/src/vs/workbench/contrib/mcp/common/discovery/extensionMcpDiscovery.ts b/src/vs/workbench/contrib/mcp/common/discovery/extensionMcpDiscovery.ts new file mode 100644 index 000000000000..75cbd2081f6d --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/discovery/extensionMcpDiscovery.ts @@ -0,0 +1,99 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Disposable, DisposableMap } from '../../../../../base/common/lifecycle.js'; +import { observableValue } from '../../../../../base/common/observable.js'; +import { IStorageService, StorageScope, StorageTarget } from '../../../../../platform/storage/common/storage.js'; +import { IExtensionService } from '../../../../services/extensions/common/extensions.js'; +import * as extensionsRegistry from '../../../../services/extensions/common/extensionsRegistry.js'; +import { mcpActivationEvent, mcpContributionPoint } from '../mcpConfiguration.js'; +import { IMcpRegistry } from '../mcpRegistryTypes.js'; +import { extensionPrefixedIdentifier, McpServerDefinition } from '../mcpTypes.js'; +import { IMcpDiscovery } from './mcpDiscovery.js'; + +const cacheKey = 'mcp.extCachedServers'; + +interface IServerCacheEntry { + readonly servers: readonly McpServerDefinition.Serialized[]; +} + +const _mcpExtensionPoint = extensionsRegistry.ExtensionsRegistry.registerExtensionPoint(mcpContributionPoint); + +export class ExtensionMcpDiscovery extends Disposable implements IMcpDiscovery { + private readonly _extensionCollectionIdsToPersist = new Set(); + private readonly cachedServers: { [collcetionId: string]: IServerCacheEntry }; + + constructor( + @IMcpRegistry private readonly _mcpRegistry: IMcpRegistry, + @IStorageService storageService: IStorageService, + @IExtensionService private readonly _extensionService: IExtensionService, + ) { + super(); + this.cachedServers = storageService.getObject(cacheKey, StorageScope.WORKSPACE, {}); + + this._register(storageService.onWillSaveState(() => { + let updated = false; + for (const collectionId of this._extensionCollectionIdsToPersist) { + const collection = this._mcpRegistry.collections.get().find(c => c.id === collectionId); + if (!collection || collection.lazy) { + continue; + } + + const defs = collection.serverDefinitions.get(); + if (defs) { + updated = true; + this.cachedServers[collectionId] = { servers: defs.map(McpServerDefinition.toSerialized) }; + } + } + + if (updated) { + storageService.store(cacheKey, this.cachedServers, StorageScope.WORKSPACE, StorageTarget.MACHINE); + } + })); + } + + public start(): void { + const extensionCollections = this._register(new DisposableMap()); + this._register(_mcpExtensionPoint.setHandler((_extensions, delta) => { + const { added, removed } = delta; + + for (const collections of removed) { + for (const coll of collections.value) { + extensionCollections.deleteAndDispose(extensionPrefixedIdentifier(collections.description.identifier, coll.id)); + } + } + + for (const collections of added) { + for (const coll of collections.value) { + const id = extensionPrefixedIdentifier(collections.description.identifier, coll.id); + this._extensionCollectionIdsToPersist.add(id); + + const serverDefs = this.cachedServers.hasOwnProperty(id) ? this.cachedServers[id].servers : undefined; + const dispo = this._mcpRegistry.registerCollection({ + id, + label: coll.label, + remoteAuthority: null, + isTrustedByDefault: true, + scope: StorageScope.WORKSPACE, + serverDefinitions: observableValue(this, serverDefs?.map(McpServerDefinition.fromSerialized) || []), + lazy: { + isCached: !!serverDefs, + load: () => this._activateExtensionServers(coll.id), + removed: () => extensionCollections.deleteAndDispose(id), + } + }); + + extensionCollections.set(id, dispo); + } + } + })); + } + + private async _activateExtensionServers(collectionId: string): Promise { + await this._extensionService.activateByEvent(mcpActivationEvent(collectionId)); + await Promise.all(this._mcpRegistry.delegates + .map(r => r.waitForInitialProviderPromises())); + } +} diff --git a/src/vs/workbench/contrib/mcp/common/discovery/mcpDiscovery.ts b/src/vs/workbench/contrib/mcp/common/discovery/mcpDiscovery.ts new file mode 100644 index 000000000000..a6a091cfaa7d --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/discovery/mcpDiscovery.ts @@ -0,0 +1,29 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IDisposable } from '../../../../../base/common/lifecycle.js'; +import { SyncDescriptor0 } from '../../../../../platform/instantiation/common/descriptors.js'; + + +export interface IMcpDiscovery extends IDisposable { + start(): void; +} + +class McpDiscoveryRegistry { + private readonly _discovery: SyncDescriptor0[] = []; + + register(discovery: SyncDescriptor0): void { + this._discovery.push(discovery); + } + + getAll(): readonly SyncDescriptor0[] { + return this._discovery; + } +} + +export const mcpDiscoveryRegistry = new McpDiscoveryRegistry(); + + + diff --git a/src/vs/workbench/contrib/mcp/common/discovery/nativeMcpDiscoveryAbstract.ts b/src/vs/workbench/contrib/mcp/common/discovery/nativeMcpDiscoveryAbstract.ts new file mode 100644 index 000000000000..803439a8b83d --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/discovery/nativeMcpDiscoveryAbstract.ts @@ -0,0 +1,120 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { RunOnceScheduler } from '../../../../../base/common/async.js'; +import { Disposable, MutableDisposable } from '../../../../../base/common/lifecycle.js'; +import { Schemas } from '../../../../../base/common/network.js'; +import { autorunWithStore, IObservable, observableValue } from '../../../../../base/common/observable.js'; +import { URI } from '../../../../../base/common/uri.js'; +import { localize } from '../../../../../nls.js'; +import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; +import { IFileService } from '../../../../../platform/files/common/files.js'; +import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; +import { ILabelService } from '../../../../../platform/label/common/label.js'; +import { INativeMcpDiscoveryData } from '../../../../../platform/mcp/common/nativeMcpDiscoveryHelper.js'; +import { observableConfigValue } from '../../../../../platform/observable/common/platformObservableUtils.js'; +import { StorageScope } from '../../../../../platform/storage/common/storage.js'; +import { Dto } from '../../../../services/extensions/common/proxyIdentifier.js'; +import { mcpDiscoverySection } from '../mcpConfiguration.js'; +import { IMcpRegistry } from '../mcpRegistryTypes.js'; +import { McpCollectionDefinition, McpCollectionSortOrder, McpServerDefinition } from '../mcpTypes.js'; +import { IMcpDiscovery } from './mcpDiscovery.js'; +import { ClaudeDesktopMpcDiscoveryAdapter, NativeMpcDiscoveryAdapter } from './nativeMcpDiscoveryAdapters.js'; + +/** + * Base class that discovers MCP servers on a filesystem, outside of the ones + * defined in VS Code settings. + */ +export abstract class FilesystemMpcDiscovery extends Disposable implements IMcpDiscovery { + private readonly adapters: readonly NativeMpcDiscoveryAdapter[]; + private _fsDiscoveryEnabled: IObservable; + private suffix = ''; + + constructor( + remoteAuthority: string | null, + @ILabelService labelService: ILabelService, + @IFileService private readonly fileService: IFileService, + @IInstantiationService instantiationService: IInstantiationService, + @IMcpRegistry private readonly mcpRegistry: IMcpRegistry, + @IConfigurationService configurationService: IConfigurationService, + ) { + super(); + if (remoteAuthority) { + this.suffix = ' ' + localize('onRemoteLabel', ' on {0}', labelService.getHostLabel(Schemas.vscodeRemote, remoteAuthority)); + } + + this._fsDiscoveryEnabled = observableConfigValue(mcpDiscoverySection, false, configurationService); + + this.adapters = [ + instantiationService.createInstance(ClaudeDesktopMpcDiscoveryAdapter, remoteAuthority) + ]; + } + + public abstract start(): void; + + protected setDetails(detailsDto: Dto | undefined) { + if (!detailsDto) { + return; + } + + const details: INativeMcpDiscoveryData = { + ...detailsDto, + homedir: URI.revive(detailsDto.homedir), + xdgHome: detailsDto.xdgHome ? URI.revive(detailsDto.xdgHome) : undefined, + winAppData: detailsDto.winAppData ? URI.revive(detailsDto.winAppData) : undefined, + }; + + for (const adapter of this.adapters) { + const file = adapter.getFilePath(details); + if (!file) { + continue; + } + + const collection = { + id: adapter.id, + label: adapter.label + this.suffix, + remoteAuthority: adapter.remoteAuthority, + scope: StorageScope.PROFILE, + isTrustedByDefault: false, + serverDefinitions: observableValue(this, []), + presentation: { + origin: file, + order: adapter.order + (adapter.remoteAuthority ? McpCollectionSortOrder.RemotePenalty : 0), + }, + } satisfies McpCollectionDefinition; + + const collectionRegistration = this._register(new MutableDisposable()); + const updateFile = async () => { + let definitions: McpServerDefinition[] = []; + try { + const contents = await this.fileService.readFile(file); + definitions = adapter.adaptFile(contents.value, details) || []; + } catch { + // ignored + } + if (!definitions.length) { + collectionRegistration.clear(); + } else { + collection.serverDefinitions.set(definitions, undefined); + if (!collectionRegistration.value) { + collectionRegistration.value = this.mcpRegistry.registerCollection(collection); + } + } + }; + + this._register(autorunWithStore((reader, store) => { + if (!this._fsDiscoveryEnabled.read(reader)) { + collectionRegistration.clear(); + return; + } + + const throttler = store.add(new RunOnceScheduler(updateFile, 500)); + const watcher = store.add(this.fileService.createWatcher(file, { recursive: false, excludes: [] })); + store.add(watcher.onDidChange(() => throttler.schedule())); + updateFile(); + })); + } + } +} diff --git a/src/vs/workbench/contrib/mcp/common/discovery/nativeMcpDiscoveryAdapters.ts b/src/vs/workbench/contrib/mcp/common/discovery/nativeMcpDiscoveryAdapters.ts new file mode 100644 index 000000000000..8eb01b9c7227 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/discovery/nativeMcpDiscoveryAdapters.ts @@ -0,0 +1,71 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { VSBuffer } from '../../../../../base/common/buffer.js'; +import { Platform } from '../../../../../base/common/platform.js'; +import { URI } from '../../../../../base/common/uri.js'; +import { INativeMcpDiscoveryData } from '../../../../../platform/mcp/common/nativeMcpDiscoveryHelper.js'; +import { McpCollectionSortOrder, McpServerDefinition, McpServerTransportType } from '../mcpTypes.js'; + +export interface NativeMpcDiscoveryAdapter { + readonly remoteAuthority: string | null; + readonly id: string; + readonly label: string; + readonly order: number; + + getFilePath(details: INativeMcpDiscoveryData): URI | undefined; + adaptFile(contents: VSBuffer, details: INativeMcpDiscoveryData): McpServerDefinition[] | undefined; +} + +export class ClaudeDesktopMpcDiscoveryAdapter implements NativeMpcDiscoveryAdapter { + public readonly id: string; + public readonly label: string = 'Claude Desktop'; + public readonly order = McpCollectionSortOrder.Filesystem; + + constructor(public readonly remoteAuthority: string | null) { + this.id = `claude-desktop.${this.remoteAuthority}`; + } + + getFilePath({ platform, winAppData, xdgHome, homedir }: INativeMcpDiscoveryData): URI | undefined { + if (platform === Platform.Windows) { + const appData = winAppData || URI.joinPath(homedir, 'AppData', 'Roaming'); + return URI.joinPath(appData, 'Claude', 'claude_desktop_config.json'); + } else if (platform === Platform.Mac) { + return URI.joinPath(homedir, 'Library', 'Application Support', 'Claude', 'claude_desktop_config.json'); + } else { + const configDir = xdgHome || URI.joinPath(homedir, '.config'); + return URI.joinPath(configDir, 'Claude', 'claude_desktop_config.json'); + } + } + + adaptFile(contents: VSBuffer, { homedir }: INativeMcpDiscoveryData): McpServerDefinition[] | undefined { + let parsed: { + mcpServers: Record; + }>; + }; + + try { + parsed = JSON.parse(contents.toString()); + } catch { + return; + } + return Object.entries(parsed.mcpServers).map(([name, server]): McpServerDefinition => { + return { + id: `claude_desktop_config.${name}`, + label: name, + launch: { + type: McpServerTransportType.Stdio, + args: server.args || [], + command: server.command, + env: server.env || {}, + cwd: homedir, + } + }; + }); + } +} diff --git a/src/vs/workbench/contrib/mcp/common/discovery/nativeMcpRemoteDiscovery.ts b/src/vs/workbench/contrib/mcp/common/discovery/nativeMcpRemoteDiscovery.ts new file mode 100644 index 000000000000..4f525561f6f6 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/discovery/nativeMcpRemoteDiscovery.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 { ProxyChannel } from '../../../../../base/parts/ipc/common/ipc.js'; +import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; +import { IFileService } from '../../../../../platform/files/common/files.js'; +import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; +import { ILabelService } from '../../../../../platform/label/common/label.js'; +import { ILogService } from '../../../../../platform/log/common/log.js'; +import { INativeMcpDiscoveryHelperService, NativeMcpDiscoveryHelperChannelName } from '../../../../../platform/mcp/common/nativeMcpDiscoveryHelper.js'; +import { IRemoteAgentService } from '../../../../services/remote/common/remoteAgentService.js'; +import { IMcpRegistry } from '../mcpRegistryTypes.js'; +import { FilesystemMpcDiscovery } from './nativeMcpDiscoveryAbstract.js'; + +/** + * Discovers MCP servers on the remote filesystem, if any. + */ +export class RemoteNativeMpcDiscovery extends FilesystemMpcDiscovery { + constructor( + @IRemoteAgentService private readonly remoteAgent: IRemoteAgentService, + @ILogService private readonly logService: ILogService, + @ILabelService labelService: ILabelService, + @IFileService fileService: IFileService, + @IInstantiationService instantiationService: IInstantiationService, + @IMcpRegistry mcpRegistry: IMcpRegistry, + @IConfigurationService configurationService: IConfigurationService, + ) { + super(remoteAgent.getConnection()?.remoteAuthority || null, labelService, fileService, instantiationService, mcpRegistry, configurationService); + } + + public override async start() { + const connection = this.remoteAgent.getConnection(); + if (!connection) { + return this.setDetails(undefined); + } + + await connection.withChannel(NativeMcpDiscoveryHelperChannelName, async channel => { + const service = ProxyChannel.toService(channel); + + service.load().then( + data => this.setDetails(data), + err => { + this.logService.warn('Error getting remote process MCP environment', err); + this.setDetails(undefined); + } + ); + }); + } +} diff --git a/src/vs/workbench/contrib/mcp/common/mcpConfiguration.ts b/src/vs/workbench/contrib/mcp/common/mcpConfiguration.ts new file mode 100644 index 000000000000..46864f954c10 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/mcpConfiguration.ts @@ -0,0 +1,142 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { IJSONSchema } from '../../../../base/common/jsonSchema.js'; +import { localize } from '../../../../nls.js'; +import { IMcpCollectionContribution } from '../../../../platform/extensions/common/extensions.js'; +import { mcpSchemaId } from '../../../services/configuration/common/configuration.js'; +import { inputsSchema } from '../../../services/configurationResolver/common/configurationResolverSchema.js'; +import { IExtensionPointDescriptor } from '../../../services/extensions/common/extensionsRegistry.js'; + +export type { McpConfigurationServer, IMcpConfigurationStdio, IMcpConfiguration } from '../../../../platform/mcp/common/mcpPlatformTypes.js'; + +const mcpActivationEventPrefix = 'onMcpCollection:'; + +export const mcpActivationEvent = (collectionId: string) => mcpActivationEventPrefix + collectionId; + +const mcpSchemaExampleServer = { + command: 'node', + args: ['my-mcp-server.js'], + env: {}, +}; + +export const mcpConfigurationSection = 'mcp'; +export const mcpDiscoverySection = 'chat.mcp.discovery.enabled'; + +export const mcpSchemaExampleServers = { + 'mcp-server-time': { + command: 'python', + args: ['-m', 'mcp_server_time', '--local-timezone=America/Los_Angeles'], + env: {}, + } +}; + +export const mcpStdioServerSchema: IJSONSchema = { + type: 'object', + additionalProperties: false, + examples: [mcpSchemaExampleServer], + properties: { + type: { + type: 'string', + enum: ['stdio'], + description: localize('app.mcp.json.type', "The type of the server.") + }, + command: { + type: 'string', + description: localize('app.mcp.json.command', "The command to run the server.") + }, + args: { + type: 'array', + description: localize('app.mcp.args.command', "Arguments passed to the server."), + items: { + type: 'string' + }, + }, + env: { + description: localize('app.mcp.env.command', "Environment variables passed to the server."), + additionalProperties: { + anyOf: [ + { type: 'null' }, + { type: 'string' }, + { type: 'number' }, + ] + } + }, + } +}; + +export const mcpServerSchema: IJSONSchema = { + id: mcpSchemaId, + type: 'object', + title: localize('app.mcp.json.title', "Model Context Protocol Servers"), + allowTrailingCommas: true, + allowComments: true, + additionalProperties: false, + properties: { + servers: { + examples: [mcpSchemaExampleServers], + additionalProperties: { + oneOf: [mcpStdioServerSchema, { + type: 'object', + additionalProperties: false, + required: ['url', 'type'], + examples: [{ + type: 'sse', + url: 'http://localhost:3001', + headers: {}, + }], + properties: { + type: { + type: 'string', + enum: ['sse'], + description: localize('app.mcp.json.type', "The type of the server.") + }, + url: { + type: 'string', + format: 'uri', + description: localize('app.mcp.json.url', "The URL of the server-sent-event (SSE) server.") + }, + env: { + description: localize('app.mcp.json.headers', "Additional headers sent to the server."), + additionalProperties: { type: 'string' }, + }, + } + }] + } + }, + inputs: inputsSchema.definitions!.inputs + } +}; + +export const mcpContributionPoint: IExtensionPointDescriptor = { + extensionPoint: 'modelContextServerCollections', + activationEventsGenerator(contribs, result) { + for (const contrib of contribs) { + if (contrib.id) { + result.push(mcpActivationEvent(contrib.id)); + } + } + }, + jsonSchema: { + description: localize('vscode.extension.contributes.mcp', 'Contributes Model Context Protocol servers. Users of this should also use `vscode.lm.registerMcpConfigurationProvider`.'), + type: 'array', + defaultSnippets: [{ body: [{ id: '', label: '' }] }], + items: { + additionalProperties: false, + type: 'object', + defaultSnippets: [{ body: { id: '', label: '' } }], + properties: { + id: { + description: localize('vscode.extension.contributes.mcp.id', "Unique ID for the collection."), + type: 'string' + }, + label: { + description: localize('vscode.extension.contributes.mcp.label', "Display name for the collection."), + type: 'string' + } + } + } + } +}; diff --git a/src/vs/workbench/contrib/mcp/common/mcpContextKeys.ts b/src/vs/workbench/contrib/mcp/common/mcpContextKeys.ts new file mode 100644 index 000000000000..0d0aff9aa6d3 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/mcpContextKeys.ts @@ -0,0 +1,63 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + + +import { Disposable } from '../../../../base/common/lifecycle.js'; +import { autorun } from '../../../../base/common/observable.js'; +import { localize } from '../../../../nls.js'; +import { IContextKeyService, RawContextKey } from '../../../../platform/contextkey/common/contextkey.js'; +import { bindContextKey } from '../../../../platform/observable/common/platformObservableUtils.js'; +import { IWorkbenchContribution } from '../../../common/contributions.js'; +import { LazyCollectionState, IMcpService, McpServerToolsState, McpConnectionState } from './mcpTypes.js'; + + +export namespace McpContextKeys { + + export const serverCount = new RawContextKey('mcp.serverCount', undefined, { type: 'number', description: localize('mcp.serverCount.description', "Context key that has the number of registered MCP servers") }); + export const hasUnknownTools = new RawContextKey('mcp.hasUnknownTools', undefined, { type: 'boolean', description: localize('mcp.hasUnknownTools.description', "Indicates whether there are MCP servers with unknown tools.") }); + /** + * A context key that indicates whether there are any servers with errors. + * + * @type {boolean} + * @default undefined + * @description This key is used to track the presence of servers with errors in the MCP context. + */ + export const hasServersWithErrors = new RawContextKey('mcp.hasServersWithErrors', undefined, { type: 'boolean', description: localize('mcp.hasServersWithErrors.description', "Indicates whether there are any MCP servers with errors.") }); + export const toolsCount = new RawContextKey('mcp.toolsCount', undefined, { type: 'number', description: localize('mcp.toolsCount.description', "Context key that has the number of registered MCP tools") }); +} + + +export class McpContextKeysController extends Disposable implements IWorkbenchContribution { + + static readonly ID = 'workbench.contrib.mcp.contextKey'; + + constructor( + @IMcpService mcpService: IMcpService, + @IContextKeyService contextKeyService: IContextKeyService + ) { + super(); + + const ctxServerCount = McpContextKeys.serverCount.bindTo(contextKeyService); + const ctxToolsCount = McpContextKeys.toolsCount.bindTo(contextKeyService); + const ctxHasUnknownTools = McpContextKeys.hasUnknownTools.bindTo(contextKeyService); + + this._store.add(bindContextKey(McpContextKeys.hasServersWithErrors, contextKeyService, r => mcpService.servers.read(r).some(c => c.connectionState.read(r).state === McpConnectionState.Kind.Error))); + + this._store.add(autorun(r => { + const servers = mcpService.servers.read(r); + const serverTools = servers.map(s => s.tools.read(r)); + ctxServerCount.set(servers.length); + ctxToolsCount.set(serverTools.reduce((count, tools) => count + tools.length, 0)); + ctxHasUnknownTools.set(mcpService.lazyCollectionState.read(r) !== LazyCollectionState.AllKnown || servers.some(s => { + if (s.trusted.read(r) === false) { + return false; + } + + const toolState = s.toolsState.read(r); + return toolState === McpServerToolsState.Unknown || toolState === McpServerToolsState.RefreshingFromUnknown; + })); + })); + } +} diff --git a/src/vs/workbench/contrib/mcp/common/mcpRegistry.ts b/src/vs/workbench/contrib/mcp/common/mcpRegistry.ts new file mode 100644 index 000000000000..6ae03b715b69 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/mcpRegistry.ts @@ -0,0 +1,278 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { StringSHA1 } from '../../../../base/common/hash.js'; +import { MarkdownString } from '../../../../base/common/htmlContent.js'; +import { Lazy } from '../../../../base/common/lazy.js'; +import { Disposable, IDisposable } from '../../../../base/common/lifecycle.js'; +import { derived, IObservable, observableValue } from '../../../../base/common/observable.js'; +import { basename } from '../../../../base/common/resources.js'; +import { localize } from '../../../../nls.js'; +import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js'; +import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; +import { observableMemento } from '../../../../platform/observable/common/observableMemento.js'; +import { IProductService } from '../../../../platform/product/common/productService.js'; +import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; +import { IConfigurationResolverService } from '../../../services/configurationResolver/common/configurationResolver.js'; +import { McpRegistryInputStorage } from './mcpRegistryInputStorage.js'; +import { IMcpHostDelegate, IMcpRegistry, IMcpResolveConnectionOptions } from './mcpRegistryTypes.js'; +import { McpServerConnection } from './mcpServerConnection.js'; +import { IMcpServerConnection, LazyCollectionState, McpCollectionDefinition, McpCollectionReference } from './mcpTypes.js'; + +const createTrustMemento = observableMemento>>({ + defaultValue: {}, + key: 'mcp.trustedCollections' +}); + +const collectionPrefixLen = 3; + +export class McpRegistry extends Disposable implements IMcpRegistry { + declare public readonly _serviceBrand: undefined; + + private readonly _trustPrompts = new Map>(); + + private readonly _collections = observableValue('collections', []); + private readonly _delegates: IMcpHostDelegate[] = []; + public readonly collections: IObservable = this._collections; + + private readonly _collectionToPrefixes = this._collections.map(c => { + // This creates tool prefixes based on a hash of the collection ID. This is + // a short prefix because tool names that are too long can cause errors (#243602). + // So we take a hash (in order for tools to be stable, because randomized + // names can cause hallicinations if present in history) and then adjust + // them if there are any collisions. + type CollectionHash = { view: number; hash: string; collection: McpCollectionDefinition }; + + const hashes = c.map((collection): CollectionHash => { + const sha = new StringSHA1(); + sha.update(collection.id); + return { view: 0, hash: sha.digest(), collection }; + }); + + const view = (h: CollectionHash) => h.hash.slice(h.view, h.view + collectionPrefixLen); + + let collided = false; + do { + hashes.sort((a, b) => view(a).localeCompare(view(b)) || a.collection.id.localeCompare(b.collection.id)); + collided = false; + for (let i = 1; i < hashes.length; i++) { + const prev = hashes[i - 1]; + const curr = hashes[i]; + if (view(prev) === view(curr) && curr.view + collectionPrefixLen < curr.hash.length) { + curr.view++; + collided = true; + } + } + } while (collided); + + return Object.fromEntries(hashes.map(h => [h.collection.id, view(h) + '.'])); + }); + + private readonly _workspaceStorage = new Lazy(() => this._register(this._instantiationService.createInstance(McpRegistryInputStorage, StorageScope.WORKSPACE, StorageTarget.USER))); + private readonly _profileStorage = new Lazy(() => this._register(this._instantiationService.createInstance(McpRegistryInputStorage, StorageScope.PROFILE, StorageTarget.USER))); + + private readonly _trustMemento = new Lazy(() => this._register(createTrustMemento(StorageScope.APPLICATION, StorageTarget.MACHINE, this._storageService))); + private readonly _lazyCollectionsToUpdate = new Set(); + private readonly _ongoingLazyActivations = observableValue(this, 0); + + public readonly lazyCollectionState = derived(reader => { + if (this._ongoingLazyActivations.read(reader) > 0) { + return LazyCollectionState.LoadingUnknown; + } + const collections = this._collections.read(reader); + return collections.some(c => c.lazy && c.lazy.isCached === false) ? LazyCollectionState.HasUnknown : LazyCollectionState.AllKnown; + }); + + public get delegates(): readonly IMcpHostDelegate[] { + return this._delegates; + } + + constructor( + @IInstantiationService private readonly _instantiationService: IInstantiationService, + @IConfigurationResolverService private readonly _configurationResolverService: IConfigurationResolverService, + @IDialogService private readonly _dialogService: IDialogService, + @IStorageService private readonly _storageService: IStorageService, + @IProductService private readonly _productService: IProductService, + ) { + super(); + } + + public registerDelegate(delegate: IMcpHostDelegate): IDisposable { + this._delegates.push(delegate); + return { + dispose: () => { + const index = this._delegates.indexOf(delegate); + if (index !== -1) { + this._delegates.splice(index, 1); + } + } + }; + } + + public registerCollection(collection: McpCollectionDefinition): IDisposable { + const currentCollections = this._collections.get(); + const toReplace = currentCollections.find(c => c.lazy && c.id === collection.id); + + // Incoming collections replace the "lazy" versions. See `ExtensionMcpDiscovery` for an example. + if (toReplace) { + this._lazyCollectionsToUpdate.add(collection.id); + this._collections.set(currentCollections.map(c => c === toReplace ? collection : c), undefined); + } else { + this._collections.set([...currentCollections, collection], undefined); + } + + return { + dispose: () => { + const currentCollections = this._collections.get(); + this._collections.set(currentCollections.filter(c => c !== collection), undefined); + } + }; + } + + public collectionToolPrefix(collection: McpCollectionReference): IObservable { + return this._collectionToPrefixes.map(p => p[collection.id] ?? ''); + } + + public async discoverCollections(): Promise { + const toDiscover = this._collections.get().filter(c => c.lazy && !c.lazy.isCached); + + this._ongoingLazyActivations.set(this._ongoingLazyActivations.get() + 1, undefined); + await Promise.all(toDiscover.map(c => c.lazy?.load())).finally(() => { + this._ongoingLazyActivations.set(this._ongoingLazyActivations.get() - 1, undefined); + }); + + const found: McpCollectionDefinition[] = []; + const current = this._collections.get(); + for (const collection of toDiscover) { + const rec = current.find(c => c.id === collection.id); + if (!rec) { + // ignored + } else if (rec.lazy) { + rec.lazy.removed?.(); // did not get replaced by the non-lazy version + } else { + found.push(rec); + } + } + + + return found; + } + + public clearSavedInputs() { + this._profileStorage.value.clearAll(); + this._workspaceStorage.value.clearAll(); + } + + public resetTrust(): void { + this._trustMemento.value.set({}, undefined); + } + + public getTrust(collectionRef: McpCollectionReference): IObservable { + return derived(reader => { + const collection = this._collections.read(reader).find(c => c.id === collectionRef.id); + if (!collection || collection.isTrustedByDefault) { + return true; + } + + const memento = this._trustMemento.value.read(reader); + return memento.hasOwnProperty(collection.id) ? memento[collection.id] : undefined; + }); + } + + private _promptForTrust(collection: McpCollectionDefinition): Promise { + // Collect all trust prompts for a single config so that concurrently trying to start N + // servers in a config don't result in N different dialogs + let resultPromise = this._trustPrompts.get(collection.id); + resultPromise ??= this._promptForTrustOpenDialog(collection).finally(() => { + this._trustPrompts.delete(collection.id); + }); + this._trustPrompts.set(collection.id, resultPromise); + + return resultPromise; + } + + private async _promptForTrustOpenDialog(collection: McpCollectionDefinition): Promise { + const labelWithOrigin = collection.presentation?.origin + ? `[\`${basename(collection.presentation.origin)}\`](${collection.presentation.origin})` + : collection.label; + const result = await this._dialogService.prompt( + { + message: localize('trustTitleWithOrigin', 'Trust MCP servers from {0}?', collection.label), + custom: { + markdownDetails: [{ + markdown: new MarkdownString(localize('mcp.trust.details', '{0} discovered Model Context Protocol servers from {1} (`{2}`). {0} can use their capabilities in Chat.\n\nDo you want to allow running MCP servers from {3}?', this._productService.nameShort, collection.label, collection.serverDefinitions.get().map(s => s.label).join('`, `'), labelWithOrigin)), + dismissOnLinkClick: true, + }] + }, + buttons: [ + { label: localize('mcp.trust.yes', 'Trust'), run: () => true }, + { label: localize('mcp.trust.no', 'Do not trust'), run: () => false } + ], + }, + ); + + return result.result; + } + + public async resolveConnection({ collectionRef, definitionRef, forceTrust }: IMcpResolveConnectionOptions): Promise { + const collection = this._collections.get().find(c => c.id === collectionRef.id); + const definition = collection?.serverDefinitions.get().find(s => s.id === definitionRef.id); + if (!collection || !definition) { + throw new Error(`Collection or definition not found for ${collectionRef.id} and ${definitionRef.id}`); + } + + const delegate = this._delegates.find(d => d.canStart(collection, definition)); + if (!delegate) { + throw new Error('No delegate found that can handle the connection'); + } + + if (!collection.isTrustedByDefault) { + const memento = this._trustMemento.value.get(); + const trusted = memento.hasOwnProperty(collection.id) ? memento[collection.id] : undefined; + + if (trusted) { + // continue + } else if (trusted === undefined || forceTrust) { + const trustValue = await this._promptForTrust(collection); + if (trustValue !== undefined) { + this._trustMemento.value.set({ ...memento, [collection.id]: trustValue }, undefined); + } + if (!trustValue) { + return; + } + } else /** trusted === false && !forceTrust */ { + return undefined; + } + } + + let launch = definition.launch; + + if (definition.variableReplacement) { + const inputStorage = definition.variableReplacement.folder ? this._workspaceStorage.value : this._profileStorage.value; + const previouslyStored = await inputStorage.getMap(); + + const { folder, section, target } = definition.variableReplacement; + + // based on _configurationResolverService.resolveWithInteractionReplace + launch = await this._configurationResolverService.resolveAnyAsync(folder, launch); + + const newVariables = await this._configurationResolverService.resolveWithInteraction(folder, launch, section, previouslyStored, target); + + if (newVariables?.size) { + const completeVariables = { ...previouslyStored, ...Object.fromEntries(newVariables) }; + launch = await this._configurationResolverService.resolveAnyAsync(folder, launch, completeVariables); + await inputStorage.setSecrets(completeVariables); + } + } + + return this._instantiationService.createInstance( + McpServerConnection, + collection, + definition, + delegate, + launch, + ); + } +} diff --git a/src/vs/workbench/contrib/mcp/common/mcpRegistryInputStorage.ts b/src/vs/workbench/contrib/mcp/common/mcpRegistryInputStorage.ts new file mode 100644 index 000000000000..6e23a0599988 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/mcpRegistryInputStorage.ts @@ -0,0 +1,192 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Sequencer } from '../../../../base/common/async.js'; +import { decodeBase64, encodeBase64, VSBuffer } from '../../../../base/common/buffer.js'; +import { Lazy } from '../../../../base/common/lazy.js'; +import { Disposable } from '../../../../base/common/lifecycle.js'; +import { isEmptyObject } from '../../../../base/common/types.js'; +import { ILogService } from '../../../../platform/log/common/log.js'; +import { ISecretStorageService } from '../../../../platform/secrets/common/secrets.js'; +import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; + +const MCP_ENCRYPTION_KEY_NAME = 'mcpEncryptionKey'; +const MCP_ENCRYPTION_KEY_ALGORITHM = 'AES-GCM'; +const MCP_ENCRYPTION_KEY_LEN = 256; +const MCP_ENCRYPTION_IV_LENGTH = 12; // 96 bits +const MCP_DATA_STORED_VERSION = 1; +const MCP_DATA_STORED_KEY = 'mcpInputs'; + +interface IStoredData { + version: number; + values: Record; + secrets?: { value: string; iv: string }; // base64, encrypted +} + +interface IHydratedData extends IStoredData { + unsealedSecrets?: Record; +} + +export class McpRegistryInputStorage extends Disposable { + private static secretSequencer = new Sequencer(); + private readonly _secretsSealerSequencer = new Sequencer(); + + private readonly _getEncryptionKey = new Lazy(() => { + return McpRegistryInputStorage.secretSequencer.queue(async () => { + const existing = await this._secretStorageService.get(MCP_ENCRYPTION_KEY_NAME); + if (existing) { + try { + const parsed: JsonWebKey = JSON.parse(existing); + return await crypto.subtle.importKey('jwk', parsed, MCP_ENCRYPTION_KEY_ALGORITHM, false, ['encrypt', 'decrypt']); + } catch { + // fall through + } + } + + const key = await crypto.subtle.generateKey( + { name: MCP_ENCRYPTION_KEY_ALGORITHM, length: MCP_ENCRYPTION_KEY_LEN }, + true, + ['encrypt', 'decrypt'], + ); + + const exported = await crypto.subtle.exportKey('jwk', key); + await this._secretStorageService.set(MCP_ENCRYPTION_KEY_NAME, JSON.stringify(exported)); + return key; + }); + }); + + private _didChange = false; + + private _record = new Lazy(() => { + const stored = this._storageService.getObject(MCP_DATA_STORED_KEY, this._scope); + return stored?.version === MCP_DATA_STORED_VERSION ? { ...stored } : { version: MCP_DATA_STORED_VERSION, values: {} }; + }); + + + constructor( + private readonly _scope: StorageScope, + _target: StorageTarget, + @IStorageService private readonly _storageService: IStorageService, + @ISecretStorageService private readonly _secretStorageService: ISecretStorageService, + @ILogService private readonly _logService: ILogService, + ) { + super(); + + this._register(_storageService.onWillSaveState(() => { + if (this._didChange) { + this._storageService.store(MCP_DATA_STORED_KEY, { + version: MCP_DATA_STORED_VERSION, + values: this._record.value.values, + secrets: this._record.value.secrets, + } satisfies IStoredData, this._scope, _target); + this._didChange = false; + } + })); + } + + /** Deletes all collection data from storage. */ + public clearAll() { + this._record.value.values = {}; + this._record.value.secrets = undefined; + this._record.value.unsealedSecrets = undefined; + this._didChange = true; + } + + /** Delete a single collection data from the storage. */ + public async clear(inputKey: string) { + const secrets = await this._unsealSecrets(); + delete this._record.value.values[inputKey]; + this._didChange = true; + + if (secrets.hasOwnProperty(inputKey)) { + delete secrets[inputKey]; + await this._sealSecrets(); + } + } + + /** Gets a mapping of saved input data. */ + public async getMap() { + const secrets = await this._unsealSecrets(); + return { ...this._record.value.values, ...secrets }; + } + + /** Updates the input data mapping. */ + public async setPlainText(values: Record) { + Object.assign(this._record.value.values, values); + this._didChange = true; + } + + /** Updates the input secrets mapping. */ + public async setSecrets(values: Record) { + const unsealed = await this._unsealSecrets(); + Object.assign(unsealed, values); + await this._sealSecrets(); + } + + private async _sealSecrets() { + return this._secretsSealerSequencer.queue(async () => { + if (!this._record.value.unsealedSecrets || isEmptyObject(this._record.value.unsealedSecrets)) { + this._record.value.secrets = undefined; + return; + } + + if (!this._record.value.secrets) { + const iv = crypto.getRandomValues(new Uint8Array(MCP_ENCRYPTION_IV_LENGTH)); + this._record.value.secrets = { + value: '', + iv: encodeBase64(VSBuffer.wrap(iv)), + }; + } + + const toSeal = JSON.stringify(this._record.value.unsealedSecrets); + const iv = decodeBase64(this._record.value.secrets.iv); + const key = await this._getEncryptionKey.value; + const encrypted = await crypto.subtle.encrypt( + { name: MCP_ENCRYPTION_KEY_ALGORITHM, iv: iv.buffer }, + key, + new TextEncoder().encode(toSeal).buffer, + ); + + const enc = encodeBase64(VSBuffer.wrap(new Uint8Array(encrypted))); + if (this._record.value.secrets.value === enc) { + return; + } + + this._record.value.secrets.value = enc; + this._didChange = true; + }); + } + + private async _unsealSecrets(): Promise> { + if (!this._record.value.secrets) { + return this._record.value.unsealedSecrets ??= {}; + } + + if (this._record.value.unsealedSecrets) { + return this._record.value.unsealedSecrets; + } + + try { + const key = await this._getEncryptionKey.value; + const iv = decodeBase64(this._record.value.secrets.iv); + const encrypted = decodeBase64(this._record.value.secrets.value); + + const decrypted = await crypto.subtle.decrypt( + { name: MCP_ENCRYPTION_KEY_ALGORITHM, iv: iv.buffer }, + key, + encrypted.buffer, + ); + + const unsealedSecrets = JSON.parse(new TextDecoder().decode(decrypted)); + this._record.value.unsealedSecrets = unsealedSecrets; + return unsealedSecrets; + } catch (e) { + this._logService.warn('Error unsealing MCP secrets', e); + this._record.value.secrets = undefined; + } + + return {}; + } +} diff --git a/src/vs/workbench/contrib/mcp/common/mcpRegistryTypes.ts b/src/vs/workbench/contrib/mcp/common/mcpRegistryTypes.ts new file mode 100644 index 000000000000..e835be770b88 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/mcpRegistryTypes.ts @@ -0,0 +1,64 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Event } from '../../../../base/common/event.js'; +import { IDisposable } from '../../../../base/common/lifecycle.js'; +import { IObservable } from '../../../../base/common/observable.js'; +import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; +import { McpCollectionDefinition, McpServerDefinition, McpServerLaunch, McpConnectionState, IMcpServerConnection, McpCollectionReference, McpDefinitionReference, LazyCollectionState } from './mcpTypes.js'; +import { MCP } from './modelContextProtocol.js'; + +export const IMcpRegistry = createDecorator('mcpRegistry'); + +/** Message transport to a single MCP server. */ +export interface IMcpMessageTransport extends IDisposable { + readonly state: IObservable; + readonly onDidLog: Event; + readonly onDidReceiveMessage: Event; + send(message: MCP.JSONRPCMessage): void; + stop(): void; +} + +export interface IMcpHostDelegate { + waitForInitialProviderPromises(): Promise; + canStart(collectionDefinition: McpCollectionDefinition, serverDefinition: McpServerDefinition): boolean; + start(collectionDefinition: McpCollectionDefinition, serverDefinition: McpServerDefinition, resolvedLaunch: McpServerLaunch): IMcpMessageTransport; +} + +export interface IMcpResolveConnectionOptions { + collectionRef: McpCollectionReference; + definitionRef: McpDefinitionReference; + /** If set, the user will be asked to trust the collection even if they untrusted it previously */ + forceTrust?: boolean; +} + +export interface IMcpRegistry { + readonly _serviceBrand: undefined; + + readonly collections: IObservable; + readonly delegates: readonly IMcpHostDelegate[]; + + /** Gets the prefix that should be applied to a collection's tools in order to avoid ID conflicts */ + collectionToolPrefix(collection: McpCollectionReference): IObservable; + + /** Whether there are new collections that can be resolved with a discover() call */ + readonly lazyCollectionState: IObservable; + /** Discover new collections, returning newly-discovered ones. */ + discoverCollections(): Promise; + + registerDelegate(delegate: IMcpHostDelegate): IDisposable; + registerCollection(collection: McpCollectionDefinition): IDisposable; + + /** Resets the trust state of all collections. */ + resetTrust(): void; + + /** Gets whether the collection is trusted. */ + getTrust(collection: McpCollectionReference): IObservable; + + /** Resets any saved inputs for the connection. */ + clearSavedInputs(collection: McpCollectionReference, definition: McpServerDefinition): void; + /** Creates a connection for the collection and definition. */ + resolveConnection(options: IMcpResolveConnectionOptions): Promise; +} diff --git a/src/vs/workbench/contrib/mcp/common/mcpServer.ts b/src/vs/workbench/contrib/mcp/common/mcpServer.ts new file mode 100644 index 000000000000..640f98da8ca3 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/mcpServer.ts @@ -0,0 +1,325 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { raceCancellationError, Sequencer } from '../../../../base/common/async.js'; +import { CancellationToken, CancellationTokenSource } from '../../../../base/common/cancellation.js'; +import { Disposable, DisposableStore, IDisposable, toDisposable } from '../../../../base/common/lifecycle.js'; +import { LRUCache } from '../../../../base/common/map.js'; +import { autorun, autorunWithStore, derived, disposableObservableValue, IObservable, ITransaction, observableFromEvent, ObservablePromise, observableValue, transaction } from '../../../../base/common/observable.js'; +import { IStorageService, StorageScope, StorageTarget } from '../../../../platform/storage/common/storage.js'; +import { IWorkspaceContextService } from '../../../../platform/workspace/common/workspace.js'; +import { IExtensionService } from '../../../services/extensions/common/extensions.js'; +import { mcpActivationEvent } from './mcpConfiguration.js'; +import { IMcpRegistry } from './mcpRegistryTypes.js'; +import { McpServerRequestHandler } from './mcpServerRequestHandler.js'; +import { extensionMcpCollectionPrefix, IMcpServer, IMcpServerConnection, IMcpTool, McpCollectionReference, McpConnectionFailedError, McpConnectionState, McpDefinitionReference, McpServerDefinition, McpServerToolsState } from './mcpTypes.js'; +import { MCP } from './modelContextProtocol.js'; + + +interface IToolCacheEntry { + /** Cached tools so we can show what's available before it's started */ + readonly tools: readonly MCP.Tool[]; +} + +interface IServerCacheEntry { + readonly servers: readonly McpServerDefinition.Serialized[]; +} + +export class McpServerMetadataCache extends Disposable { + private didChange = false; + private readonly cache = new LRUCache(128); + private readonly extensionServers = new Map(); + + constructor( + scope: StorageScope, + @IStorageService storageService: IStorageService, + ) { + super(); + + type StoredType = { + extensionServers: [string, IServerCacheEntry][]; + serverTools: [string, IToolCacheEntry][]; + }; + + const storageKey = 'mcpToolCache'; + this._register(storageService.onWillSaveState(() => { + if (this.didChange) { + storageService.store(storageKey, { + extensionServers: [...this.extensionServers], + serverTools: this.cache.toJSON(), + } satisfies StoredType, scope, StorageTarget.MACHINE); + this.didChange = false; + } + })); + + try { + const cached: StoredType | undefined = storageService.getObject(storageKey, scope); + this.extensionServers = new Map(cached?.extensionServers ?? []); + cached?.serverTools?.forEach(([k, v]) => this.cache.set(k, v)); + } catch { + // ignored + } + } + + /** Resets the cache for tools and extension servers */ + reset() { + this.cache.clear(); + this.extensionServers.clear(); + this.didChange = true; + } + + /** Gets cached tools for a server (used before a server is running) */ + getTools(definitionId: string): readonly MCP.Tool[] | undefined { + return this.cache.get(definitionId)?.tools; + } + + /** Sets cached tools for a server */ + storeTools(definitionId: string, tools: readonly MCP.Tool[]): void { + this.cache.set(definitionId, { ...this.cache.get(definitionId), tools }); + this.didChange = true; + } + + /** Gets cached servers for a collection (used for extensions, before the extension activates) */ + getServers(collectionId: string) { + return this.extensionServers.get(collectionId); + } + + /** Sets cached servers for a collection */ + storeServers(collectionId: string, entry: IServerCacheEntry | undefined): void { + if (entry) { + this.extensionServers.set(collectionId, entry); + } else { + this.extensionServers.delete(collectionId); + } + this.didChange = true; + } +} + +export class McpServer extends Disposable implements IMcpServer { + private readonly _connectionSequencer = new Sequencer(); + private readonly _connection = this._register(disposableObservableValue(this, undefined)); + + public readonly connection = this._connection; + public readonly connectionState: IObservable = derived(reader => this._connection.read(reader)?.state.read(reader) ?? { state: McpConnectionState.Kind.Stopped }); + + private get toolsFromCache() { + return this._toolCache.getTools(this.definition.id); + } + private readonly toolsFromServerPromise = observableValue | undefined>(this, undefined); + private readonly toolsFromServer = derived(reader => this.toolsFromServerPromise.read(reader)?.promiseResult.read(reader)?.data); + + public readonly tools: IObservable; + + public readonly toolsState = derived(reader => { + const fromServer = this.toolsFromServerPromise.read(reader); + const connectionState = this.connectionState.read(reader); + const isIdle = McpConnectionState.canBeStarted(connectionState.state) && !fromServer; + if (isIdle) { + return this.toolsFromCache ? McpServerToolsState.Cached : McpServerToolsState.Unknown; + } + + const fromServerResult = fromServer?.promiseResult.read(reader); + if (!fromServerResult) { + return this.toolsFromCache ? McpServerToolsState.RefreshingFromCached : McpServerToolsState.RefreshingFromUnknown; + } + + return fromServerResult.error ? (this.toolsFromCache ? McpServerToolsState.Cached : McpServerToolsState.Unknown) : McpServerToolsState.Live; + }); + + public get trusted() { + return this._mcpRegistry.getTrust(this.collection); + } + + constructor( + public readonly collection: McpCollectionReference, + public readonly definition: McpDefinitionReference, + private readonly _requiresExtensionActivation: boolean | undefined, + private readonly _toolCache: McpServerMetadataCache, + @IMcpRegistry private readonly _mcpRegistry: IMcpRegistry, + @IWorkspaceContextService workspacesService: IWorkspaceContextService, + @IExtensionService private readonly _extensionService: IExtensionService, + ) { + super(); + + // 1. Reflect workspaces into the MCP roots + const workspaces = observableFromEvent( + this, + workspacesService.onDidChangeWorkspaceFolders, + () => workspacesService.getWorkspace().folders, + ); + + this._register(autorunWithStore(reader => { + const cnx = this._connection.read(reader)?.handler.read(reader); + if (!cnx) { + return; + } + + cnx.roots = workspaces.read(reader).map(wf => ({ + uri: wf.uri.toString(), + name: wf.name, + })); + })); + + // 2. Populate this.tools when we connect to a server. + this._register(autorunWithStore((reader, store) => { + const cnx = this._connection.read(reader)?.handler.read(reader); + if (cnx) { + this.populateLiveData(cnx, store); + } else { + this.resetLiveData(); + } + })); + + // 3. Update the cache when tools update + this._register(autorun(reader => { + const tools = this.toolsFromServer.read(reader); + if (tools) { + this._toolCache.storeTools(definition.id, tools); + } + })); + + // 4. Publish tools + const toolPrefix = this._mcpRegistry.collectionToolPrefix(this.collection); + this.tools = derived(reader => { + const serverTools = this.toolsFromServer.read(reader); + const definitions = serverTools ?? this.toolsFromCache ?? []; + const prefix = toolPrefix.read(reader); + return definitions.map(def => new McpTool(this, prefix, def)); + }); + } + + public showOutput(): void { + this._connection.get()?.showOutput(); + } + + public start(isFromInteraction?: boolean): Promise { + return this._connectionSequencer.queue(async () => { + const activationEvent = mcpActivationEvent(this.collection.id.slice(extensionMcpCollectionPrefix.length)); + if (this._requiresExtensionActivation && !this._extensionService.activationEventIsDone(activationEvent)) { + await this._extensionService.activateByEvent(activationEvent); + await Promise.all(this._mcpRegistry.delegates + .map(r => r.waitForInitialProviderPromises())); + // This can happen if the server was created from a cached MCP server seen + // from an extension, but then it wasn't registered when the extension activated. + if (this._store.isDisposed) { + return { state: McpConnectionState.Kind.Stopped }; + } + } + + let connection = this._connection.get(); + if (connection && McpConnectionState.canBeStarted(connection.state.get().state)) { + connection.dispose(); + connection = undefined; + this._connection.set(connection, undefined); + } + + if (!connection) { + connection = await this._mcpRegistry.resolveConnection({ + collectionRef: this.collection, + definitionRef: this.definition, + forceTrust: isFromInteraction, + }); + if (!connection) { + return { state: McpConnectionState.Kind.Stopped }; + } + + if (this._store.isDisposed) { + connection.dispose(); + return { state: McpConnectionState.Kind.Stopped }; + } + + this._connection.set(connection, undefined); + } + + return connection.start(); + }); + } + + public stop(): Promise { + return this._connection.get()?.stop() || Promise.resolve(); + } + + private resetLiveData() { + transaction(tx => { + this.toolsFromServerPromise.set(undefined, tx); + }); + } + + private populateLiveData(handler: McpServerRequestHandler, store: DisposableStore) { + const cts = new CancellationTokenSource(); + store.add(toDisposable(() => cts.dispose(true))); + + // todo: add more than just tools here + + const updateTools = (tx: ITransaction | undefined) => { + this.toolsFromServerPromise.set(new ObservablePromise(handler.listTools({}, cts.token)), tx); + }; + + store.add(handler.onDidChangeToolList(() => updateTools(undefined))); + + transaction(tx => { + updateTools(tx); + }); + } + + /** + * Helper function to call the function on the handler once it's online. The + * connection started if it is not already. + */ + public async callOn(fn: (handler: McpServerRequestHandler) => Promise, token: CancellationToken = CancellationToken.None): Promise { + + await this.start(); // idempotent + + let ranOnce = false; + let d: IDisposable; + + const callPromise = new Promise((resolve, reject) => { + + d = autorun(reader => { + const connection = this._connection.read(reader); + if (!connection || ranOnce) { + return; + } + + const handler = connection.handler.read(reader); + if (!handler) { + const state = connection.state.read(reader); + if (state.state === McpConnectionState.Kind.Error) { + reject(new McpConnectionFailedError(`MCP server could not be started: ${state.message}`)); + return; + } else if (state.state === McpConnectionState.Kind.Stopped) { + reject(new McpConnectionFailedError('MCP server has stopped')); + return; + } else { + // keep waiting for handler + return; + } + } + + resolve(fn(handler)); + ranOnce = true; // aggressive prevent multiple racey calls, don't dispose because autorun is sync + }); + }); + + return raceCancellationError(callPromise, token).finally(() => d.dispose()); + } +} + +export class McpTool implements IMcpTool { + + readonly id: string; + + constructor( + private readonly _server: McpServer, + idPrefix: string, + public readonly definition: MCP.Tool, + ) { + this.id = (idPrefix + definition.name).replaceAll('.', '_'); + } + + call(params: Record, token?: CancellationToken): Promise { + return this._server.callOn(h => h.callTool({ name: this.definition.name, arguments: params }), token); + } +} diff --git a/src/vs/workbench/contrib/mcp/common/mcpServerConnection.ts b/src/vs/workbench/contrib/mcp/common/mcpServerConnection.ts new file mode 100644 index 000000000000..5016ebe7f531 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/mcpServerConnection.ts @@ -0,0 +1,145 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { CancellationTokenSource } from '../../../../base/common/cancellation.js'; +import { Disposable, DisposableStore, IReference, MutableDisposable, toDisposable } from '../../../../base/common/lifecycle.js'; +import { autorun, IObservable, observableValue } from '../../../../base/common/observable.js'; +import { localize } from '../../../../nls.js'; +import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; +import { ILogger, ILoggerService } from '../../../../platform/log/common/log.js'; +import { IOutputService } from '../../../services/output/common/output.js'; +import { IMcpHostDelegate, IMcpMessageTransport } from './mcpRegistryTypes.js'; +import { McpServerRequestHandler } from './mcpServerRequestHandler.js'; +import { McpCollectionDefinition, IMcpServerConnection, McpServerDefinition, McpConnectionState, McpServerLaunch } from './mcpTypes.js'; + +export class McpServerConnection extends Disposable implements IMcpServerConnection { + private readonly _launch = this._register(new MutableDisposable>()); + private readonly _state = observableValue('mcpServerState', { state: McpConnectionState.Kind.Stopped }); + private readonly _requestHandler = observableValue('mcpServerRequestHandler', undefined); + + public readonly state: IObservable = this._state; + public readonly handler: IObservable = this._requestHandler; + + private readonly _loggerId: string; + private readonly _logger: ILogger; + private _launchId = 0; + + constructor( + private readonly _collection: McpCollectionDefinition, + public readonly definition: McpServerDefinition, + private readonly _delegate: IMcpHostDelegate, + public readonly launchDefinition: McpServerLaunch, + @ILoggerService private readonly _loggerService: ILoggerService, + @IOutputService private readonly _outputService: IOutputService, + @IInstantiationService private readonly _instantiationService: IInstantiationService, + ) { + super(); + this._loggerId = `mcpServer/${definition.id}`; + this._logger = this._register(_loggerService.createLogger(this._loggerId, { hidden: true, name: `MCP: ${definition.label}` })); + } + + /** @inheritdoc */ + public showOutput(): void { + this._loggerService.setVisibility(this._loggerId, true); + this._outputService.showChannel(this._loggerId); + } + + /** @inheritdoc */ + public async start(): Promise { + const currentState = this._state.get(); + if (!McpConnectionState.canBeStarted(currentState.state)) { + return this._waitForState(McpConnectionState.Kind.Running, McpConnectionState.Kind.Error); + } + + const launchId = ++this._launchId; + this._launch.value = undefined; + this._state.set({ state: McpConnectionState.Kind.Starting }, undefined); + this._logger.info(localize('mcpServer.starting', 'Starting server {0}', this.definition.label)); + + try { + const launch = this._delegate.start(this._collection, this.definition, this.launchDefinition); + this._launch.value = this.adoptLaunch(launch, launchId); + return this._waitForState(McpConnectionState.Kind.Running, McpConnectionState.Kind.Error); + } catch (e) { + const errorState: McpConnectionState = { + state: McpConnectionState.Kind.Error, + message: e instanceof Error ? e.message : String(e) + }; + this._state.set(errorState, undefined); + return errorState; + } + } + + private adoptLaunch(launch: IMcpMessageTransport, launchId: number): IReference { + const store = new DisposableStore(); + const cts = new CancellationTokenSource(); + + store.add(toDisposable(() => cts.dispose(true))); + store.add(launch); + store.add(launch.onDidLog(msg => { + this._logger.info(msg); + })); + + let didStart = false; + store.add(autorun(reader => { + const state = launch.state.read(reader); + this._state.set(state, undefined); + this._logger.info(localize('mcpServer.state', 'Connection state: {0}', McpConnectionState.toString(state))); + + if (state.state === McpConnectionState.Kind.Running && !didStart) { + didStart = true; + McpServerRequestHandler.create(this._instantiationService, launch, this._logger, cts.token).then( + handler => { + if (this._launchId === launchId) { + this._requestHandler.set(handler, undefined); + } else { + handler.dispose(); + } + }, + err => { + store.dispose(); + if (this._launchId === launchId) { + this._logger.error(err); + this._state.set({ state: McpConnectionState.Kind.Error, message: `Could not initialize MCP server: ${err.message}` }, undefined); + } + }, + ); + } + })); + + return { dispose: () => store.dispose(), object: launch }; + } + + public async stop(): Promise { + this._launchId = -1; + this._logger.info(localize('mcpServer.stopping', 'Stopping server {0}', this.definition.label)); + this._launch.value?.object.stop(); + await this._waitForState(McpConnectionState.Kind.Stopped, McpConnectionState.Kind.Error); + } + + public override dispose(): void { + this._launchId = -1; + this._requestHandler.get()?.dispose(); + super.dispose(); + this._state.set({ state: McpConnectionState.Kind.Stopped }, undefined); + } + + private _waitForState(...kinds: McpConnectionState.Kind[]): Promise { + const current = this._state.get(); + if (kinds.includes(current.state)) { + return Promise.resolve(current); + } + + return new Promise(resolve => { + const disposable = autorun(reader => { + const state = this._state.read(reader); + if (kinds.includes(state.state)) { + disposable.dispose(); + resolve(state); + } + }); + }); + } +} diff --git a/src/vs/workbench/contrib/mcp/common/mcpServerRequestHandler.ts b/src/vs/workbench/contrib/mcp/common/mcpServerRequestHandler.ts new file mode 100644 index 000000000000..651cb01d8cdc --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/mcpServerRequestHandler.ts @@ -0,0 +1,461 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { equals } from '../../../../base/common/arrays.js'; +import { DeferredPromise } from '../../../../base/common/async.js'; +import { CancellationToken } from '../../../../base/common/cancellation.js'; +import { Emitter } from '../../../../base/common/event.js'; +import { Disposable } from '../../../../base/common/lifecycle.js'; +import { autorun } from '../../../../base/common/observable.js'; +import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; +import { ILogger } from '../../../../platform/log/common/log.js'; +import { IProductService } from '../../../../platform/product/common/productService.js'; +import { IMcpMessageTransport } from './mcpRegistryTypes.js'; +import { McpConnectionState, MpcResponseError } from './mcpTypes.js'; +import { MCP } from './modelContextProtocol.js'; + +/** + * Maps request IDs to handlers. + */ +interface PendingRequest { + promise: DeferredPromise; +} + +export interface McpRoot { + uri: string; + name?: string; +} + +/** + * Request handler for communicating with an MCP server. + * + * Handles sending requests and receiving responses, with automatic + * handling of ping requests and typed client request methods. + */ +export class McpServerRequestHandler extends Disposable { + private _nextRequestId = 1; + private readonly _pendingRequests = new Map(); + + private _hasAnnouncedRoots = false; + private _roots: MCP.Root[] = []; + + public set roots(roots: MCP.Root[]) { + if (!equals(this._roots, roots)) { + this._roots = roots; + if (this._hasAnnouncedRoots) { + this.sendNotification({ method: 'notifications/roots/list_changed' }); + this._hasAnnouncedRoots = false; + } + } + } + + private _serverInit!: MCP.InitializeResult; + public get capabilities(): MCP.ServerCapabilities { + return this._serverInit.capabilities; + } + + // Event emitters for server notifications + private readonly _onDidReceiveCancelledNotification = this._register(new Emitter()); + readonly onDidReceiveCancelledNotification = this._onDidReceiveCancelledNotification.event; + + private readonly _onDidReceiveProgressNotification = this._register(new Emitter()); + readonly onDidReceiveProgressNotification = this._onDidReceiveProgressNotification.event; + + private readonly _onDidChangeResourceList = this._register(new Emitter()); + readonly onDidChangeResourceList = this._onDidChangeResourceList.event; + + private readonly _onDidUpdateResource = this._register(new Emitter()); + readonly onDidUpdateResource = this._onDidUpdateResource.event; + + private readonly _onDidChangeToolList = this._register(new Emitter()); + readonly onDidChangeToolList = this._onDidChangeToolList.event; + + private readonly _onDidChangePromptList = this._register(new Emitter()); + readonly onDidChangePromptList = this._onDidChangePromptList.event; + + /** + * Connects to the MCP server and does the initialization handshake. + * @throws MpcResponseError if the server fails to initialize. + */ + public static async create(instaService: IInstantiationService, launch: IMcpMessageTransport, logger: ILogger, token?: CancellationToken) { + const mcp = new McpServerRequestHandler(launch, logger); + try { + await instaService.invokeFunction(async accessor => { + const productService = accessor.get(IProductService); + const initialized = await mcp.sendRequest({ + method: 'initialize', + params: { + protocolVersion: MCP.LATEST_PROTOCOL_VERSION, + capabilities: { + roots: { listChanged: true }, + }, + clientInfo: { + name: productService.nameLong, + version: productService.version, + } + } + }, token); + + mcp._serverInit = initialized; + + mcp.sendNotification({ + method: 'notifications/initialized' + }); + }); + + return mcp; + } catch (e) { + mcp.dispose(); + throw e; + } + } + + protected constructor( + private readonly launch: IMcpMessageTransport, + private readonly logger: ILogger, + ) { + super(); + this._register(launch.onDidReceiveMessage(message => this.handleMessage(message))); + this._register(autorun(reader => { + const state = launch.state.read(reader).state; + // the handler will get disposed when the launch stops, but if we're still + // create()'ing we need to make sure to cancel the initialize request. + if (state === McpConnectionState.Kind.Error || state === McpConnectionState.Kind.Stopped) { + this.cancelAllRequests(); + } + })); + } + + /** + * Send a client request to the server and return the response. + * + * @param request The request to send + * @param token Cancellation token + * @param timeoutMs Optional timeout in milliseconds + * @returns A promise that resolves with the response + */ + private async sendRequest( + request: Pick, + token: CancellationToken = CancellationToken.None + ): Promise { + const id = this._nextRequestId++; + + // Create the full JSON-RPC request + const jsonRpcRequest: MCP.JSONRPCRequest = { + jsonrpc: MCP.JSONRPC_VERSION, + id, + ...request + }; + + const promise = new DeferredPromise(); + // Store the pending request + this._pendingRequests.set(id, { promise }); + + // Set up cancellation + const cancelListener = token.onCancellationRequested(() => { + if (!promise.isSettled) { + this._pendingRequests.delete(id); + this.sendNotification({ method: 'notifications/cancelled', params: { requestId: id } }); + promise.cancel(); + } + }); + + // Send the request + this.launch.send(jsonRpcRequest); + const ret = promise.p.finally(() => { + cancelListener.dispose(); + this._pendingRequests.delete(id); + }); + + return ret as Promise; + } + + /** + * Handles paginated requests by making multiple requests until all items are retrieved. + * + * @param method The method name to call + * @param getItems Function to extract the array of items from a result + * @param initialParams Initial parameters + * @param token Cancellation token + * @returns Promise with all items combined + */ + private async sendRequestPaginated(method: T['method'], getItems: (result: R) => I[], initialParams?: Omit, token: CancellationToken = CancellationToken.None): Promise { + let allItems: I[] = []; + let nextCursor: MCP.Cursor | undefined = undefined; + + do { + const params: T['params'] = { + ...initialParams, + cursor: nextCursor + }; + + const result: R = await this.sendRequest({ method, params }, token); + allItems = allItems.concat(getItems(result)); + nextCursor = result.nextCursor; + } while (nextCursor !== undefined && !token.isCancellationRequested); + + return allItems; + } + + private sendNotification(notification: N): void { + this.launch.send({ ...notification, jsonrpc: MCP.JSONRPC_VERSION }); + } + + /** + * Handle incoming messages from the server + */ + private handleMessage(message: MCP.JSONRPCMessage): void { + // Handle responses to our requests + if ('id' in message) { + if ('result' in message) { + this.handleResult(message); + } else if ('error' in message) { + this.handleError(message); + } + } + + // Handle requests from the server + if ('method' in message) { + if ('id' in message) { + this.handleServerRequest(message as MCP.JSONRPCRequest & MCP.ServerRequest); + } else { + this.handleServerNotification(message as MCP.JSONRPCNotification & MCP.ServerNotification); + + } + } + } + + /** + * Handle successful responses + */ + private handleResult(response: MCP.JSONRPCResponse): void { + const request = this._pendingRequests.get(response.id); + if (request) { + this._pendingRequests.delete(response.id); + request.promise.complete(response.result); + } + } + + /** + * Handle error responses + */ + private handleError(response: MCP.JSONRPCError): void { + const request = this._pendingRequests.get(response.id); + if (request) { + this._pendingRequests.delete(response.id); + request.promise.error(new MpcResponseError(response.error.message, response.error.code, response.error.data)); + } + } + + /** + * Handle incoming server requests + */ + private handleServerRequest(request: MCP.JSONRPCRequest & MCP.ServerRequest): void { + switch (request.method) { + case 'ping': + return this.respondToRequest(request, this.handlePing(request)); + case 'roots/list': + return this.respondToRequest(request, this.handleRootsList(request)); + + default: { + const errorResponse: MCP.JSONRPCError = { + jsonrpc: MCP.JSONRPC_VERSION, + id: request.id, + error: { + code: MCP.METHOD_NOT_FOUND, + message: `Method not found: ${request.method}` + } + }; + this.launch.send(errorResponse); + break; + } + } + } + /** + * Handle incoming server notifications + */ + private handleServerNotification(request: MCP.JSONRPCNotification & MCP.ServerNotification): void { + switch (request.method) { + case 'notifications/message': + return this.handleLoggingNotification(request); + case 'notifications/cancelled': + this._onDidReceiveCancelledNotification.fire(request); + return this.handleCancelledNotification(request); + case 'notifications/progress': + this._onDidReceiveProgressNotification.fire(request); + return; + case 'notifications/resources/list_changed': + this._onDidChangeResourceList.fire(); + return; + case 'notifications/resources/updated': + this._onDidUpdateResource.fire(request); + return; + case 'notifications/tools/list_changed': + this._onDidChangeToolList.fire(); + return; + case 'notifications/prompts/list_changed': + this._onDidChangePromptList.fire(); + return; + } + } + + private handleCancelledNotification(request: MCP.CancelledNotification): void { + const pendingRequest = this._pendingRequests.get(request.params.requestId); + if (pendingRequest) { + this._pendingRequests.delete(request.params.requestId); + pendingRequest.promise.cancel(); + } + } + + private handleLoggingNotification(request: MCP.LoggingMessageNotification): void { + let contents = typeof request.params.data === 'string' ? request.params.data : JSON.stringify(request.params.data); + if (request.params.logger) { + contents = `${request.params.logger}: ${contents}`; + } + + switch (request.params?.level) { + case 'debug': + this.logger.debug(contents); + break; + case 'info': + case 'notice': + this.logger.info(contents); + break; + case 'warning': + this.logger.warn(contents); + break; + case 'error': + case 'critical': + case 'alert': + case 'emergency': + this.logger.error(contents); + break; + default: + this.logger.info(contents); + break; + } + } + + /** + * Send a generic response to a request + */ + private respondToRequest(request: MCP.JSONRPCRequest, result: MCP.Result): void { + const response: MCP.JSONRPCResponse = { + jsonrpc: MCP.JSONRPC_VERSION, + id: request.id, + result + }; + this.launch.send(response); + } + + /** + * Send a response to a ping request + */ + private handlePing(_request: MCP.PingRequest): {} { + return {}; + } + + /** + * Send a response to a roots/list request + */ + private handleRootsList(_request: MCP.ListRootsRequest): MCP.ListRootsResult { + this._hasAnnouncedRoots = true; + return { roots: this._roots }; + } + + private cancelAllRequests() { + this._pendingRequests.forEach(pending => pending.promise.cancel()); + this._pendingRequests.clear(); + } + + public override dispose(): void { + this.cancelAllRequests(); + super.dispose(); + } + + /** + * Send an initialize request + */ + initialize(params: MCP.InitializeRequest['params'], token?: CancellationToken): Promise { + return this.sendRequest({ method: 'initialize', params }, token); + } + + /** + * List available resources + */ + listResources(params?: MCP.ListResourcesRequest['params'], token?: CancellationToken): Promise { + return this.sendRequestPaginated('resources/list', result => result.resources, params, token); + } + + /** + * Read a specific resource + */ + readResource(params: MCP.ReadResourceRequest['params'], token?: CancellationToken): Promise { + return this.sendRequest({ method: 'resources/read', params }, token); + } + + /** + * List available resource templates + */ + listResourceTemplates(params?: MCP.ListResourceTemplatesRequest['params'], token?: CancellationToken): Promise { + return this.sendRequestPaginated('resources/templates/list', result => result.resourceTemplates, params, token); + } + + /** + * Subscribe to resource updates + */ + subscribe(params: MCP.SubscribeRequest['params'], token?: CancellationToken): Promise { + return this.sendRequest({ method: 'resources/subscribe', params }, token); + } + + /** + * Unsubscribe from resource updates + */ + unsubscribe(params: MCP.UnsubscribeRequest['params'], token?: CancellationToken): Promise { + return this.sendRequest({ method: 'resources/unsubscribe', params }, token); + } + + /** + * List available prompts + */ + listPrompts(params?: MCP.ListPromptsRequest['params'], token?: CancellationToken): Promise { + return this.sendRequestPaginated('prompts/list', result => result.prompts, params, token); + } + + /** + * Get a specific prompt + */ + getPrompt(params: MCP.GetPromptRequest['params'], token?: CancellationToken): Promise { + return this.sendRequest({ method: 'prompts/get', params }, token); + } + + /** + * List available tools + */ + listTools(params?: MCP.ListToolsRequest['params'], token?: CancellationToken): Promise { + return this.sendRequestPaginated('tools/list', result => result.tools, params, token); + } + + /** + * Call a specific tool + */ + callTool(params: MCP.CallToolRequest['params'], token?: CancellationToken): Promise { + return this.sendRequest({ method: 'tools/call', params }, token); + } + + /** + * Set the logging level + */ + setLevel(params: MCP.SetLevelRequest['params'], token?: CancellationToken): Promise { + return this.sendRequest({ method: 'logging/setLevel', params }, token); + } + + /** + * Find completions for an argument + */ + complete(params: MCP.CompleteRequest['params'], token?: CancellationToken): Promise { + return this.sendRequest({ method: 'completion/complete', params }, token); + } +} diff --git a/src/vs/workbench/contrib/mcp/common/mcpService.ts b/src/vs/workbench/contrib/mcp/common/mcpService.ts new file mode 100644 index 000000000000..8def1af9e27b --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/mcpService.ts @@ -0,0 +1,250 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { RunOnceScheduler } from '../../../../base/common/async.js'; +import { CancellationToken } from '../../../../base/common/cancellation.js'; +import { MarkdownString } from '../../../../base/common/htmlContent.js'; +import { Disposable, DisposableStore, IDisposable, IReference, toDisposable } from '../../../../base/common/lifecycle.js'; +import { equals } from '../../../../base/common/objects.js'; +import { autorun, IObservable, observableValue, transaction } from '../../../../base/common/observable.js'; +import { localize } from '../../../../nls.js'; +import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; +import { ILogService } from '../../../../platform/log/common/log.js'; +import { IProductService } from '../../../../platform/product/common/productService.js'; +import { StorageScope } from '../../../../platform/storage/common/storage.js'; +import { CountTokensCallback, ILanguageModelToolsService, IPreparedToolInvocation, IToolData, IToolImpl, IToolInvocation, IToolResult } from '../../chat/common/languageModelToolsService.js'; +import { IMcpRegistry } from './mcpRegistryTypes.js'; +import { McpServer, McpServerMetadataCache } from './mcpServer.js'; +import { IMcpServer, IMcpService, IMcpTool, McpCollectionDefinition, McpServerDefinition, McpServerToolsState } from './mcpTypes.js'; + +interface ISyncedToolData { + toolData: IToolData; + toolDispose: IDisposable; + implDispose: IDisposable; +} + +type IMcpServerRec = IReference; + +export class McpService extends Disposable implements IMcpService { + + declare _serviceBrand: undefined; + + private readonly _servers = observableValue(this, []); + public readonly servers: IObservable = this._servers.map(servers => servers.map(s => s.object)); + + public get lazyCollectionState() { return this._mcpRegistry.lazyCollectionState; } + + protected readonly userCache: McpServerMetadataCache; + protected readonly workspaceCache: McpServerMetadataCache; + + constructor( + @IInstantiationService private readonly _instantiationService: IInstantiationService, + @IMcpRegistry private readonly _mcpRegistry: IMcpRegistry, + @ILanguageModelToolsService private readonly _toolsService: ILanguageModelToolsService, + @IProductService productService: IProductService, + @ILogService private readonly _logService: ILogService, + ) { + super(); + + this.userCache = this._register(_instantiationService.createInstance(McpServerMetadataCache, StorageScope.PROFILE)); + this.workspaceCache = this._register(_instantiationService.createInstance(McpServerMetadataCache, StorageScope.WORKSPACE)); + + const updateThrottle = this._store.add(new RunOnceScheduler(() => this._updateCollectedServers(), 500)); + + // Throttle changes so that if a collection is changed, or a server is + // unregistered/registered, we don't stop servers unnecessarily. + this._register(autorun(reader => { + for (const collection of this._mcpRegistry.collections.read(reader)) { + collection.serverDefinitions.read(reader); + } + updateThrottle.schedule(500); + })); + } + + public resetCaches(): void { + this.userCache.reset(); + this.workspaceCache.reset(); + } + + public async activateCollections(): Promise { + const collections = await this._mcpRegistry.discoverCollections(); + const collectionIds = new Set(collections.map(c => c.id)); + + this._updateCollectedServers(); + + // Discover any newly-collected servers with unknown tools + const todo: Promise[] = []; + for (const { object: server } of this._servers.get()) { + if (collectionIds.has(server.collection.id)) { + const state = server.toolsState.get(); + if (state === McpServerToolsState.Unknown) { + todo.push(server.start()); + } + } + } + + await Promise.all(todo); + } + + private _syncTools(server: McpServer, store: DisposableStore) { + const tools = new Map(); + + store.add(autorun(reader => { + const toDelete = new Set(tools.keys()); + for (const tool of server.tools.read(reader)) { + const existing = tools.get(tool.id); + const toolData: IToolData = { + id: tool.id, + displayName: tool.definition.name, + toolReferenceName: tool.definition.name, + modelDescription: tool.definition.description ?? '', + userDescription: tool.definition.description ?? '', + inputSchema: tool.definition.inputSchema, + canBeReferencedInPrompt: true, + tags: ['mcp', 'vscode_editing'], // TODO@jrieken remove this tag + }; + + if (existing) { + if (!equals(existing.toolData, toolData)) { + existing.toolData = toolData; + existing.toolDispose.dispose(); + existing.toolDispose = this._toolsService.registerToolData(toolData); + } + toDelete.delete(tool.id); + } else { + tools.set(tool.id, { + toolData, + toolDispose: this._toolsService.registerToolData(toolData), + implDispose: this._toolsService.registerToolImplementation(tool.id, this._instantiationService.createInstance(McpToolImplementation, tool, server)), + }); + } + } + + for (const id of toDelete) { + const tool = tools.get(id); + if (tool) { + tool.toolDispose.dispose(); + tool.implDispose.dispose(); + tools.delete(id); + } + } + })); + + store.add(toDisposable(() => { + for (const tool of tools.values()) { + tool.toolDispose.dispose(); + tool.implDispose.dispose(); + } + })); + } + + private _updateCollectedServers() { + const definitions = this._mcpRegistry.collections.get().flatMap(collectionDefinition => + collectionDefinition.serverDefinitions.get().map(serverDefinition => ({ + serverDefinition, + collectionDefinition, + })) + ); + + const nextDefinitions = new Set(definitions); + const currentServers = this._servers.get(); + const nextServers: IMcpServerRec[] = []; + const pushMatch = (match: (typeof definitions)[0], rec: IMcpServerRec) => { + nextDefinitions.delete(match); + nextServers.push(rec); + const connection = rec.object.connection.get(); + // if the definition was modified, stop the server; it'll be restarted again on-demand + if (connection && !McpServerDefinition.equals(connection.definition, match.serverDefinition)) { + rec.object.stop(); + this._logService.debug(`MCP server ${rec.object.definition.id} stopped because the definition changed`); + } + }; + + // Transfer over any servers that are still valid. + for (const server of currentServers) { + const match = definitions.find(d => defsEqual(server.object, d)); + if (match) { + pushMatch(match, server); + } else { + server.dispose(); + } + } + + // Create any new servers that are needed. + for (const def of nextDefinitions) { + const store = new DisposableStore(); + const object = this._instantiationService.createInstance(McpServer, def.collectionDefinition, def.serverDefinition, false, def.collectionDefinition.scope === StorageScope.WORKSPACE ? this.workspaceCache : this.userCache); + store.add(object); + this._syncTools(object, store); + + nextServers.push({ object, dispose: () => store.dispose() }); + } + + transaction(tx => { + this._servers.set(nextServers, tx); + }); + } + + public override dispose(): void { + this._servers.get().forEach(s => s.dispose()); + super.dispose(); + } +} + +function defsEqual(server: IMcpServer, def: { serverDefinition: McpServerDefinition; collectionDefinition: McpCollectionDefinition }) { + return server.collection.id === def.collectionDefinition.id && server.definition.id === def.serverDefinition.id; +} + +class McpToolImplementation implements IToolImpl { + constructor( + private readonly _tool: IMcpTool, + private readonly _server: IMcpServer, + @IProductService private readonly _productService: IProductService, + ) { } + + async prepareToolInvocation(parameters: any, token: CancellationToken) { + const tool = this._tool; + const server = this._server; + + const mcpToolWarning = localize( + 'mcp.tool.warning', + "MCP servers or malicious conversation content may attempt to misuse '{0}' through the installed tools. Please carefully review any requested actions.", + this._productService.nameShort + ); + + return { + confirmationMessages: { + title: localize('msg.title', "Run `{0}` from $(server) `{1}` (MCP server)", tool.definition.name, server.definition.label), + message: new MarkdownString(localize('msg.msg', "{0}\n\n$(warning) {1}", tool.definition.description, mcpToolWarning), { supportThemeIcons: true }), + toolInput: parameters + }, + invocationMessage: new MarkdownString(localize('msg.run', "Running `{0}`", tool.definition.name, server.definition.label)), + pastTenseMessage: new MarkdownString(localize('msg.ran', "Ran `{0}` ", tool.definition.name, server.definition.label)) + } satisfies IPreparedToolInvocation; + } + + async invoke(invocation: IToolInvocation, _countTokens: CountTokensCallback, token: CancellationToken) { + + const result: IToolResult = { + content: [] + }; + + const callResult = await this._tool.call(invocation.parameters as Record, token); + for (const item of callResult.content) { + if (item.type === 'text') { + result.content.push({ + kind: 'text', + value: item.text + }); + } else { + // TODO@jrieken handle different item types + } + } + + // result.toolResultMessage = new MarkdownString(localize('reuslt.pattern', "```json\n{0}\n```", JSON.stringify(callResult, undefined, 2))); + + return result; + } +} diff --git a/src/vs/workbench/contrib/mcp/common/mcpTypes.ts b/src/vs/workbench/contrib/mcp/common/mcpTypes.ts new file mode 100644 index 000000000000..db12df70ab62 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/mcpTypes.ts @@ -0,0 +1,384 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { assertNever } from '../../../../base/common/assert.js'; +import { CancellationToken } from '../../../../base/common/cancellation.js'; +import { IDisposable } from '../../../../base/common/lifecycle.js'; +import { equals as objectsEqual } from '../../../../base/common/objects.js'; +import { IObservable } from '../../../../base/common/observable.js'; +import { URI, UriComponents } from '../../../../base/common/uri.js'; +import { localize } from '../../../../nls.js'; +import { ConfigurationTarget } from '../../../../platform/configuration/common/configuration.js'; +import { ExtensionIdentifier } from '../../../../platform/extensions/common/extensions.js'; +import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; +import { StorageScope } from '../../../../platform/storage/common/storage.js'; +import { IWorkspaceFolderData } from '../../../../platform/workspace/common/workspace.js'; +import { McpServerRequestHandler } from './mcpServerRequestHandler.js'; +import { MCP } from './modelContextProtocol.js'; + +export const extensionMcpCollectionPrefix = 'ext.'; + +export function extensionPrefixedIdentifier(identifier: ExtensionIdentifier, id: string): string { + return ExtensionIdentifier.toKey(identifier) + '/' + id; +} + +/** + * An McpCollection contains McpServers. There may be multiple collections for + * different locations servers are discovered. + */ +export interface McpCollectionDefinition { + /** Origin authority from which this collection was discovered. */ + readonly remoteAuthority: string | null; + /** Globally-unique, stable ID for this definition */ + readonly id: string; + /** Human-readable label for the definition */ + readonly label: string; + /** Definitions this collection contains. */ + readonly serverDefinitions: IObservable; + /** If 'false', consent is required before any MCP servers in this collection are automatically launched. */ + readonly isTrustedByDefault: boolean; + /** Scope where associated collection info should be stored. */ + readonly scope: StorageScope; + + /** For lazy-loaded collections only: */ + readonly lazy?: { + /** True if `serverDefinitions` were loaded from the cache */ + isCached: boolean; + /** Triggers a load of the real server definition, which should be pushed to the IMcpRegistry. If not this definition will be removed. */ + load(): Promise; + /** Called after `load()` if the extension is not found. */ + removed?(): void; + }; + + readonly presentation?: { + /** Sort order of the collection. */ + readonly order?: number; + /** Place where this server is configured, used in workspac trust prompts */ + readonly origin?: URI; + }; +} + +export const enum McpCollectionSortOrder { + Workspace = 0, + User = 100, + Extension = 200, + Filesystem = 300, + + RemotePenalty = 50, +} + +export namespace McpCollectionDefinition { + export interface FromExtHost { + readonly id: string; + readonly label: string; + readonly isTrustedByDefault: boolean; + readonly scope: StorageScope; + } + + export function equals(a: McpCollectionDefinition, b: McpCollectionDefinition): boolean { + return a.id === b.id + && a.remoteAuthority === b.remoteAuthority + && a.label === b.label + && a.isTrustedByDefault === b.isTrustedByDefault; + } +} + +export interface McpServerDefinition { + /** Globally-unique, stable ID for this definition */ + readonly id: string; + /** Human-readable label for the definition */ + readonly label: string; + /** Descriptor defining how the configuration should be launched. */ + readonly launch: McpServerLaunch; + /** If set, allows configuration variables to be resolved in the {@link launch} with the given context */ + readonly variableReplacement?: McpServerDefinitionVariableReplacement; +} + +export namespace McpServerDefinition { + export interface Serialized { + readonly id: string; + readonly label: string; + readonly launch: McpServerLaunch.Serialized; + readonly variableReplacement?: McpServerDefinitionVariableReplacement.Serialized; + } + + export function toSerialized(def: McpServerDefinition): McpServerDefinition.Serialized { + return def; + } + + export function fromSerialized(def: McpServerDefinition.Serialized): McpServerDefinition { + return { + id: def.id, + label: def.label, + launch: McpServerLaunch.fromSerialized(def.launch), + variableReplacement: def.variableReplacement ? McpServerDefinitionVariableReplacement.fromSerialized(def.variableReplacement) : undefined, + }; + } + + export function equals(a: McpServerDefinition, b: McpServerDefinition): boolean { + return a.id === b.id + && a.label === b.label + && objectsEqual(a.launch, b.launch) + && objectsEqual(a.variableReplacement, b.variableReplacement); + } +} + + +export interface McpServerDefinitionVariableReplacement { + section?: string; // e.g. 'mcp' + folder?: IWorkspaceFolderData; + target?: ConfigurationTarget; +} + +export namespace McpServerDefinitionVariableReplacement { + export interface Serialized { + section?: string; + folder?: { name: string; index: number; uri: UriComponents }; + target?: ConfigurationTarget; + } + + export function toSerialized(def: McpServerDefinitionVariableReplacement): McpServerDefinitionVariableReplacement.Serialized { + return def; + } + + export function fromSerialized(def: McpServerDefinitionVariableReplacement.Serialized): McpServerDefinitionVariableReplacement { + return { + section: def.section, + folder: def.folder ? { ...def.folder, uri: URI.revive(def.folder.uri) } : undefined, + target: def.target, + }; + } +} + +export interface IMcpService { + _serviceBrand: undefined; + readonly servers: IObservable; + + /** Resets the cached tools. */ + resetCaches(): void; + + /** Set if there are extensions that register MCP servers that have never been activated. */ + readonly lazyCollectionState: IObservable; + /** Activatese extensions and runs their MCP servers. */ + activateCollections(): Promise; +} + +export const enum LazyCollectionState { + HasUnknown, + LoadingUnknown, + AllKnown, +} + +export const IMcpService = createDecorator('IMcpService'); + +export interface McpCollectionReference { + id: string; + label: string; + presentation?: McpCollectionDefinition['presentation']; +} + +export interface McpDefinitionReference { + id: string; + label: string; +} + +export interface IMcpServer extends IDisposable { + readonly collection: McpCollectionReference; + readonly definition: McpDefinitionReference; + readonly connection: IObservable; + readonly connectionState: IObservable; + /** + * Reflects the MCP server trust state. True if trusted, false if untrusted, + * undefined if consent is required but not indicated. + */ + readonly trusted: IObservable; + + showOutput(): void; + /** + * Starts the server and returns its resulting state. One of: + * - Running, if all good + * - Error, if the server failed to start + * - Stopped, if the server was disposed or the user cancelled the launch + */ + start(isFromInteraction?: boolean): Promise; + stop(): Promise; + + readonly toolsState: IObservable; + readonly tools: IObservable; +} + +export const enum McpServerToolsState { + /** Tools have not been read before */ + Unknown, + /** Tools were read from the cache */ + Cached, + /** Tools are refreshing for the first time */ + RefreshingFromUnknown, + /** Tools are refreshing and the current tools are cached */ + RefreshingFromCached, + /** Tool state is live, server is connected */ + Live, +} + +export interface IMcpTool { + + readonly id: string; + + readonly definition: MCP.Tool; + + /** + * Calls a tool + * @throws {@link MpcResponseError} if the tool fails to execute + * @throws {@link McpConnectionFailedError} if the connection to the server fails + */ + call(params: Record, token?: CancellationToken): Promise; +} + +export const enum McpServerTransportType { + /** A command-line MCP server communicating over standard in/out */ + Stdio = 1 << 0, + /** An MCP server that uses Server-Sent Events */ + SSE = 1 << 1, +} + +/** + * MCP server launched on the command line which communicated over stdio. + * https://spec.modelcontextprotocol.io/specification/2024-11-05/basic/transports/#stdio + */ +export interface McpServerTransportStdio { + readonly type: McpServerTransportType.Stdio; + readonly cwd: URI | undefined; + readonly command: string; + readonly args: readonly string[]; + readonly env: Record; +} + +/** + * MCP server launched on the command line which communicated over server-sent-events. + * https://spec.modelcontextprotocol.io/specification/2024-11-05/basic/transports/#http-with-sse + */ +export interface McpServerTransportSSE { + readonly type: McpServerTransportType.SSE; + readonly uri: URI; + readonly headers: [string, string][]; +} + +export type McpServerLaunch = + | McpServerTransportStdio + | McpServerTransportSSE; + +export namespace McpServerLaunch { + export type Serialized = + | { type: McpServerTransportType.SSE; uri: UriComponents; headers: [string, string][] } + | { type: McpServerTransportType.Stdio; cwd: UriComponents | undefined; command: string; args: readonly string[]; env: Record }; + + export function toSerialized(launch: McpServerLaunch): McpServerLaunch.Serialized { + return launch; + } + + export function fromSerialized(launch: McpServerLaunch.Serialized): McpServerLaunch { + switch (launch.type) { + case McpServerTransportType.SSE: + return { type: launch.type, uri: URI.revive(launch.uri), headers: launch.headers }; + case McpServerTransportType.Stdio: + return { + type: launch.type, + cwd: launch.cwd ? URI.revive(launch.cwd) : undefined, + command: launch.command, + args: launch.args, + env: launch.env, + }; + } + } +} + +/** + * An instance that manages a connection to an MCP server. It can be started, + * stopped, and restarted. Once started and in a running state, it will + * eventually build a {@link IMcpServerConnection.handler}. + */ +export interface IMcpServerConnection extends IDisposable { + readonly definition: McpServerDefinition; + readonly state: IObservable; + readonly handler: IObservable; + + /** + * Shows the current server output. + */ + showOutput(): void; + + /** + * Starts the server if it's stopped. Returns a promise that resolves once + * server exits a 'starting' state. + */ + start(): Promise; + + /** + * Stops the server. + */ + stop(): Promise; +} + +/** + * McpConnectionState is the state of the underlying connection and is + * communicated e.g. from the extension host to the renderer. + */ +export namespace McpConnectionState { + export const enum Kind { + Stopped, + Starting, + Running, + Error, + } + + export const toString = (s: McpConnectionState): string => { + switch (s.state) { + case Kind.Stopped: + return localize('mcpstate.stopped', 'Stopped'); + case Kind.Starting: + return localize('mcpstate.starting', 'Starting'); + case Kind.Running: + return localize('mcpstate.running', 'Running'); + case Kind.Error: + return localize('mcpstate.error', 'Error {0}', s.message); + default: + assertNever(s); + } + }; + + /** Returns if the MCP state is one where starting a new server is valid */ + export const canBeStarted = (s: Kind) => s === Kind.Error || s === Kind.Stopped; + + export interface Stopped { + readonly state: Kind.Stopped; + } + + export interface Starting { + readonly state: Kind.Starting; + } + + export interface Running { + readonly state: Kind.Running; + } + + export interface Error { + readonly state: Kind.Error; + readonly message: string; + } +} + +export type McpConnectionState = + | McpConnectionState.Stopped + | McpConnectionState.Starting + | McpConnectionState.Running + | McpConnectionState.Error; + +export class MpcResponseError extends Error { + constructor(message: string, public readonly code: number, public readonly data: unknown) { + super(`MPC ${code}: ${message}`); + } +} + +export class McpConnectionFailedError extends Error { } diff --git a/src/vs/workbench/contrib/mcp/common/modelContextProtocol.ts b/src/vs/workbench/contrib/mcp/common/modelContextProtocol.ts new file mode 100644 index 000000000000..582e69eaf930 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/common/modelContextProtocol.ts @@ -0,0 +1,1138 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +/* eslint-disable @stylistic/ts/member-delimiter-style */ +/* eslint-disable local/code-no-unexternalized-strings */ + +/** + * Schema updated from the Model Context Protocol repository at + * https://github.com/modelcontextprotocol/specification/tree/main/schema + * + * ⚠️ Do not edit within `namespace` manually except to update schema versions ⚠️ + */ +export namespace MCP { + /* JSON-RPC types */ + export type JSONRPCMessage = + | JSONRPCRequest + | JSONRPCNotification + | JSONRPCResponse + | JSONRPCError; + + export const LATEST_PROTOCOL_VERSION = "2024-11-05"; + export const JSONRPC_VERSION = "2.0"; + + /** + * A progress token, used to associate progress notifications with the original request. + */ + export type ProgressToken = string | number; + + /** + * An opaque token used to represent a cursor for pagination. + */ + export type Cursor = string; + + export interface Request { + method: string; + params?: { + _meta?: { + /** + * If specified, the caller is requesting out-of-band progress notifications for this request (as represented by notifications/progress). The value of this parameter is an opaque token that will be attached to any subsequent notifications. The receiver is not obligated to provide these notifications. + */ + progressToken?: ProgressToken; + }; + [key: string]: unknown; + }; + } + + export interface Notification { + method: string; + params?: { + /** + * This parameter name is reserved by MCP to allow clients and servers to attach additional metadata to their notifications. + */ + _meta?: { [key: string]: unknown }; + [key: string]: unknown; + }; + } + + export interface Result { + /** + * This result property is reserved by the protocol to allow clients and servers to attach additional metadata to their responses. + */ + _meta?: { [key: string]: unknown }; + [key: string]: unknown; + } + + /** + * A uniquely identifying ID for a request in JSON-RPC. + */ + export type RequestId = string | number; + + /** + * A request that expects a response. + */ + export interface JSONRPCRequest extends Request { + jsonrpc: typeof JSONRPC_VERSION; + id: RequestId; + } + + /** + * A notification which does not expect a response. + */ + export interface JSONRPCNotification extends Notification { + jsonrpc: typeof JSONRPC_VERSION; + } + + /** + * A successful (non-error) response to a request. + */ + export interface JSONRPCResponse { + jsonrpc: typeof JSONRPC_VERSION; + id: RequestId; + result: Result; + } + + // Standard JSON-RPC error codes + export const PARSE_ERROR = -32700; + export const INVALID_REQUEST = -32600; + export const METHOD_NOT_FOUND = -32601; + export const INVALID_PARAMS = -32602; + export const INTERNAL_ERROR = -32603; + + /** + * A response to a request that indicates an error occurred. + */ + export interface JSONRPCError { + jsonrpc: typeof JSONRPC_VERSION; + id: RequestId; + error: { + /** + * The error type that occurred. + */ + code: number; + /** + * A short description of the error. The message SHOULD be limited to a concise single sentence. + */ + message: string; + /** + * Additional information about the error. The value of this member is defined by the sender (e.g. detailed error information, nested errors etc.). + */ + data?: unknown; + }; + } + + /* Empty result */ + /** + * A response that indicates success but carries no data. + */ + export type EmptyResult = Result; + + /* Cancellation */ + /** + * This notification can be sent by either side to indicate that it is cancelling a previously-issued request. + * + * The request SHOULD still be in-flight, but due to communication latency, it is always possible that this notification MAY arrive after the request has already finished. + * + * This notification indicates that the result will be unused, so any associated processing SHOULD cease. + * + * A client MUST NOT attempt to cancel its `initialize` request. + */ + export interface CancelledNotification extends Notification { + method: "notifications/cancelled"; + params: { + /** + * The ID of the request to cancel. + * + * This MUST correspond to the ID of a request previously issued in the same direction. + */ + requestId: RequestId; + + /** + * An optional string describing the reason for the cancellation. This MAY be logged or presented to the user. + */ + reason?: string; + }; + } + + /* Initialization */ + /** + * This request is sent from the client to the server when it first connects, asking it to begin initialization. + */ + export interface InitializeRequest extends Request { + method: "initialize"; + params: { + /** + * The latest version of the Model Context Protocol that the client supports. The client MAY decide to support older versions as well. + */ + protocolVersion: string; + capabilities: ClientCapabilities; + clientInfo: Implementation; + }; + } + + /** + * After receiving an initialize request from the client, the server sends this response. + */ + export interface InitializeResult extends Result { + /** + * The version of the Model Context Protocol that the server wants to use. This may not match the version that the client requested. If the client cannot support this version, it MUST disconnect. + */ + protocolVersion: string; + capabilities: ServerCapabilities; + serverInfo: Implementation; + /** + * Instructions describing how to use the server and its features. + * + * This can be used by clients to improve the LLM's understanding of available tools, resources, etc. It can be thought of like a "hint" to the model. For example, this information MAY be added to the system prompt. + */ + instructions?: string; + } + + /** + * This notification is sent from the client to the server after initialization has finished. + */ + export interface InitializedNotification extends Notification { + method: "notifications/initialized"; + } + + /** + * Capabilities a client may support. Known capabilities are defined here, in this schema, but this is not a closed set: any client can define its own, additional capabilities. + */ + export interface ClientCapabilities { + /** + * Experimental, non-standard capabilities that the client supports. + */ + experimental?: { [key: string]: object }; + /** + * Present if the client supports listing roots. + */ + roots?: { + /** + * Whether the client supports notifications for changes to the roots list. + */ + listChanged?: boolean; + }; + /** + * Present if the client supports sampling from an LLM. + */ + sampling?: object; + } + + /** + * Capabilities that a server may support. Known capabilities are defined here, in this schema, but this is not a closed set: any server can define its own, additional capabilities. + */ + export interface ServerCapabilities { + /** + * Experimental, non-standard capabilities that the server supports. + */ + experimental?: { [key: string]: object }; + /** + * Present if the server supports sending log messages to the client. + */ + logging?: object; + /** + * Present if the server offers any prompt templates. + */ + prompts?: { + /** + * Whether this server supports notifications for changes to the prompt list. + */ + listChanged?: boolean; + }; + /** + * Present if the server offers any resources to read. + */ + resources?: { + /** + * Whether this server supports subscribing to resource updates. + */ + subscribe?: boolean; + /** + * Whether this server supports notifications for changes to the resource list. + */ + listChanged?: boolean; + }; + /** + * Present if the server offers any tools to call. + */ + tools?: { + /** + * Whether this server supports notifications for changes to the tool list. + */ + listChanged?: boolean; + }; + } + + /** + * Describes the name and version of an MCP implementation. + */ + export interface Implementation { + name: string; + version: string; + } + + /* Ping */ + /** + * A ping, issued by either the server or the client, to check that the other party is still alive. The receiver must promptly respond, or else may be disconnected. + */ + export interface PingRequest extends Request { + method: "ping"; + } + + /* Progress notifications */ + /** + * An out-of-band notification used to inform the receiver of a progress update for a long-running request. + */ + export interface ProgressNotification extends Notification { + method: "notifications/progress"; + params: { + /** + * The progress token which was given in the initial request, used to associate this notification with the request that is proceeding. + */ + progressToken: ProgressToken; + /** + * The progress thus far. This should increase every time progress is made, even if the total is unknown. + * + * @TJS-type number + */ + progress: number; + /** + * Total number of items to process (or total progress required), if known. + * + * @TJS-type number + */ + total?: number; + }; + } + + /* Pagination */ + export interface PaginatedRequest extends Request { + params?: { + /** + * An opaque token representing the current pagination position. + * If provided, the server should return results starting after this cursor. + */ + cursor?: Cursor; + }; + } + + export interface PaginatedResult extends Result { + /** + * An opaque token representing the pagination position after the last returned result. + * If present, there may be more results available. + */ + nextCursor?: Cursor; + } + + /* Resources */ + /** + * Sent from the client to request a list of resources the server has. + */ + export interface ListResourcesRequest extends PaginatedRequest { + method: "resources/list"; + } + + /** + * The server's response to a resources/list request from the client. + */ + export interface ListResourcesResult extends PaginatedResult { + resources: Resource[]; + } + + /** + * Sent from the client to request a list of resource templates the server has. + */ + export interface ListResourceTemplatesRequest extends PaginatedRequest { + method: "resources/templates/list"; + } + + /** + * The server's response to a resources/templates/list request from the client. + */ + export interface ListResourceTemplatesResult extends PaginatedResult { + resourceTemplates: ResourceTemplate[]; + } + + /** + * Sent from the client to the server, to read a specific resource URI. + */ + export interface ReadResourceRequest extends Request { + method: "resources/read"; + params: { + /** + * The URI of the resource to read. The URI can use any protocol; it is up to the server how to interpret it. + * + * @format uri + */ + uri: string; + }; + } + + /** + * The server's response to a resources/read request from the client. + */ + export interface ReadResourceResult extends Result { + contents: (TextResourceContents | BlobResourceContents)[]; + } + + /** + * An optional notification from the server to the client, informing it that the list of resources it can read from has changed. This may be issued by servers without any previous subscription from the client. + */ + export interface ResourceListChangedNotification extends Notification { + method: "notifications/resources/list_changed"; + } + + /** + * Sent from the client to request resources/updated notifications from the server whenever a particular resource changes. + */ + export interface SubscribeRequest extends Request { + method: "resources/subscribe"; + params: { + /** + * The URI of the resource to subscribe to. The URI can use any protocol; it is up to the server how to interpret it. + * + * @format uri + */ + uri: string; + }; + } + + /** + * Sent from the client to request cancellation of resources/updated notifications from the server. This should follow a previous resources/subscribe request. + */ + export interface UnsubscribeRequest extends Request { + method: "resources/unsubscribe"; + params: { + /** + * The URI of the resource to unsubscribe from. + * + * @format uri + */ + uri: string; + }; + } + + /** + * A notification from the server to the client, informing it that a resource has changed and may need to be read again. This should only be sent if the client previously sent a resources/subscribe request. + */ + export interface ResourceUpdatedNotification extends Notification { + method: "notifications/resources/updated"; + params: { + /** + * The URI of the resource that has been updated. This might be a sub-resource of the one that the client actually subscribed to. + * + * @format uri + */ + uri: string; + }; + } + + /** + * A known resource that the server is capable of reading. + */ + export interface Resource extends Annotated { + /** + * The URI of this resource. + * + * @format uri + */ + uri: string; + + /** + * A human-readable name for this resource. + * + * This can be used by clients to populate UI elements. + */ + name: string; + + /** + * A description of what this resource represents. + * + * This can be used by clients to improve the LLM's understanding of available resources. It can be thought of like a "hint" to the model. + */ + description?: string; + + /** + * The MIME type of this resource, if known. + */ + mimeType?: string; + + /** + * The size of the raw resource content, in bytes (i.e., before base64 encoding or any tokenization), if known. + * + * This can be used by Hosts to display file sizes and estimate context window usage. + */ + size?: number; + } + + /** + * A template description for resources available on the server. + */ + export interface ResourceTemplate extends Annotated { + /** + * A URI template (according to RFC 6570) that can be used to construct resource URIs. + * + * @format uri-template + */ + uriTemplate: string; + + /** + * A human-readable name for the type of resource this template refers to. + * + * This can be used by clients to populate UI elements. + */ + name: string; + + /** + * A description of what this template is for. + * + * This can be used by clients to improve the LLM's understanding of available resources. It can be thought of like a "hint" to the model. + */ + description?: string; + + /** + * The MIME type for all resources that match this template. This should only be included if all resources matching this template have the same type. + */ + mimeType?: string; + } + + /** + * The contents of a specific resource or sub-resource. + */ + export interface ResourceContents { + /** + * The URI of this resource. + * + * @format uri + */ + uri: string; + /** + * The MIME type of this resource, if known. + */ + mimeType?: string; + } + + export interface TextResourceContents extends ResourceContents { + /** + * The text of the item. This must only be set if the item can actually be represented as text (not binary data). + */ + text: string; + } + + export interface BlobResourceContents extends ResourceContents { + /** + * A base64-encoded string representing the binary data of the item. + * + * @format byte + */ + blob: string; + } + + /* Prompts */ + /** + * Sent from the client to request a list of prompts and prompt templates the server has. + */ + export interface ListPromptsRequest extends PaginatedRequest { + method: "prompts/list"; + } + + /** + * The server's response to a prompts/list request from the client. + */ + export interface ListPromptsResult extends PaginatedResult { + prompts: Prompt[]; + } + + /** + * Used by the client to get a prompt provided by the server. + */ + export interface GetPromptRequest extends Request { + method: "prompts/get"; + params: { + /** + * The name of the prompt or prompt template. + */ + name: string; + /** + * Arguments to use for templating the prompt. + */ + arguments?: { [key: string]: string }; + }; + } + + /** + * The server's response to a prompts/get request from the client. + */ + export interface GetPromptResult extends Result { + /** + * An optional description for the prompt. + */ + description?: string; + messages: PromptMessage[]; + } + + /** + * A prompt or prompt template that the server offers. + */ + export interface Prompt { + /** + * The name of the prompt or prompt template. + */ + name: string; + /** + * An optional description of what this prompt provides + */ + description?: string; + /** + * A list of arguments to use for templating the prompt. + */ + arguments?: PromptArgument[]; + } + + /** + * Describes an argument that a prompt can accept. + */ + export interface PromptArgument { + /** + * The name of the argument. + */ + name: string; + /** + * A human-readable description of the argument. + */ + description?: string; + /** + * Whether this argument must be provided. + */ + required?: boolean; + } + + /** + * The sender or recipient of messages and data in a conversation. + */ + export type Role = "user" | "assistant"; + + /** + * Describes a message returned as part of a prompt. + * + * This is similar to `SamplingMessage`, but also supports the embedding of + * resources from the MCP server. + */ + export interface PromptMessage { + role: Role; + content: TextContent | ImageContent | EmbeddedResource; + } + + /** + * The contents of a resource, embedded into a prompt or tool call result. + * + * It is up to the client how best to render embedded resources for the benefit + * of the LLM and/or the user. + */ + export interface EmbeddedResource extends Annotated { + type: "resource"; + resource: TextResourceContents | BlobResourceContents; + } + + /** + * An optional notification from the server to the client, informing it that the list of prompts it offers has changed. This may be issued by servers without any previous subscription from the client. + */ + export interface PromptListChangedNotification extends Notification { + method: "notifications/prompts/list_changed"; + } + + /* Tools */ + /** + * Sent from the client to request a list of tools the server has. + */ + export interface ListToolsRequest extends PaginatedRequest { + method: "tools/list"; + } + + /** + * The server's response to a tools/list request from the client. + */ + export interface ListToolsResult extends PaginatedResult { + tools: Tool[]; + } + + /** + * The server's response to a tool call. + * + * Any errors that originate from the tool SHOULD be reported inside the result + * object, with `isError` set to true, _not_ as an MCP protocol-level error + * response. Otherwise, the LLM would not be able to see that an error occurred + * and self-correct. + * + * However, any errors in _finding_ the tool, an error indicating that the + * server does not support tool calls, or any other exceptional conditions, + * should be reported as an MCP error response. + */ + export interface CallToolResult extends Result { + content: (TextContent | ImageContent | EmbeddedResource)[]; + + /** + * Whether the tool call ended in an error. + * + * If not set, this is assumed to be false (the call was successful). + */ + isError?: boolean; + } + + /** + * Used by the client to invoke a tool provided by the server. + */ + export interface CallToolRequest extends Request { + method: "tools/call"; + params: { + name: string; + arguments?: { [key: string]: unknown }; + }; + } + + /** + * An optional notification from the server to the client, informing it that the list of tools it offers has changed. This may be issued by servers without any previous subscription from the client. + */ + export interface ToolListChangedNotification extends Notification { + method: "notifications/tools/list_changed"; + } + + /** + * Definition for a tool the client can call. + */ + export interface Tool { + /** + * The name of the tool. + */ + name: string; + /** + * A human-readable description of the tool. + */ + description?: string; + /** + * A JSON Schema object defining the expected parameters for the tool. + */ + inputSchema: { + type: "object"; + properties?: { [key: string]: object }; + required?: string[]; + }; + } + + /* Logging */ + /** + * A request from the client to the server, to enable or adjust logging. + */ + export interface SetLevelRequest extends Request { + method: "logging/setLevel"; + params: { + /** + * The level of logging that the client wants to receive from the server. The server should send all logs at this level and higher (i.e., more severe) to the client as notifications/message. + */ + level: LoggingLevel; + }; + } + + /** + * Notification of a log message passed from server to client. If no logging/setLevel request has been sent from the client, the server MAY decide which messages to send automatically. + */ + export interface LoggingMessageNotification extends Notification { + method: "notifications/message"; + params: { + /** + * The severity of this log message. + */ + level: LoggingLevel; + /** + * An optional name of the logger issuing this message. + */ + logger?: string; + /** + * The data to be logged, such as a string message or an object. Any JSON serializable type is allowed here. + */ + data: unknown; + }; + } + + /** + * The severity of a log message. + * + * These map to syslog message severities, as specified in RFC-5424: + * https://datatracker.ietf.org/doc/html/rfc5424#section-6.2.1 + */ + export type LoggingLevel = + | "debug" + | "info" + | "notice" + | "warning" + | "error" + | "critical" + | "alert" + | "emergency"; + + /* Sampling */ + /** + * A request from the server to sample an LLM via the client. The client has full discretion over which model to select. The client should also inform the user before beginning sampling, to allow them to inspect the request (human in the loop) and decide whether to approve it. + */ + export interface CreateMessageRequest extends Request { + method: "sampling/createMessage"; + params: { + messages: SamplingMessage[]; + /** + * The server's preferences for which model to select. The client MAY ignore these preferences. + */ + modelPreferences?: ModelPreferences; + /** + * An optional system prompt the server wants to use for sampling. The client MAY modify or omit this prompt. + */ + systemPrompt?: string; + /** + * A request to include context from one or more MCP servers (including the caller), to be attached to the prompt. The client MAY ignore this request. + */ + includeContext?: "none" | "thisServer" | "allServers"; + /** + * @TJS-type number + */ + temperature?: number; + /** + * The maximum number of tokens to sample, as requested by the server. The client MAY choose to sample fewer tokens than requested. + */ + maxTokens: number; + stopSequences?: string[]; + /** + * Optional metadata to pass through to the LLM provider. The format of this metadata is provider-specific. + */ + metadata?: object; + }; + } + + /** + * The client's response to a sampling/create_message request from the server. The client should inform the user before returning the sampled message, to allow them to inspect the response (human in the loop) and decide whether to allow the server to see it. + */ + export interface CreateMessageResult extends Result, SamplingMessage { + /** + * The name of the model that generated the message. + */ + model: string; + /** + * The reason why sampling stopped, if known. + */ + stopReason?: "endTurn" | "stopSequence" | "maxTokens" | string; + } + + /** + * Describes a message issued to or received from an LLM API. + */ + export interface SamplingMessage { + role: Role; + content: TextContent | ImageContent; + } + + /** + * Base for objects that include optional annotations for the client. The client can use annotations to inform how objects are used or displayed + */ + export interface Annotated { + annotations?: { + /** + * Describes who the intended customer of this object or data is. + * + * It can include multiple entries to indicate content useful for multiple audiences (e.g., `["user", "assistant"]`). + */ + audience?: Role[]; + + /** + * Describes how important this data is for operating the server. + * + * A value of 1 means "most important," and indicates that the data is + * effectively required, while 0 means "least important," and indicates that + * the data is entirely optional. + * + * @TJS-type number + * @minimum 0 + * @maximum 1 + */ + priority?: number; + } + } + + /** + * Text provided to or from an LLM. + */ + export interface TextContent extends Annotated { + type: "text"; + /** + * The text content of the message. + */ + text: string; + } + + /** + * An image provided to or from an LLM. + */ + export interface ImageContent extends Annotated { + type: "image"; + /** + * The base64-encoded image data. + * + * @format byte + */ + data: string; + /** + * The MIME type of the image. Different providers may support different image types. + */ + mimeType: string; + } + + /** + * The server's preferences for model selection, requested of the client during sampling. + * + * Because LLMs can vary along multiple dimensions, choosing the "best" model is + * rarely straightforward. Different models excel in different areas-some are + * faster but less capable, others are more capable but more expensive, and so + * on. This interface allows servers to express their priorities across multiple + * dimensions to help clients make an appropriate selection for their use case. + * + * These preferences are always advisory. The client MAY ignore them. It is also + * up to the client to decide how to interpret these preferences and how to + * balance them against other considerations. + */ + export interface ModelPreferences { + /** + * Optional hints to use for model selection. + * + * If multiple hints are specified, the client MUST evaluate them in order + * (such that the first match is taken). + * + * The client SHOULD prioritize these hints over the numeric priorities, but + * MAY still use the priorities to select from ambiguous matches. + */ + hints?: ModelHint[]; + + /** + * How much to prioritize cost when selecting a model. A value of 0 means cost + * is not important, while a value of 1 means cost is the most important + * factor. + * + * @TJS-type number + * @minimum 0 + * @maximum 1 + */ + costPriority?: number; + + /** + * How much to prioritize sampling speed (latency) when selecting a model. A + * value of 0 means speed is not important, while a value of 1 means speed is + * the most important factor. + * + * @TJS-type number + * @minimum 0 + * @maximum 1 + */ + speedPriority?: number; + + /** + * How much to prioritize intelligence and capabilities when selecting a + * model. A value of 0 means intelligence is not important, while a value of 1 + * means intelligence is the most important factor. + * + * @TJS-type number + * @minimum 0 + * @maximum 1 + */ + intelligencePriority?: number; + } + + /** + * Hints to use for model selection. + * + * Keys not declared here are currently left unspecified by the spec and are up + * to the client to interpret. + */ + export interface ModelHint { + /** + * A hint for a model name. + * + * The client SHOULD treat this as a substring of a model name; for example: + * - `claude-3-5-sonnet` should match `claude-3-5-sonnet-20241022` + * - `sonnet` should match `claude-3-5-sonnet-20241022`, `claude-3-sonnet-20240229`, etc. + * - `claude` should match any Claude model + * + * The client MAY also map the string to a different provider's model name or a different model family, as long as it fills a similar niche; for example: + * - `gemini-1.5-flash` could match `claude-3-haiku-20240307` + */ + name?: string; + } + + /* Autocomplete */ + /** + * A request from the client to the server, to ask for completion options. + */ + export interface CompleteRequest extends Request { + method: "completion/complete"; + params: { + ref: PromptReference | ResourceReference; + /** + * The argument's information + */ + argument: { + /** + * The name of the argument + */ + name: string; + /** + * The value of the argument to use for completion matching. + */ + value: string; + }; + }; + } + + /** + * The server's response to a completion/complete request + */ + export interface CompleteResult extends Result { + completion: { + /** + * An array of completion values. Must not exceed 100 items. + */ + values: string[]; + /** + * The total number of completion options available. This can exceed the number of values actually sent in the response. + */ + total?: number; + /** + * Indicates whether there are additional completion options beyond those provided in the current response, even if the exact total is unknown. + */ + hasMore?: boolean; + }; + } + + /** + * A reference to a resource or resource template definition. + */ + export interface ResourceReference { + type: "ref/resource"; + /** + * The URI or URI template of the resource. + * + * @format uri-template + */ + uri: string; + } + + /** + * Identifies a prompt. + */ + export interface PromptReference { + type: "ref/prompt"; + /** + * The name of the prompt or prompt template + */ + name: string; + } + + /* Roots */ + /** + * Sent from the server to request a list of root URIs from the client. Roots allow + * servers to ask for specific directories or files to operate on. A common example + * for roots is providing a set of repositories or directories a server should operate + * on. + * + * This request is typically used when the server needs to understand the file system + * structure or access specific locations that the client has permission to read from. + */ + export interface ListRootsRequest extends Request { + method: "roots/list"; + } + + /** + * The client's response to a roots/list request from the server. + * This result contains an array of Root objects, each representing a root directory + * or file that the server can operate on. + */ + export interface ListRootsResult extends Result { + roots: Root[]; + } + + /** + * Represents a root directory or file that the server can operate on. + */ + export interface Root { + /** + * The URI identifying the root. This *must* start with file:// for now. + * This restriction may be relaxed in future versions of the protocol to allow + * other URI schemes. + * + * @format uri + */ + uri: string; + /** + * An optional name for the root. This can be used to provide a human-readable + * identifier for the root, which may be useful for display purposes or for + * referencing the root in other parts of the application. + */ + name?: string; + } + + /** + * A notification from the client to the server, informing it that the list of roots has changed. + * This notification should be sent whenever the client adds, removes, or modifies any root. + * The server should then request an updated list of roots using the ListRootsRequest. + */ + export interface RootsListChangedNotification extends Notification { + method: "notifications/roots/list_changed"; + } + + /* Client messages */ + export type ClientRequest = + | PingRequest + | InitializeRequest + | CompleteRequest + | SetLevelRequest + | GetPromptRequest + | ListPromptsRequest + | ListResourcesRequest + | ListResourceTemplatesRequest + | ReadResourceRequest + | SubscribeRequest + | UnsubscribeRequest + | CallToolRequest + | ListToolsRequest; + + export type ClientNotification = + | CancelledNotification + | ProgressNotification + | InitializedNotification + | RootsListChangedNotification; + + export type ClientResult = EmptyResult | CreateMessageResult | ListRootsResult; + + /* Server messages */ + export type ServerRequest = + | PingRequest + | CreateMessageRequest + | ListRootsRequest; + + export type ServerNotification = + | CancelledNotification + | ProgressNotification + | LoggingMessageNotification + | ResourceUpdatedNotification + | ResourceListChangedNotification + | ToolListChangedNotification + | PromptListChangedNotification; + + export type ServerResult = + | EmptyResult + | InitializeResult + | CompleteResult + | GetPromptResult + | ListPromptsResult + | ListResourcesResult + | ListResourceTemplatesResult + | ReadResourceResult + | CallToolResult + | ListToolsResult; +} diff --git a/src/vs/workbench/contrib/mcp/electron-sandbox/mcp.contribution.ts b/src/vs/workbench/contrib/mcp/electron-sandbox/mcp.contribution.ts new file mode 100644 index 000000000000..efdf3ee192d4 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/electron-sandbox/mcp.contribution.ts @@ -0,0 +1,10 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { SyncDescriptor } from '../../../../platform/instantiation/common/descriptors.js'; +import { mcpDiscoveryRegistry } from '../common/discovery/mcpDiscovery.js'; +import { NativeMcpDiscovery } from './nativeMpcDiscovery.js'; + +mcpDiscoveryRegistry.register(new SyncDescriptor(NativeMcpDiscovery)); diff --git a/src/vs/workbench/contrib/mcp/electron-sandbox/nativeMpcDiscovery.ts b/src/vs/workbench/contrib/mcp/electron-sandbox/nativeMpcDiscovery.ts new file mode 100644 index 000000000000..11da0b14991f --- /dev/null +++ b/src/vs/workbench/contrib/mcp/electron-sandbox/nativeMpcDiscovery.ts @@ -0,0 +1,42 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { ProxyChannel } from '../../../../base/parts/ipc/common/ipc.js'; +import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; +import { IFileService } from '../../../../platform/files/common/files.js'; +import { IInstantiationService } from '../../../../platform/instantiation/common/instantiation.js'; +import { IMainProcessService } from '../../../../platform/ipc/common/mainProcessService.js'; +import { ILabelService } from '../../../../platform/label/common/label.js'; +import { ILogService } from '../../../../platform/log/common/log.js'; +import { INativeMcpDiscoveryHelperService, NativeMcpDiscoveryHelperChannelName } from '../../../../platform/mcp/common/nativeMcpDiscoveryHelper.js'; +import { FilesystemMpcDiscovery } from '../common/discovery/nativeMcpDiscoveryAbstract.js'; +import { IMcpRegistry } from '../common/mcpRegistryTypes.js'; + +export class NativeMcpDiscovery extends FilesystemMpcDiscovery { + constructor( + @IMainProcessService private readonly mainProcess: IMainProcessService, + @ILogService private readonly logService: ILogService, + @ILabelService labelService: ILabelService, + @IFileService fileService: IFileService, + @IInstantiationService instantiationService: IInstantiationService, + @IMcpRegistry mcpRegistry: IMcpRegistry, + @IConfigurationService configurationService: IConfigurationService, + ) { + super(null, labelService, fileService, instantiationService, mcpRegistry, configurationService); + } + + public override start(): void { + const service = ProxyChannel.toService( + this.mainProcess.getChannel(NativeMcpDiscoveryHelperChannelName)); + + service.load().then( + data => this.setDetails(data), + err => { + this.logService.warn('Error getting main process MCP environment', err); + this.setDetails(undefined); + } + ); + } +} diff --git a/src/vs/workbench/contrib/mcp/test/common/mcpRegistry.test.ts b/src/vs/workbench/contrib/mcp/test/common/mcpRegistry.test.ts new file mode 100644 index 000000000000..774878b19d28 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/test/common/mcpRegistry.test.ts @@ -0,0 +1,591 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import * as sinon from 'sinon'; +import { cloneAndChange } from '../../../../../base/common/objects.js'; +import { ISettableObservable, observableValue } from '../../../../../base/common/observable.js'; +import { upcast } from '../../../../../base/common/types.js'; +import { URI } from '../../../../../base/common/uri.js'; +import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; +import { ConfigurationTarget } from '../../../../../platform/configuration/common/configuration.js'; +import { IDialogService } from '../../../../../platform/dialogs/common/dialogs.js'; +import { ServiceCollection } from '../../../../../platform/instantiation/common/serviceCollection.js'; +import { TestInstantiationService } from '../../../../../platform/instantiation/test/common/instantiationServiceMock.js'; +import { ILoggerService } from '../../../../../platform/log/common/log.js'; +import { ISecretStorageService } from '../../../../../platform/secrets/common/secrets.js'; +import { TestSecretStorageService } from '../../../../../platform/secrets/test/common/testSecretStorageService.js'; +import { IStorageService, StorageScope } from '../../../../../platform/storage/common/storage.js'; +import { IConfigurationResolverService } from '../../../../services/configurationResolver/common/configurationResolver.js'; +import { IOutputService } from '../../../../services/output/common/output.js'; +import { TestLoggerService, TestStorageService } from '../../../../test/common/workbenchTestServices.js'; +import { McpRegistry } from '../../common/mcpRegistry.js'; +import { IMcpHostDelegate, IMcpMessageTransport } from '../../common/mcpRegistryTypes.js'; +import { McpServerConnection } from '../../common/mcpServerConnection.js'; +import { LazyCollectionState, McpCollectionDefinition, McpCollectionReference, McpServerDefinition, McpServerTransportType } from '../../common/mcpTypes.js'; +import { TestMcpMessageTransport } from './mcpRegistryTypes.js'; +import { timeout } from '../../../../../base/common/async.js'; +import { IProductService } from '../../../../../platform/product/common/productService.js'; + +class TestConfigurationResolverService implements Partial { + declare readonly _serviceBrand: undefined; + + private interactiveCounter = 0; + + // Used to simulate stored/resolved variables + private readonly resolvedVariables = new Map(); + + constructor() { + // Add some test variables + this.resolvedVariables.set('workspaceFolder', '/test/workspace'); + this.resolvedVariables.set('fileBasename', 'test.txt'); + } + + resolveAsync(folder: any, value: any): Promise { + if (typeof value === 'string') { + return Promise.resolve(this.replaceVariables(value)); + } else if (Array.isArray(value)) { + return Promise.resolve(value.map(v => typeof v === 'string' ? this.replaceVariables(v) : v)); + } else { + const result: Record = {}; + for (const key in value) { + if (typeof value[key] === 'string') { + result[key] = this.replaceVariables(value[key]); + } else { + result[key] = value[key]; + } + } + return Promise.resolve(result); + } + } + + private replaceVariables(value: string): string { + let result = value; + for (const [key, val] of this.resolvedVariables.entries()) { + result = result.replace(`\${${key}}`, val); + } + return result; + } + + resolveAnyAsync(folder: any, config: any, commandValueMapping?: Record): Promise { + // Use cloneAndChange to recursively replace variables in the config + const newConfig = cloneAndChange(config, (value) => { + if (typeof value === 'string') { + // Replace any ${variable} with its value + let result = value; + for (const [key, val] of this.resolvedVariables.entries()) { + result = result.replace(`\${${key}}`, val); + } + + // If a commandValueMapping is provided, use it for additional replacements + if (commandValueMapping) { + for (const [key, val] of Object.entries(commandValueMapping)) { + result = result.replace(`\${${key}}`, val); + } + } + + return result === value ? undefined : result; + } + return undefined; + }); + + return Promise.resolve(newConfig); + } + + resolveWithInteraction(folder: any, config: any, section?: string, variables?: Record, target?: ConfigurationTarget): Promise | undefined> { + // For testing, we simulate interaction by returning a map with some variables + const result = new Map(); + result.set('input:testInteractive', `interactiveValue${this.interactiveCounter++}`); + result.set('command:testCommand', `commandOutput${this.interactiveCounter++}}`); + + // If variables are provided, include those too + if (variables) { + Object.entries(variables).forEach(([key, value]) => { + result.set(key, value); + }); + } + + return Promise.resolve(result); + } +} + +class TestMcpHostDelegate implements IMcpHostDelegate { + canStart(): boolean { + return true; + } + + start(): IMcpMessageTransport { + return new TestMcpMessageTransport(); + } + + waitForInitialProviderPromises(): Promise { + return Promise.resolve(); + } +} + +class TestDialogService implements Partial { + declare readonly _serviceBrand: undefined; + + private _promptResult: boolean | undefined; + private _promptSpy: sinon.SinonStub; + + constructor() { + this._promptSpy = sinon.stub(); + this._promptSpy.callsFake(() => { + return Promise.resolve({ result: this._promptResult }); + }); + } + + setPromptResult(result: boolean | undefined): void { + this._promptResult = result; + } + + get promptSpy(): sinon.SinonStub { + return this._promptSpy; + } + + prompt(options: any): Promise { + return this._promptSpy(options); + } +} + +suite('Workbench - MCP - Registry', () => { + const store = ensureNoDisposablesAreLeakedInTestSuite(); + + let registry: McpRegistry; + let testStorageService: TestStorageService; + let testConfigResolverService: TestConfigurationResolverService; + let testDialogService: TestDialogService; + let testCollection: McpCollectionDefinition & { serverDefinitions: ISettableObservable }; + let baseDefinition: McpServerDefinition; + + setup(() => { + testConfigResolverService = new TestConfigurationResolverService(); + testStorageService = store.add(new TestStorageService()); + testDialogService = new TestDialogService(); + + const services = new ServiceCollection( + [IConfigurationResolverService, testConfigResolverService], + [IStorageService, testStorageService], + [ISecretStorageService, new TestSecretStorageService()], + [ILoggerService, store.add(new TestLoggerService())], + [IOutputService, upcast({ showChannel: () => { } })], + [IDialogService, testDialogService], + [IProductService, {}], + ); + + const instaService = store.add(new TestInstantiationService(services)); + registry = store.add(instaService.createInstance(McpRegistry)); + + // Create test collection that can be reused + testCollection = { + id: 'test-collection', + label: 'Test Collection', + remoteAuthority: null, + serverDefinitions: observableValue('serverDefs', []), + isTrustedByDefault: true, + scope: StorageScope.APPLICATION + }; + + // Create base definition that can be reused + baseDefinition = { + id: 'test-server', + label: 'Test Server', + launch: { + type: McpServerTransportType.Stdio, + command: 'test-command', + args: [], + env: {}, + cwd: URI.parse('file:///test') + } + }; + }); + + test('registerCollection adds collection to registry', () => { + const disposable = registry.registerCollection(testCollection); + store.add(disposable); + + assert.strictEqual(registry.collections.get().length, 1); + assert.strictEqual(registry.collections.get()[0], testCollection); + + disposable.dispose(); + assert.strictEqual(registry.collections.get().length, 0); + }); + + test('registerDelegate adds delegate to registry', () => { + const delegate = new TestMcpHostDelegate(); + const disposable = registry.registerDelegate(delegate); + store.add(disposable); + + assert.strictEqual(registry.delegates.length, 1); + assert.strictEqual(registry.delegates[0], delegate); + + disposable.dispose(); + assert.strictEqual(registry.delegates.length, 0); + }); + + test('resolveConnection creates connection with resolved variables and memorizes them until cleared', async () => { + const definition: McpServerDefinition = { + ...baseDefinition, + launch: { + type: McpServerTransportType.Stdio, + command: '${workspaceFolder}/cmd', + args: ['--file', '${fileBasename}'], + env: { + PATH: '${input:testInteractive}' + }, + cwd: URI.parse('file:///test') + }, + variableReplacement: { + section: 'mcp' + } + }; + + const delegate = new TestMcpHostDelegate(); + store.add(registry.registerDelegate(delegate)); + testCollection.serverDefinitions.set([definition], undefined); + store.add(registry.registerCollection(testCollection)); + + const connection = await registry.resolveConnection({ collectionRef: testCollection, definitionRef: definition }) as McpServerConnection; + + assert.ok(connection); + assert.strictEqual(connection.definition, definition); + assert.strictEqual((connection.launchDefinition as any).command, '/test/workspace/cmd'); + assert.strictEqual((connection.launchDefinition as any).env.PATH, 'interactiveValue0'); + connection.dispose(); + + const connection2 = await registry.resolveConnection({ collectionRef: testCollection, definitionRef: definition }) as McpServerConnection; + + assert.ok(connection2); + assert.strictEqual((connection2.launchDefinition as any).env.PATH, 'interactiveValue0'); + connection2.dispose(); + + registry.clearSavedInputs(); + + const connection3 = await registry.resolveConnection({ collectionRef: testCollection, definitionRef: definition }) as McpServerConnection; + + assert.ok(connection3); + assert.strictEqual((connection3.launchDefinition as any).env.PATH, 'interactiveValue4'); + connection3.dispose(); + }); + + suite('Trust Management', () => { + setup(() => { + const delegate = new TestMcpHostDelegate(); + store.add(registry.registerDelegate(delegate)); + }); + + test('resolveConnection connects to server when trusted by default', async () => { + const definition = { ...baseDefinition }; + store.add(registry.registerCollection(testCollection)); + testCollection.serverDefinitions.set([definition], undefined); + + const connection = await registry.resolveConnection({ collectionRef: testCollection, definitionRef: definition }); + + assert.ok(connection); + assert.strictEqual(testDialogService.promptSpy.called, false); + connection?.dispose(); + }); + + test('resolveConnection prompts for confirmation when not trusted by default', async () => { + const untrustedCollection: McpCollectionDefinition = { + ...testCollection, + isTrustedByDefault: false + }; + + const definition = { ...baseDefinition }; + store.add(registry.registerCollection(untrustedCollection)); + testCollection.serverDefinitions.set([definition], undefined); + + testDialogService.setPromptResult(true); + + const connection = await registry.resolveConnection({ + collectionRef: untrustedCollection, + definitionRef: definition + }); + + assert.ok(connection); + assert.strictEqual(testDialogService.promptSpy.called, true); + connection?.dispose(); + + testDialogService.promptSpy.resetHistory(); + const connection2 = await registry.resolveConnection({ + collectionRef: untrustedCollection, + definitionRef: definition + }); + + assert.ok(connection2); + assert.strictEqual(testDialogService.promptSpy.called, false); + connection2?.dispose(); + }); + + test('resolveConnection returns undefined when user does not trust the server', async () => { + const untrustedCollection: McpCollectionDefinition = { + ...testCollection, + isTrustedByDefault: false + }; + + const definition = { ...baseDefinition }; + store.add(registry.registerCollection(untrustedCollection)); + testCollection.serverDefinitions.set([definition], undefined); + + testDialogService.setPromptResult(false); + + const connection = await registry.resolveConnection({ + collectionRef: untrustedCollection, + definitionRef: definition + }); + + assert.strictEqual(connection, undefined); + assert.strictEqual(testDialogService.promptSpy.called, true); + + testDialogService.promptSpy.resetHistory(); + const connection2 = await registry.resolveConnection({ + collectionRef: untrustedCollection, + definitionRef: definition + }); + + assert.strictEqual(connection2, undefined); + assert.strictEqual(testDialogService.promptSpy.called, false); + }); + + test('resolveConnection honors forceTrust parameter', async () => { + const untrustedCollection: McpCollectionDefinition = { + ...testCollection, + isTrustedByDefault: false + }; + + const definition = { ...baseDefinition }; + store.add(registry.registerCollection(untrustedCollection)); + testCollection.serverDefinitions.set([definition], undefined); + + testDialogService.setPromptResult(false); + + const connection1 = await registry.resolveConnection({ + collectionRef: untrustedCollection, + definitionRef: definition + }); + + assert.strictEqual(connection1, undefined); + + testDialogService.promptSpy.resetHistory(); + testDialogService.setPromptResult(true); + + const connection2 = await registry.resolveConnection({ + collectionRef: untrustedCollection, + definitionRef: definition, + forceTrust: true + }); + + assert.ok(connection2); + assert.strictEqual(testDialogService.promptSpy.called, true); + connection2?.dispose(); + + testDialogService.promptSpy.resetHistory(); + const connection3 = await registry.resolveConnection({ + collectionRef: untrustedCollection, + definitionRef: definition + }); + + assert.ok(connection3); + assert.strictEqual(testDialogService.promptSpy.called, false); + connection3?.dispose(); + }); + }); + + suite('Lazy Collections', () => { + let lazyCollection: McpCollectionDefinition; + let normalCollection: McpCollectionDefinition; + let removedCalled: boolean; + + setup(() => { + removedCalled = false; + lazyCollection = { + ...testCollection, + id: 'lazy-collection', + lazy: { + isCached: false, + load: () => Promise.resolve(), + removed: () => { removedCalled = true; } + } + }; + normalCollection = { + ...testCollection, + id: 'lazy-collection', + serverDefinitions: observableValue('serverDefs', [baseDefinition]) + }; + }); + + test('registers lazy collection', () => { + const disposable = registry.registerCollection(lazyCollection); + store.add(disposable); + + assert.strictEqual(registry.collections.get().length, 1); + assert.strictEqual(registry.collections.get()[0], lazyCollection); + assert.strictEqual(registry.lazyCollectionState.get(), LazyCollectionState.HasUnknown); + }); + + test('lazy collection is replaced by normal collection', () => { + store.add(registry.registerCollection(lazyCollection)); + store.add(registry.registerCollection(normalCollection)); + + const collections = registry.collections.get(); + assert.strictEqual(collections.length, 1); + assert.strictEqual(collections[0], normalCollection); + assert.strictEqual(collections[0].lazy, undefined); + assert.strictEqual(registry.lazyCollectionState.get(), LazyCollectionState.AllKnown); + }); + + test('lazyCollectionState updates correctly during loading', async () => { + lazyCollection = { + ...lazyCollection, + lazy: { + ...lazyCollection.lazy!, + load: async () => { + await timeout(0); + store.add(registry.registerCollection(normalCollection)); + return Promise.resolve(); + } + } + }; + + store.add(registry.registerCollection(lazyCollection)); + assert.strictEqual(registry.lazyCollectionState.get(), LazyCollectionState.HasUnknown); + + const loadingPromise = registry.discoverCollections(); + assert.strictEqual(registry.lazyCollectionState.get(), LazyCollectionState.LoadingUnknown); + + await loadingPromise; + + // The collection wasn't replaced, so it should be removed + assert.strictEqual(registry.collections.get().length, 1); + assert.strictEqual(registry.lazyCollectionState.get(), LazyCollectionState.AllKnown); + assert.strictEqual(removedCalled, false); + }); + + test('removed callback is called when lazy collection is not replaced', async () => { + store.add(registry.registerCollection(lazyCollection)); + await registry.discoverCollections(); + + assert.strictEqual(removedCalled, true); + }); + + test('cached lazy collections are tracked correctly', () => { + lazyCollection.lazy!.isCached = true; + store.add(registry.registerCollection(lazyCollection)); + + assert.strictEqual(registry.lazyCollectionState.get(), LazyCollectionState.AllKnown); + + // Adding an uncached lazy collection changes the state + const uncachedLazy = { + ...lazyCollection, + id: 'uncached-lazy', + lazy: { + ...lazyCollection.lazy!, + isCached: false + } + }; + store.add(registry.registerCollection(uncachedLazy)); + + assert.strictEqual(registry.lazyCollectionState.get(), LazyCollectionState.HasUnknown); + }); + }); + + suite('Collection Tool Prefixes', () => { + test('assigns unique prefixes to collections', () => { + const collection1: McpCollectionDefinition = { + id: 'collection1', + label: 'Collection 1', + remoteAuthority: null, + serverDefinitions: observableValue('serverDefs', []), + isTrustedByDefault: true, + scope: StorageScope.APPLICATION + }; + + const collection2: McpCollectionDefinition = { + id: 'collection2', + label: 'Collection 2', + remoteAuthority: null, + serverDefinitions: observableValue('serverDefs', []), + isTrustedByDefault: true, + scope: StorageScope.APPLICATION + }; + + store.add(registry.registerCollection(collection1)); + store.add(registry.registerCollection(collection2)); + + const prefix1 = registry.collectionToolPrefix(collection1).get(); + const prefix2 = registry.collectionToolPrefix(collection2).get(); + + assert.notStrictEqual(prefix1, prefix2); + assert.ok(/^[a-f0-9]{3}\.$/.test(prefix1)); + assert.ok(/^[a-f0-9]{3}\.$/.test(prefix2)); + }); + + test('handles hash collisions by incrementing view', () => { + // These strings are known to have SHA1 hash collisions in their first 3 characters + const collection1: McpCollectionDefinition = { + id: 'potato', + label: 'Collection 1', + remoteAuthority: null, + serverDefinitions: observableValue('serverDefs', []), + isTrustedByDefault: true, + scope: StorageScope.APPLICATION + }; + + const collection2: McpCollectionDefinition = { + id: 'candidate_83048', + label: 'Collection 2', + remoteAuthority: null, + serverDefinitions: observableValue('serverDefs', []), + isTrustedByDefault: true, + scope: StorageScope.APPLICATION + }; + + store.add(registry.registerCollection(collection1)); + store.add(registry.registerCollection(collection2)); + + const prefix1 = registry.collectionToolPrefix(collection1).get(); + const prefix2 = registry.collectionToolPrefix(collection2).get(); + + assert.notStrictEqual(prefix1, prefix2); + assert.ok(/^[a-f0-9]{3}\.$/.test(prefix1)); + assert.ok(/^[a-f0-9]{3}\.$/.test(prefix2)); + }); + + test('prefix changes when collections change', () => { + const collection1: McpCollectionDefinition = { + id: 'collection1', + label: 'Collection 1', + remoteAuthority: null, + serverDefinitions: observableValue('serverDefs', []), + isTrustedByDefault: true, + scope: StorageScope.APPLICATION + }; + + const disposable = registry.registerCollection(collection1); + store.add(disposable); + + const prefix1 = registry.collectionToolPrefix(collection1).get(); + assert.ok(!!prefix1); + + disposable.dispose(); + + const prefix2 = registry.collectionToolPrefix(collection1).get(); + + assert.strictEqual(prefix2, ''); + }); + + test('prefix is empty for unknown collections', () => { + const unknownCollection: McpCollectionReference = { + id: 'unknown', + label: 'Unknown' + }; + + const prefix = registry.collectionToolPrefix(unknownCollection).get(); + assert.strictEqual(prefix, ''); + }); + }); +}); diff --git a/src/vs/workbench/contrib/mcp/test/common/mcpRegistryInputStorage.test.ts b/src/vs/workbench/contrib/mcp/test/common/mcpRegistryInputStorage.test.ts new file mode 100644 index 000000000000..f6f745d57512 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/test/common/mcpRegistryInputStorage.test.ts @@ -0,0 +1,178 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; +import { ILogService, NullLogService } from '../../../../../platform/log/common/log.js'; +import { TestSecretStorageService } from '../../../../../platform/secrets/test/common/testSecretStorageService.js'; +import { StorageScope, StorageTarget } from '../../../../../platform/storage/common/storage.js'; +import { TestStorageService } from '../../../../test/common/workbenchTestServices.js'; +import { McpRegistryInputStorage } from '../../common/mcpRegistryInputStorage.js'; + +suite('Workbench - MCP - RegistryInputStorage', () => { + const store = ensureNoDisposablesAreLeakedInTestSuite(); + + let testStorageService: TestStorageService; + let testSecretStorageService: TestSecretStorageService; + let testLogService: ILogService; + let mcpInputStorage: McpRegistryInputStorage; + + setup(() => { + testStorageService = store.add(new TestStorageService()); + testSecretStorageService = new TestSecretStorageService(); + testLogService = store.add(new NullLogService()); + + // Create the input storage with APPLICATION scope + mcpInputStorage = store.add(new McpRegistryInputStorage( + StorageScope.APPLICATION, + StorageTarget.MACHINE, + testStorageService, + testSecretStorageService, + testLogService + )); + }); + + test('setPlainText stores values that can be retrieved with getMap', async () => { + const values = { + 'key1': 'value1', + 'key2': 'value2' + }; + + await mcpInputStorage.setPlainText(values); + const result = await mcpInputStorage.getMap(); + + assert.strictEqual(result.key1, 'value1'); + assert.strictEqual(result.key2, 'value2'); + }); + + test('setSecrets stores encrypted values that can be retrieved with getMap', async () => { + const secrets = { + 'secretKey1': 'secretValue1', + 'secretKey2': 'secretValue2' + }; + + await mcpInputStorage.setSecrets(secrets); + const result = await mcpInputStorage.getMap(); + + assert.strictEqual(result.secretKey1, 'secretValue1'); + assert.strictEqual(result.secretKey2, 'secretValue2'); + }); + + test('getMap returns combined plain text and secret values', async () => { + await mcpInputStorage.setPlainText({ + 'plainKey': 'plainValue' + }); + + await mcpInputStorage.setSecrets({ + 'secretKey': 'secretValue' + }); + + const result = await mcpInputStorage.getMap(); + + assert.strictEqual(result.plainKey, 'plainValue'); + assert.strictEqual(result.secretKey, 'secretValue'); + }); + + test('clear removes specific values', async () => { + await mcpInputStorage.setPlainText({ + 'key1': 'value1', + 'key2': 'value2' + }); + + await mcpInputStorage.setSecrets({ + 'secretKey1': 'secretValue1', + 'secretKey2': 'secretValue2' + }); + + // Clear one plain and one secret value + await mcpInputStorage.clear('key1'); + await mcpInputStorage.clear('secretKey1'); + + const result = await mcpInputStorage.getMap(); + + assert.strictEqual(result.key1, undefined); + assert.strictEqual(result.key2, 'value2'); + assert.strictEqual(result.secretKey1, undefined); + assert.strictEqual(result.secretKey2, 'secretValue2'); + }); + + test('clearAll removes all values', async () => { + await mcpInputStorage.setPlainText({ + 'key1': 'value1' + }); + + await mcpInputStorage.setSecrets({ + 'secretKey1': 'secretValue1' + }); + + mcpInputStorage.clearAll(); + + const result = await mcpInputStorage.getMap(); + + assert.deepStrictEqual(result, {}); + }); + + test('updates to plain text values overwrite existing values', async () => { + await mcpInputStorage.setPlainText({ + 'key1': 'value1', + 'key2': 'value2' + }); + + await mcpInputStorage.setPlainText({ + 'key1': 'updatedValue1' + }); + + const result = await mcpInputStorage.getMap(); + + assert.strictEqual(result.key1, 'updatedValue1'); + assert.strictEqual(result.key2, 'value2'); + }); + + test('updates to secret values overwrite existing values', async () => { + await mcpInputStorage.setSecrets({ + 'secretKey1': 'secretValue1', + 'secretKey2': 'secretValue2' + }); + + await mcpInputStorage.setSecrets({ + 'secretKey1': 'updatedSecretValue1' + }); + + const result = await mcpInputStorage.getMap(); + + assert.strictEqual(result.secretKey1, 'updatedSecretValue1'); + assert.strictEqual(result.secretKey2, 'secretValue2'); + }); + + test('storage persists values across instances', async () => { + // Set values on first instance + await mcpInputStorage.setPlainText({ + 'key1': 'value1' + }); + + await mcpInputStorage.setSecrets({ + 'secretKey1': 'secretValue1' + }); + + await testStorageService.flush(); + + // Create a second instance that should have access to the same storage + const secondInstance = store.add(new McpRegistryInputStorage( + StorageScope.APPLICATION, + StorageTarget.MACHINE, + testStorageService, + testSecretStorageService, + testLogService + )); + + const result = await secondInstance.getMap(); + + assert.strictEqual(result.key1, 'value1'); + assert.strictEqual(result.secretKey1, 'secretValue1'); + + assert.ok(!testStorageService.get('mcpInputs', StorageScope.APPLICATION)?.includes('secretValue1')); + }); +}); + diff --git a/src/vs/workbench/contrib/mcp/test/common/mcpRegistryTypes.ts b/src/vs/workbench/contrib/mcp/test/common/mcpRegistryTypes.ts new file mode 100644 index 000000000000..567c613329da --- /dev/null +++ b/src/vs/workbench/contrib/mcp/test/common/mcpRegistryTypes.ts @@ -0,0 +1,107 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import { Emitter } from '../../../../../base/common/event.js'; +import { Disposable } from '../../../../../base/common/lifecycle.js'; +import { observableValue } from '../../../../../base/common/observable.js'; +import { IMcpMessageTransport } from '../../common/mcpRegistryTypes.js'; +import { McpConnectionState } from '../../common/mcpTypes.js'; +import { MCP } from '../../common/modelContextProtocol.js'; + +/** + * Implementation of IMcpMessageTransport for testing purposes. + * Allows tests to easily send/receive messages and control the connection state. + */ +export class TestMcpMessageTransport extends Disposable implements IMcpMessageTransport { + private readonly _onDidLog = this._register(new Emitter()); + public readonly onDidLog = this._onDidLog.event; + + private readonly _onDidReceiveMessage = this._register(new Emitter()); + public readonly onDidReceiveMessage = this._onDidReceiveMessage.event; + + private readonly _stateValue = observableValue('testTransportState', { state: McpConnectionState.Kind.Starting }); + public readonly state = this._stateValue; + + private readonly _sentMessages: MCP.JSONRPCMessage[] = []; + + constructor() { + super(); + } + + /** + * Send a message through the transport. + */ + public send(message: MCP.JSONRPCMessage): void { + this._sentMessages.push(message); + } + + /** + * Stop the transport. + */ + public stop(): void { + this._stateValue.set({ state: McpConnectionState.Kind.Stopped }, undefined); + } + + // Test Helper Methods + + /** + * Simulate receiving a message from the server. + */ + public simulateReceiveMessage(message: MCP.JSONRPCMessage): void { + this._onDidReceiveMessage.fire(message); + } + + /** + * Simulates a reply to an 'initialized' request. + */ + public simulateInitialized() { + if (!this._sentMessages.length) { + throw new Error('initialize was not called yet'); + } + + this.simulateReceiveMessage({ + jsonrpc: MCP.JSONRPC_VERSION, + id: (this.getSentMessages()[0] as MCP.JSONRPCRequest).id, + result: { + protocolVersion: MCP.LATEST_PROTOCOL_VERSION, + capabilities: { + tools: {}, + }, + serverInfo: { + name: 'Test Server', + version: '1.0.0' + }, + } satisfies MCP.InitializeResult + }); + } + + /** + * Simulate a log event. + */ + public simulateLog(message: string): void { + this._onDidLog.fire(message); + } + + /** + * Set the connection state. + */ + public setConnectionState(state: McpConnectionState): void { + this._stateValue.set(state, undefined); + } + + /** + * Get all messages that have been sent. + */ + public getSentMessages(): readonly MCP.JSONRPCMessage[] { + return [...this._sentMessages]; + } + + /** + * Clear the sent messages history. + */ + public clearSentMessages(): void { + this._sentMessages.length = 0; + } +} diff --git a/src/vs/workbench/contrib/mcp/test/common/mcpServerConnection.test.ts b/src/vs/workbench/contrib/mcp/test/common/mcpServerConnection.test.ts new file mode 100644 index 000000000000..83b9933c3f25 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/test/common/mcpServerConnection.test.ts @@ -0,0 +1,429 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { timeout } from '../../../../../base/common/async.js'; +import { Disposable } from '../../../../../base/common/lifecycle.js'; +import { autorun, observableValue } from '../../../../../base/common/observable.js'; +import { upcast } from '../../../../../base/common/types.js'; +import { URI } from '../../../../../base/common/uri.js'; +import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; +import { ServiceCollection } from '../../../../../platform/instantiation/common/serviceCollection.js'; +import { TestInstantiationService } from '../../../../../platform/instantiation/test/common/instantiationServiceMock.js'; +import { ILogger, ILoggerService } from '../../../../../platform/log/common/log.js'; +import { IProductService } from '../../../../../platform/product/common/productService.js'; +import { IStorageService, StorageScope } from '../../../../../platform/storage/common/storage.js'; +import { IOutputService } from '../../../../services/output/common/output.js'; +import { TestLoggerService, TestProductService, TestStorageService } from '../../../../test/common/workbenchTestServices.js'; +import { IMcpHostDelegate, IMcpMessageTransport } from '../../common/mcpRegistryTypes.js'; +import { McpServerConnection } from '../../common/mcpServerConnection.js'; +import { McpCollectionDefinition, McpConnectionState, McpServerDefinition, McpServerTransportType } from '../../common/mcpTypes.js'; +import { TestMcpMessageTransport } from './mcpRegistryTypes.js'; + +class TestMcpHostDelegate extends Disposable implements IMcpHostDelegate { + private readonly _transport: TestMcpMessageTransport; + private _canStartValue = true; + + constructor() { + super(); + this._transport = this._register(new TestMcpMessageTransport()); + } + + canStart(): boolean { + return this._canStartValue; + } + + start(): IMcpMessageTransport { + if (!this._canStartValue) { + throw new Error('Cannot start server'); + } + return this._transport; + } + + getTransport(): TestMcpMessageTransport { + return this._transport; + } + + setCanStart(value: boolean): void { + this._canStartValue = value; + } + + waitForInitialProviderPromises(): Promise { + return Promise.resolve(); + } +} + +suite('Workbench - MCP - ServerConnection', () => { + const store = ensureNoDisposablesAreLeakedInTestSuite(); + + let instantiationService: TestInstantiationService; + let delegate: TestMcpHostDelegate; + let transport: TestMcpMessageTransport; + let collection: McpCollectionDefinition; + let serverDefinition: McpServerDefinition; + + setup(() => { + delegate = store.add(new TestMcpHostDelegate()); + transport = delegate.getTransport(); + + // Setup test services + const services = new ServiceCollection( + [ILoggerService, store.add(new TestLoggerService())], + [IOutputService, upcast({ showChannel: () => { } })], + [IStorageService, store.add(new TestStorageService())], + [IProductService, TestProductService], + ); + + instantiationService = store.add(new TestInstantiationService(services)); + + // Create test collection + collection = { + id: 'test-collection', + label: 'Test Collection', + remoteAuthority: null, + serverDefinitions: observableValue('serverDefs', []), + isTrustedByDefault: true, + scope: StorageScope.APPLICATION + }; + + // Create server definition + serverDefinition = { + id: 'test-server', + label: 'Test Server', + launch: { + type: McpServerTransportType.Stdio, + command: 'test-command', + args: [], + env: {}, + cwd: URI.parse('file:///test') + } + }; + }); + + function waitForHandler(cnx: McpServerConnection) { + const handler = cnx.handler.get(); + if (handler) { + return Promise.resolve(handler); + } + + return new Promise(resolve => { + const disposable = autorun(reader => { + const handler = cnx.handler.read(reader); + if (handler) { + disposable.dispose(); + resolve(handler); + } + }); + }); + } + + test('should start and set state to Running when transport succeeds', async () => { + // Create server connection + const connection = instantiationService.createInstance( + McpServerConnection, + collection, + serverDefinition, + delegate, + serverDefinition.launch + ); + store.add(connection); + + // Start the connection + const startPromise = connection.start(); + + // Simulate successful connection + transport.setConnectionState({ state: McpConnectionState.Kind.Running }); + + const state = await startPromise; + assert.strictEqual(state.state, McpConnectionState.Kind.Running); + + transport.simulateInitialized(); + assert.ok(await waitForHandler(connection)); + }); + + test('should handle errors during start', async () => { + // Setup delegate to fail on start + delegate.setCanStart(false); + + // Create server connection + const connection = instantiationService.createInstance( + McpServerConnection, + collection, + serverDefinition, + delegate, + serverDefinition.launch + ); + store.add(connection); + + // Start the connection + const state = await connection.start(); + + assert.strictEqual(state.state, McpConnectionState.Kind.Error); + assert.ok(state.message); + }); + + test('should handle transport errors', async () => { + // Create server connection + const connection = instantiationService.createInstance( + McpServerConnection, + collection, + serverDefinition, + delegate, + serverDefinition.launch + ); + store.add(connection); + + // Start the connection + const startPromise = connection.start(); + + // Simulate error in transport + transport.setConnectionState({ + state: McpConnectionState.Kind.Error, + message: 'Test error message' + }); + + const state = await startPromise; + assert.strictEqual(state.state, McpConnectionState.Kind.Error); + assert.strictEqual(state.message, 'Test error message'); + }); + + test('should stop and set state to Stopped', async () => { + // Create server connection + const connection = instantiationService.createInstance( + McpServerConnection, + collection, + serverDefinition, + delegate, + serverDefinition.launch + ); + store.add(connection); + + // Start the connection + const startPromise = connection.start(); + transport.setConnectionState({ state: McpConnectionState.Kind.Running }); + await startPromise; + + // Stop the connection + const stopPromise = connection.stop(); + await stopPromise; + + assert.strictEqual(connection.state.get().state, McpConnectionState.Kind.Stopped); + }); + + test('should not restart if already starting', async () => { + // Create server connection + const connection = instantiationService.createInstance( + McpServerConnection, + collection, + serverDefinition, + delegate, + serverDefinition.launch + ); + store.add(connection); + + // Start the connection + const startPromise1 = connection.start(); + + // Try to start again while starting + const startPromise2 = connection.start(); + + // Simulate successful connection + transport.setConnectionState({ state: McpConnectionState.Kind.Running }); + + const state1 = await startPromise1; + const state2 = await startPromise2; + + // Both promises should resolve to the same state + assert.strictEqual(state1.state, McpConnectionState.Kind.Running); + assert.strictEqual(state2.state, McpConnectionState.Kind.Running); + + transport.simulateInitialized(); + assert.ok(await waitForHandler(connection)); + }); + + test('should clean up when disposed', async () => { + // Create server connection + const connection = instantiationService.createInstance( + McpServerConnection, + collection, + serverDefinition, + delegate, + serverDefinition.launch + ); + + // Start the connection + const startPromise = connection.start(); + transport.setConnectionState({ state: McpConnectionState.Kind.Running }); + await startPromise; + + // Dispose the connection + connection.dispose(); + + assert.strictEqual(connection.state.get().state, McpConnectionState.Kind.Stopped); + }); + + test('showOutput should call logger and output services', () => { + let channelShown = false; + const outputService = { + showChannel: (id: string) => { + assert.strictEqual(id, `mcpServer/${serverDefinition.id}`); + channelShown = true; + } + }; + + let loggerVisible = false; + const loggerService = new class extends TestLoggerService { + override setVisibility(id: string, visible: boolean): void { + assert.strictEqual(id, `mcpServer/${serverDefinition.id}`); + assert.strictEqual(visible, true); + loggerVisible = true; + } + }; + + // Override services + const services = new ServiceCollection( + [ILoggerService, store.add(loggerService)], + [IOutputService, upcast(outputService)], + [IStorageService, store.add(new TestStorageService())] + ); + + const localInstantiationService = store.add(new TestInstantiationService(services)); + + // Create server connection + const connection = localInstantiationService.createInstance( + McpServerConnection, + collection, + serverDefinition, + delegate, + serverDefinition.launch + ); + store.add(connection); + + // Show output + connection.showOutput(); + + assert.strictEqual(channelShown, true); + assert.strictEqual(loggerVisible, true); + }); + + test('should log transport messages', async () => { + // Track logged messages + const loggedMessages: string[] = []; + const loggerService = new class extends TestLoggerService { + override createLogger(id: string) { + return { + info: (message: string) => { + loggedMessages.push(message); + }, + error: () => { }, + dispose: () => { } + } as Partial as ILogger; + } + }; + + // Override services + const services = new ServiceCollection( + [ILoggerService, store.add(loggerService)], + [IOutputService, upcast({ showChannel: () => { } })], + [IStorageService, store.add(new TestStorageService())] + ); + + const localInstantiationService = store.add(new TestInstantiationService(services)); + + // Create server connection + const connection = localInstantiationService.createInstance( + McpServerConnection, + collection, + serverDefinition, + delegate, + serverDefinition.launch + ); + store.add(connection); + + // Start the connection + const startPromise = connection.start(); + + // Simulate log message from transport + transport.simulateLog('Test log message'); + + // Set connection to running + transport.setConnectionState({ state: McpConnectionState.Kind.Running }); + await startPromise; + + // Check that the message was logged + assert.ok(loggedMessages.some(msg => msg === 'Test log message')); + }); + + test('should correctly handle transitions to and from error state', async () => { + // Create server connection + const connection = instantiationService.createInstance( + McpServerConnection, + collection, + serverDefinition, + delegate, + serverDefinition.launch + ); + store.add(connection); + + // Start the connection + const startPromise = connection.start(); + + // Transition to error state + const errorState: McpConnectionState = { + state: McpConnectionState.Kind.Error, + message: 'Temporary error' + }; + transport.setConnectionState(errorState); + + let state = await startPromise; + assert.equal(state, errorState); + + + transport.setConnectionState({ state: McpConnectionState.Kind.Stopped }); + + // Transition back to running state + const startPromise2 = connection.start(); + transport.setConnectionState({ state: McpConnectionState.Kind.Running }); + state = await startPromise2; + assert.deepStrictEqual(state, { state: McpConnectionState.Kind.Running }); + + connection.dispose(); + await timeout(10); + }); + + test('should handle multiple start/stop cycles', async () => { + // Create server connection + const connection = instantiationService.createInstance( + McpServerConnection, + collection, + serverDefinition, + delegate, + serverDefinition.launch + ); + store.add(connection); + + // First cycle + let startPromise = connection.start(); + transport.setConnectionState({ state: McpConnectionState.Kind.Running }); + await startPromise; + + await connection.stop(); + assert.deepStrictEqual(connection.state.get(), { state: McpConnectionState.Kind.Stopped }); + + // Second cycle + startPromise = connection.start(); + transport.setConnectionState({ state: McpConnectionState.Kind.Running }); + await startPromise; + + assert.deepStrictEqual(connection.state.get(), { state: McpConnectionState.Kind.Running }); + + await connection.stop(); + + assert.deepStrictEqual(connection.state.get(), { state: McpConnectionState.Kind.Stopped }); + + connection.dispose(); + await timeout(10); + }); +}); diff --git a/src/vs/workbench/contrib/mcp/test/common/mcpServerRequestHandler.test.ts b/src/vs/workbench/contrib/mcp/test/common/mcpServerRequestHandler.test.ts new file mode 100644 index 000000000000..ebbfeb757981 --- /dev/null +++ b/src/vs/workbench/contrib/mcp/test/common/mcpServerRequestHandler.test.ts @@ -0,0 +1,396 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as assert from 'assert'; +import { upcast } from '../../../../../base/common/types.js'; +import { ensureNoDisposablesAreLeakedInTestSuite } from '../../../../../base/test/common/utils.js'; +import { ServiceCollection } from '../../../../../platform/instantiation/common/serviceCollection.js'; +import { TestInstantiationService } from '../../../../../platform/instantiation/test/common/instantiationServiceMock.js'; +import { ILoggerService } from '../../../../../platform/log/common/log.js'; +import { IProductService } from '../../../../../platform/product/common/productService.js'; +import { IStorageService } from '../../../../../platform/storage/common/storage.js'; +import { TestLoggerService, TestProductService, TestStorageService } from '../../../../test/common/workbenchTestServices.js'; +import { IMcpHostDelegate } from '../../common/mcpRegistryTypes.js'; +import { McpServerRequestHandler } from '../../common/mcpServerRequestHandler.js'; +import { McpConnectionState } from '../../common/mcpTypes.js'; +import { MCP } from '../../common/modelContextProtocol.js'; +import { TestMcpMessageTransport } from './mcpRegistryTypes.js'; +import { IOutputService } from '../../../../services/output/common/output.js'; +import { Disposable } from '../../../../../base/common/lifecycle.js'; +import { CancellationTokenSource } from '../../../../../base/common/cancellation.js'; + +class TestMcpHostDelegate extends Disposable implements IMcpHostDelegate { + private readonly _transport: TestMcpMessageTransport; + + constructor() { + super(); + this._transport = this._register(new TestMcpMessageTransport()); + } + + canStart(): boolean { + return true; + } + + start(): TestMcpMessageTransport { + return this._transport; + } + + getTransport(): TestMcpMessageTransport { + return this._transport; + } + + waitForInitialProviderPromises(): Promise { + return Promise.resolve(); + } +} + +suite('Workbench - MCP - ServerRequestHandler', () => { + const store = ensureNoDisposablesAreLeakedInTestSuite(); + + let instantiationService: TestInstantiationService; + let delegate: TestMcpHostDelegate; + let transport: TestMcpMessageTransport; + let handler: McpServerRequestHandler; + let cts: CancellationTokenSource; + + setup(async () => { + delegate = store.add(new TestMcpHostDelegate()); + transport = delegate.getTransport(); + cts = store.add(new CancellationTokenSource()); + + // Setup test services + const services = new ServiceCollection( + [ILoggerService, store.add(new TestLoggerService())], + [IOutputService, upcast({ showChannel: () => { } })], + [IStorageService, store.add(new TestStorageService())], + [IProductService, TestProductService], + ); + + instantiationService = store.add(new TestInstantiationService(services)); + + transport.setConnectionState({ state: McpConnectionState.Kind.Running }); + + // Manually create the handler since we need the transport already set up + const logger = store.add((instantiationService.get(ILoggerService) as TestLoggerService) + .createLogger('mcpServerTest', { hidden: true, name: 'MCP Test' })); + + // Start the handler creation + const handlerPromise = McpServerRequestHandler.create(instantiationService, transport, logger, cts.token); + + // Simulate successful initialization + // We need to respond to the initialize request that the handler will make + transport.simulateReceiveMessage({ + jsonrpc: MCP.JSONRPC_VERSION, + id: 1, // The handler uses 1 for the first request + result: { + protocolVersion: MCP.LATEST_PROTOCOL_VERSION, + serverInfo: { + name: 'Test MCP Server', + version: '1.0.0', + }, + capabilities: { + resources: { + supportedTypes: ['text/plain'], + }, + tools: { + supportsCancellation: true, + } + } + } + }); + + handler = await handlerPromise; + store.add(handler); + }); + + test('should send and receive JSON-RPC requests', async () => { + // Setup request + const requestPromise = handler.listResources(); + + // Get the sent message and verify it + const sentMessages = transport.getSentMessages(); + assert.strictEqual(sentMessages.length, 3); // initialize + listResources + + // Verify listResources request format + const listResourcesRequest = sentMessages[2] as MCP.JSONRPCRequest; + assert.strictEqual(listResourcesRequest.method, 'resources/list'); + assert.strictEqual(listResourcesRequest.jsonrpc, MCP.JSONRPC_VERSION); + assert.ok(typeof listResourcesRequest.id === 'number'); + + // Simulate server response with mock resources that match the expected Resource interface + transport.simulateReceiveMessage({ + jsonrpc: MCP.JSONRPC_VERSION, + id: listResourcesRequest.id, + result: { + resources: [ + { uri: 'resource1', type: 'text/plain', name: 'Test Resource 1' }, + { uri: 'resource2', type: 'text/plain', name: 'Test Resource 2' } + ] + } + }); + + // Verify the result + const resources = await requestPromise; + assert.strictEqual(resources.length, 2); + assert.strictEqual(resources[0].uri, 'resource1'); + assert.strictEqual(resources[1].name, 'Test Resource 2'); + }); + + test('should handle paginated requests', async () => { + // Setup request + const requestPromise = handler.listResources(); + + // Get the first request and respond with pagination + const sentMessages = transport.getSentMessages(); + const listResourcesRequest = sentMessages[2] as MCP.JSONRPCRequest; + + // Send first page with nextCursor + transport.simulateReceiveMessage({ + jsonrpc: MCP.JSONRPC_VERSION, + id: listResourcesRequest.id, + result: { + resources: [ + { uri: 'resource1', type: 'text/plain', name: 'Test Resource 1' } + ], + nextCursor: 'page2' + } + }); + + // Clear the sent messages to only capture the next page request + transport.clearSentMessages(); + + // Wait a bit to allow the handler to process and send the next request + await new Promise(resolve => setTimeout(resolve, 0)); + + // Get the second request and verify cursor is included + const sentMessages2 = transport.getSentMessages(); + assert.strictEqual(sentMessages2.length, 1); + + const listResourcesRequest2 = sentMessages2[0] as MCP.JSONRPCRequest; + assert.strictEqual(listResourcesRequest2.method, 'resources/list'); + assert.deepStrictEqual(listResourcesRequest2.params, { cursor: 'page2' }); + + // Send final page with no nextCursor + transport.simulateReceiveMessage({ + jsonrpc: MCP.JSONRPC_VERSION, + id: listResourcesRequest2.id, + result: { + resources: [ + { uri: 'resource2', type: 'text/plain', name: 'Test Resource 2' } + ] + } + }); + + // Verify the combined result + const resources = await requestPromise; + assert.strictEqual(resources.length, 2); + assert.strictEqual(resources[0].uri, 'resource1'); + assert.strictEqual(resources[1].uri, 'resource2'); + }); + + test('should handle error responses', async () => { + // Setup request + const requestPromise = handler.readResource({ uri: 'non-existent' }); + + // Get the sent message + const sentMessages = transport.getSentMessages(); + const readResourceRequest = sentMessages[2] as MCP.JSONRPCRequest; // [0] is initialize + + // Simulate error response + transport.simulateReceiveMessage({ + jsonrpc: MCP.JSONRPC_VERSION, + id: readResourceRequest.id, + error: { + code: MCP.METHOD_NOT_FOUND, + message: 'Resource not found' + } + }); + + // Verify the error is thrown correctly + try { + await requestPromise; + assert.fail('Expected error was not thrown'); + } catch (e: any) { + assert.strictEqual(e.message, 'MPC -32601: Resource not found'); + assert.strictEqual(e.code, MCP.METHOD_NOT_FOUND); + } + }); + + test('should handle server requests', async () => { + // Simulate ping request from server + const pingRequest: MCP.JSONRPCRequest & MCP.PingRequest = { + jsonrpc: MCP.JSONRPC_VERSION, + id: 100, + method: 'ping' + }; + + transport.simulateReceiveMessage(pingRequest); + + // The handler should have sent a response + const sentMessages = transport.getSentMessages(); + const pingResponse = sentMessages.find(m => + 'id' in m && m.id === pingRequest.id && 'result' in m + ) as MCP.JSONRPCResponse; + + assert.ok(pingResponse, 'No ping response was sent'); + assert.deepStrictEqual(pingResponse.result, {}); + }); + + test('should handle roots list requests', async () => { + // Set roots + handler.roots = [ + { uri: 'file:///test/root1', name: 'Root 1' }, + { uri: 'file:///test/root2', name: 'Root 2' } + ]; + + // Simulate roots/list request from server + const rootsRequest: MCP.JSONRPCRequest & MCP.ListRootsRequest = { + jsonrpc: MCP.JSONRPC_VERSION, + id: 101, + method: 'roots/list' + }; + + transport.simulateReceiveMessage(rootsRequest); + + // The handler should have sent a response + const sentMessages = transport.getSentMessages(); + const rootsResponse = sentMessages.find(m => + 'id' in m && m.id === rootsRequest.id && 'result' in m + ) as MCP.JSONRPCResponse; + + assert.ok(rootsResponse, 'No roots/list response was sent'); + assert.strictEqual((rootsResponse.result as MCP.ListRootsResult).roots.length, 2); + assert.strictEqual((rootsResponse.result as MCP.ListRootsResult).roots[0].uri, 'file:///test/root1'); + }); + + test('should handle server notifications', async () => { + let progressNotificationReceived = false; + store.add(handler.onDidReceiveProgressNotification(notification => { + progressNotificationReceived = true; + assert.strictEqual(notification.method, 'notifications/progress'); + assert.strictEqual(notification.params.progressToken, 'token1'); + assert.strictEqual(notification.params.progress, 50); + })); + + // Simulate progress notification with correct format + const progressNotification: MCP.JSONRPCNotification & MCP.ProgressNotification = { + jsonrpc: MCP.JSONRPC_VERSION, + method: 'notifications/progress', + params: { + progressToken: 'token1', + progress: 50, + total: 100 + } + }; + + transport.simulateReceiveMessage(progressNotification); + assert.strictEqual(progressNotificationReceived, true); + }); + + test('should handle cancellation', async () => { + // Setup a new cancellation token source for this specific test + const testCts = store.add(new CancellationTokenSource()); + const requestPromise = handler.listResources(undefined, testCts.token); + + // Get the request ID + const sentMessages = transport.getSentMessages(); + const listResourcesRequest = sentMessages[2] as MCP.JSONRPCRequest; + const requestId = listResourcesRequest.id; + + // Cancel the request + testCts.cancel(); + + // Check that a cancellation notification was sent + const cancelNotification = transport.getSentMessages().find(m => + !('id' in m) && + 'method' in m && + m.method === 'notifications/cancelled' && + 'params' in m && + m.params && m.params.requestId === requestId + ); + + assert.ok(cancelNotification, 'No cancellation notification was sent'); + + // Verify the promise was cancelled + try { + await requestPromise; + assert.fail('Promise should have been cancelled'); + } catch (e) { + assert.strictEqual(e.name, 'Canceled'); + } + }); + + test('should handle cancelled notification from server', async () => { + // Setup request + const requestPromise = handler.listResources(); + + // Get the request ID + const sentMessages = transport.getSentMessages(); + const listResourcesRequest = sentMessages[2] as MCP.JSONRPCRequest; + const requestId = listResourcesRequest.id; + + // Simulate cancelled notification from server + const cancelledNotification: MCP.JSONRPCNotification & MCP.CancelledNotification = { + jsonrpc: MCP.JSONRPC_VERSION, + method: 'notifications/cancelled', + params: { + requestId + } + }; + + transport.simulateReceiveMessage(cancelledNotification); + + // Verify the promise was cancelled + try { + await requestPromise; + assert.fail('Promise should have been cancelled'); + } catch (e) { + assert.strictEqual(e.name, 'Canceled'); + } + }); + + test('should dispose properly and cancel pending requests', async () => { + // Setup multiple requests + const request1 = handler.listResources(); + const request2 = handler.listTools(); + + // Dispose the handler + handler.dispose(); + + // Verify all promises were cancelled + try { + await request1; + assert.fail('Promise 1 should have been cancelled'); + } catch (e) { + assert.strictEqual(e.name, 'Canceled'); + } + + try { + await request2; + assert.fail('Promise 2 should have been cancelled'); + } catch (e) { + assert.strictEqual(e.name, 'Canceled'); + } + }); + + test('should handle connection error by cancelling requests', async () => { + // Setup request + const requestPromise = handler.listResources(); + + // Simulate connection error + transport.setConnectionState({ + state: McpConnectionState.Kind.Error, + message: 'Connection lost' + }); + + // Verify the promise was cancelled + try { + await requestPromise; + assert.fail('Promise should have been cancelled'); + } catch (e) { + assert.strictEqual(e.name, 'Canceled'); + } + }); +}); diff --git a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInputModel.ts b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInputModel.ts index c0c4acc617b7..a110faf6a993 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInputModel.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/mergeEditorInputModel.ts @@ -4,9 +4,9 @@ *--------------------------------------------------------------------------------------------*/ import { assertFn } from '../../../../base/common/assert.js'; -import { BugIndicatingError } from '../../../../base/common/errors.js'; +import { BugIndicatingError, onUnexpectedError } from '../../../../base/common/errors.js'; import { Event } from '../../../../base/common/event.js'; -import { DisposableStore, IDisposable } from '../../../../base/common/lifecycle.js'; +import { DisposableStore, IDisposable, IReference } from '../../../../base/common/lifecycle.js'; import { derived, IObservable, observableFromEvent, observableValue } from '../../../../base/common/observable.js'; import { basename, isEqual } from '../../../../base/common/resources.js'; import Severity from '../../../../base/common/severity.js'; @@ -27,6 +27,8 @@ import { MergeEditorTelemetry } from './telemetry.js'; import { StorageCloseWithConflicts } from '../common/mergeEditor.js'; import { IEditorService } from '../../../services/editor/common/editorService.js'; import { ITextFileEditorModel, ITextFileSaveOptions, ITextFileService } from '../../../services/textfile/common/textfiles.js'; +import { ITextModel } from '../../../../editor/common/model.js'; +import { ILanguageService } from '../../../../editor/common/languages/language.js'; export interface MergeEditorArgs { base: URI; @@ -273,6 +275,8 @@ export class WorkspaceMergeEditorModeFactory implements IMergeEditorInputModelFa @IInstantiationService private readonly _instantiationService: IInstantiationService, @ITextModelService private readonly _textModelService: ITextModelService, @ITextFileService private readonly textFileService: ITextFileService, + @IModelService private readonly _modelService: IModelService, + @ILanguageService private readonly _languageService: ILanguageService, ) { } @@ -292,18 +296,33 @@ export class WorkspaceMergeEditorModeFactory implements IMergeEditorInputModelFa modelListener.add(this.textFileService.files.onDidCreate(handleDidCreate)); this.textFileService.files.models.forEach(handleDidCreate); - const [ + let [ base, result, input1Data, input2Data, ] = await Promise.all([ - this._textModelService.createModelReference(args.base), + this._textModelService.createModelReference(args.base).then>(v => ({ + object: v.object.textEditorModel, + dispose: () => v.dispose(), + })).catch(e => { + onUnexpectedError(e); + console.error(e); // Only file not found error should be handled ideally + return undefined; + }), this._textModelService.createModelReference(args.result), toInputData(args.input1, this._textModelService, store), toInputData(args.input2, this._textModelService, store), ]); + if (base === undefined) { + const tm = this._modelService.createModel('', this._languageService.createById(result.object.getLanguageId())); + base = { + dispose: () => { tm.dispose(); }, + object: tm + }; + } + store.add(base); store.add(result); @@ -321,7 +340,7 @@ export class WorkspaceMergeEditorModeFactory implements IMergeEditorInputModelFa const model = this._instantiationService.createInstance( MergeEditorModel, - base.object.textEditorModel, + base.object, input1Data, input2Data, result.object.textEditorModel, diff --git a/src/vs/workbench/contrib/mergeEditor/browser/utils.ts b/src/vs/workbench/contrib/mergeEditor/browser/utils.ts index d60d64bcb8d0..308db886d5b4 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/utils.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/utils.ts @@ -5,7 +5,7 @@ import { ArrayQueue, CompareResult } from '../../../../base/common/arrays.js'; import { onUnexpectedError } from '../../../../base/common/errors.js'; -import { DisposableStore, IDisposable, toDisposable } from '../../../../base/common/lifecycle.js'; +import { DisposableStore, IDisposable } from '../../../../base/common/lifecycle.js'; import { IObservable, autorunOpts } from '../../../../base/common/observable.js'; import { CodeEditorWidget } from '../../../../editor/browser/widget/codeEditor/codeEditorWidget.js'; import { IModelDeltaDecoration } from '../../../../editor/common/model.js'; @@ -85,19 +85,6 @@ export function elementAtOrUndefined(arr: T[], index: number): T | undefined return arr[index]; } -export function thenIfNotDisposed(promise: Promise, then: () => void): IDisposable { - let disposed = false; - promise.then(() => { - if (disposed) { - return; - } - then(); - }); - return toDisposable(() => { - disposed = true; - }); -} - export function setFields(obj: T, fields: Partial): T { return Object.assign(obj, fields); } @@ -154,4 +141,3 @@ export class PersistentStore { ); } } - diff --git a/src/vs/workbench/contrib/mergeEditor/browser/view/mergeEditor.ts b/src/vs/workbench/contrib/mergeEditor/browser/view/mergeEditor.ts index 32b12772bef7..666ad3f373ee 100644 --- a/src/vs/workbench/contrib/mergeEditor/browser/view/mergeEditor.ts +++ b/src/vs/workbench/contrib/mergeEditor/browser/view/mergeEditor.ts @@ -10,7 +10,7 @@ import { CancellationToken } from '../../../../../base/common/cancellation.js'; import { Color } from '../../../../../base/common/color.js'; import { BugIndicatingError, onUnexpectedError } from '../../../../../base/common/errors.js'; import { Emitter, Event } from '../../../../../base/common/event.js'; -import { Disposable, DisposableStore, IDisposable, MutableDisposable, toDisposable } from '../../../../../base/common/lifecycle.js'; +import { Disposable, DisposableStore, IDisposable, MutableDisposable, thenIfNotDisposed, toDisposable } from '../../../../../base/common/lifecycle.js'; import { autorun, autorunWithStore, IObservable, IReader, observableValue, transaction } from '../../../../../base/common/observable.js'; import { basename, isEqual } from '../../../../../base/common/resources.js'; import { isDefined } from '../../../../../base/common/types.js'; @@ -23,7 +23,6 @@ import { ICodeEditorViewState, ScrollType } from '../../../../../editor/common/e import { ITextModel } from '../../../../../editor/common/model.js'; import { ITextResourceConfigurationService } from '../../../../../editor/common/services/textResourceConfiguration.js'; import { localize } from '../../../../../nls.js'; -import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.js'; import { IContextKey, IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.js'; import { IEditorOptions, ITextEditorOptions, ITextResourceEditorInput } from '../../../../../platform/editor/common/editor.js'; import { IFileService } from '../../../../../platform/files/common/files.js'; @@ -39,8 +38,7 @@ import { readTransientState, writeTransientState } from '../../../codeEditor/bro import { MergeEditorInput } from '../mergeEditorInput.js'; import { IMergeEditorInputModel } from '../mergeEditorInputModel.js'; import { MergeEditorModel } from '../model/mergeEditorModel.js'; -import { deepMerge, PersistentStore, thenIfNotDisposed } from '../utils.js'; -import { observableConfigValue } from '../../../../../platform/observable/common/platformObservableUtils.js'; +import { deepMerge, PersistentStore } from '../utils.js'; import { BaseCodeEditorView } from './editors/baseCodeEditorView.js'; import { ScrollSynchronizer } from './scrollSynchronizer.js'; import { MergeEditorViewModel } from './viewModel.js'; @@ -90,22 +88,12 @@ export class MergeEditor extends AbstractTextEditor { return this.inputModel.get()?.model; } - private get inputsWritable(): boolean { - return !!this._configurationService.getValue('mergeEditor.writableInputs'); - } - private readonly viewZoneComputer = new ViewZoneComputer( this.input1View.editor, this.input2View.editor, this.inputResultView.editor, ); - protected readonly codeLensesVisible = observableConfigValue( - 'mergeEditor.showCodeLenses', - true, - this.configurationService, - ); - private readonly scrollSynchronizer = this._register(new ScrollSynchronizer(this._viewModel, this.input1View, this.input2View, this.baseView, this.inputResultView, this._layoutModeObs)); constructor( @@ -116,12 +104,10 @@ export class MergeEditor extends AbstractTextEditor { @IStorageService storageService: IStorageService, @IThemeService themeService: IThemeService, @ITextResourceConfigurationService textResourceConfigurationService: ITextResourceConfigurationService, - @IConfigurationService private readonly _configurationService: IConfigurationService, @IEditorService editorService: IEditorService, @IEditorGroupsService editorGroupService: IEditorGroupsService, @IFileService fileService: IFileService, - @ICodeEditorService private readonly _codeEditorService: ICodeEditorService, - @IConfigurationService private readonly configurationService: IConfigurationService + @ICodeEditorService private readonly _codeEditorService: ICodeEditorService ) { super(MergeEditor.ID, group, telemetryService, instantiation, storageService, textResourceConfigurationService, themeService, editorService, editorGroupService, fileService); } @@ -170,14 +156,18 @@ export class MergeEditor extends AbstractTextEditor { const inputOptions: ICodeEditorOptions = deepMerge(options, { minimap: { enabled: false }, glyphMargin: false, - lineNumbersMinChars: 2, - readOnly: !this.inputsWritable + lineNumbersMinChars: 2 }); - this.input1View.updateOptions(inputOptions); - this.input2View.updateOptions(inputOptions); + const readOnlyInputOptions: ICodeEditorOptions = deepMerge(inputOptions, { + readOnly: true, + readOnlyMessage: undefined + }); + + this.input1View.updateOptions(readOnlyInputOptions); + this.input2View.updateOptions(readOnlyInputOptions); this.baseViewOptions.set({ ...this.input2View.editor.getRawOptions() }, undefined); - this.inputResultView.updateOptions(options); + this.inputResultView.updateOptions(inputOptions); } protected getMainControl(): ICodeEditor | undefined { @@ -213,7 +203,6 @@ export class MergeEditor extends AbstractTextEditor { this.showNonConflictingChanges, ); - model.telemetry.reportMergeEditorOpened({ combinableConflictCount: model.combinableConflictCount, conflictCount: model.conflictCount, @@ -382,7 +371,7 @@ export class MergeEditor extends AbstractTextEditor { const resultViewZoneIds: string[] = []; const viewZones = this.viewZoneComputer.computeViewZones(reader, viewModel, { - codeLensesVisible: this.codeLensesVisible.read(reader), + codeLensesVisible: true, showNonConflictingChanges: this.showNonConflictingChanges.read(reader), shouldAlignBase, shouldAlignResult, diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/cellDiagnostics/cellDiagnosticEditorContrib.ts b/src/vs/workbench/contrib/notebook/browser/contrib/cellDiagnostics/cellDiagnosticEditorContrib.ts index 00b45c45d822..584fc890a929 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/cellDiagnostics/cellDiagnosticEditorContrib.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/cellDiagnostics/cellDiagnosticEditorContrib.ts @@ -11,10 +11,10 @@ import { IConfigurationService } from '../../../../../../platform/configuration/ import { CellKind, NotebookSetting } from '../../../common/notebookCommon.js'; import { INotebookEditor, INotebookEditorContribution } from '../../notebookBrowser.js'; import { registerNotebookContribution } from '../../notebookEditorExtensions.js'; -import { Iterable } from '../../../../../../base/common/iterator.js'; import { CodeCellViewModel } from '../../viewModel/codeCellViewModel.js'; import { Event } from '../../../../../../base/common/event.js'; import { IChatAgentService } from '../../../../chat/common/chatAgents.js'; +import { ChatAgentLocation } from '../../../../chat/common/constants.js'; export class CellDiagnostics extends Disposable implements INotebookEditorContribution { @@ -43,12 +43,17 @@ export class CellDiagnostics extends Disposable implements INotebookEditorContri })); } + private hasNotebookAgent(): boolean { + const agents = this.chatAgentService.getAgents(); + return !!agents.find(agent => agent.locations.includes(ChatAgentLocation.Notebook)); + } + private updateEnabled() { const settingEnabled = this.configurationService.getValue(NotebookSetting.cellFailureDiagnostics); - if (this.enabled && (!settingEnabled || Iterable.isEmpty(this.chatAgentService.getAgents()))) { + if (this.enabled && (!settingEnabled || !this.hasNotebookAgent())) { this.enabled = false; this.clearAll(); - } else if (!this.enabled && settingEnabled && !Iterable.isEmpty(this.chatAgentService.getAgents())) { + } else if (!this.enabled && settingEnabled && this.hasNotebookAgent()) { this.enabled = true; if (!this.listening) { this.listening = true; diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/cellDiagnostics/diagnosticCellStatusBarContrib.ts b/src/vs/workbench/contrib/notebook/browser/contrib/cellDiagnostics/diagnosticCellStatusBarContrib.ts index 0d7c0c3d00f1..f10ea92275cc 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/cellDiagnostics/diagnosticCellStatusBarContrib.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/cellDiagnostics/diagnosticCellStatusBarContrib.ts @@ -16,7 +16,7 @@ import { CodeCellViewModel } from '../../viewModel/codeCellViewModel.js'; import { INotebookCellStatusBarItem, CellStatusbarAlignment } from '../../../common/notebookCommon.js'; import { ICellExecutionError } from '../../../common/notebookExecutionStateService.js'; import { IChatAgentService } from '../../../../chat/common/chatAgents.js'; -import { Iterable } from '../../../../../../base/common/iterator.js'; +import { ChatAgentLocation } from '../../../../chat/common/constants.js'; export class DiagnosticCellStatusBarContrib extends Disposable implements INotebookEditorContribution { static id: string = 'workbench.notebook.statusBar.diagtnostic'; @@ -49,10 +49,15 @@ class DiagnosticCellStatusBarItem extends Disposable { this._register(autorun((reader) => this.updateSparkleItem(reader.readObservable(cell.executionErrorDiagnostic)))); } + private hasNotebookAgent(): boolean { + const agents = this.chatAgentService.getAgents(); + return !!agents.find(agent => agent.locations.includes(ChatAgentLocation.Notebook)); + } + private async updateSparkleItem(error: ICellExecutionError | undefined) { let item: INotebookCellStatusBarItem | undefined; - if (error?.location && !Iterable.isEmpty(this.chatAgentService.getAgents())) { + if (error?.location && this.hasNotebookAgent()) { const keybinding = this.keybindingService.lookupKeybinding(OPEN_CELL_FAILURE_ACTIONS_COMMAND_ID)?.getLabel(); const tooltip = localize('notebook.cell.status.diagnostic', "Quick Actions {0}", `(${keybinding})`); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/executionStatusBarItemController.ts b/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/executionStatusBarItemController.ts index 992fcddbbaaf..184c94dbaac8 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/executionStatusBarItemController.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/cellStatusBar/executionStatusBarItemController.ts @@ -288,7 +288,7 @@ class TimerCellStatusBarItem extends Disposable { this._deferredUpdate = disposableTimeout(() => { this._deferredUpdate = undefined; this._currentItemIds = this._notebookViewModel.deltaCellStatusBarItems(this._currentItemIds, [{ handle: this._cell.handle, items }]); - }, UPDATE_TIMER_GRACE_PERIOD); + }, UPDATE_TIMER_GRACE_PERIOD, this._store); } } else { this._deferredUpdate?.dispose(); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/contribution.ts b/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/contribution.ts deleted file mode 100644 index 6b8387bd9086..000000000000 --- a/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/contribution.ts +++ /dev/null @@ -1,17 +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 { INotebookModelSynchronizerFactory, NotebookModelSynchronizerFactory } from './notebookSynchronizer.js'; -import { INotebookOriginalModelReferenceFactory, NotebookOriginalModelReferenceFactory } from './notebookOriginalModelRefFactory.js'; -import { registerNotebookContribution } from '../../notebookEditorExtensions.js'; -import { InstantiationType, registerSingleton } from '../../../../../../platform/instantiation/common/extensions.js'; -import { INotebookOriginalCellModelFactory, OriginalNotebookCellModelFactory } from './notebookOriginalCellModelFactory.js'; -import { NotebookChatEditorControllerContrib } from './notebookChatEditController.js'; - - -registerNotebookContribution(NotebookChatEditorControllerContrib.ID, NotebookChatEditorControllerContrib); -registerSingleton(INotebookOriginalModelReferenceFactory, NotebookOriginalModelReferenceFactory, InstantiationType.Delayed); -registerSingleton(INotebookModelSynchronizerFactory, NotebookModelSynchronizerFactory, InstantiationType.Delayed); -registerSingleton(INotebookOriginalCellModelFactory, OriginalNotebookCellModelFactory, InstantiationType.Delayed); diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookCellDecorators.ts b/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookCellDecorators.ts deleted file mode 100644 index ec0d2f435115..000000000000 --- a/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookCellDecorators.ts +++ /dev/null @@ -1,361 +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 { DisposableStore, toDisposable } from '../../../../../../base/common/lifecycle.js'; -import { autorun, autorunWithStore, derived, observableFromEvent } from '../../../../../../base/common/observable.js'; -import { IChatEditingService, ChatEditingSessionState } from '../../../../chat/common/chatEditingService.js'; -import { INotebookEditor } from '../../notebookBrowser.js'; -import { ThrottledDelayer } from '../../../../../../base/common/async.js'; -import { ICodeEditor, IViewZone } from '../../../../../../editor/browser/editorBrowser.js'; -import { IEditorWorkerService } from '../../../../../../editor/common/services/editorWorker.js'; -import { EditorOption } from '../../../../../../editor/common/config/editorOptions.js'; -import { themeColorFromId } from '../../../../../../base/common/themables.js'; -import { RenderOptions, LineSource, renderLines } from '../../../../../../editor/browser/widget/diffEditor/components/diffEditorViewZones/renderLines.js'; -import { diffAddDecoration, diffWholeLineAddDecoration, diffDeleteDecoration } from '../../../../../../editor/browser/widget/diffEditor/registrations.contribution.js'; -import { IDocumentDiff } from '../../../../../../editor/common/diff/documentDiffProvider.js'; -import { ITextModel, TrackedRangeStickiness, MinimapPosition, IModelDeltaDecoration, OverviewRulerLane } from '../../../../../../editor/common/model.js'; -import { ModelDecorationOptions } from '../../../../../../editor/common/model/textModel.js'; -import { InlineDecoration, InlineDecorationType } from '../../../../../../editor/common/viewModel.js'; -import { Range } from '../../../../../../editor/common/core/range.js'; -import { NotebookCellTextModel } from '../../../common/model/notebookCellTextModel.js'; -import { INotebookOriginalCellModelFactory } from './notebookOriginalCellModelFactory.js'; -import { DetailedLineRangeMapping } from '../../../../../../editor/common/diff/rangeMapping.js'; -import { isEqual } from '../../../../../../base/common/resources.js'; -import { minimapGutterAddedBackground, minimapGutterDeletedBackground, minimapGutterModifiedBackground, overviewRulerAddedForeground, overviewRulerDeletedForeground, overviewRulerModifiedForeground } from '../../../../scm/common/quickDiff.js'; - - -export class NotebookCellDiffDecorator extends DisposableStore { - private _viewZones: string[] = []; - private readonly throttledDecorator = new ThrottledDelayer(50); - private diffForPreviouslyAppliedDecorators?: IDocumentDiff; - - private readonly perEditorDisposables = this.add(new DisposableStore()); - constructor( - notebookEditor: INotebookEditor, - public readonly modifiedCell: NotebookCellTextModel, - public readonly originalCell: NotebookCellTextModel, - @IChatEditingService private readonly _chatEditingService: IChatEditingService, - @IEditorWorkerService private readonly _editorWorkerService: IEditorWorkerService, - @INotebookOriginalCellModelFactory private readonly originalCellModelFactory: INotebookOriginalCellModelFactory, - - ) { - super(); - - const onDidChangeVisibleRanges = observableFromEvent(notebookEditor.onDidChangeVisibleRanges, () => notebookEditor.visibleRanges); - const editorObs = derived((r) => { - const visibleRanges = onDidChangeVisibleRanges.read(r); - const visibleCellHandles = visibleRanges.map(range => notebookEditor.getCellsInRange(range)).flat().map(c => c.handle); - if (!visibleCellHandles.includes(modifiedCell.handle)) { - return; - } - const editor = notebookEditor.codeEditors.find(item => item[0].handle === modifiedCell.handle)?.[1]; - if (editor?.getModel() !== this.modifiedCell.textModel) { - return; - } - return editor; - }); - - this.add(autorunWithStore((r, store) => { - const editor = editorObs.read(r); - this.perEditorDisposables.clear(); - - if (editor) { - store.add(editor.onDidChangeModel(() => { - this.perEditorDisposables.clear(); - })); - store.add(editor.onDidChangeModelContent(() => { - this.update(editor); - })); - store.add(editor.onDidChangeConfiguration((e) => { - if (e.hasChanged(EditorOption.fontInfo) || e.hasChanged(EditorOption.lineHeight)) { - this.update(editor); - } - })); - this.update(editor); - } - })); - - const shouldBeReadOnly = derived(this, r => { - const editorUri = editorObs.read(r)?.getModel()?.uri; - if (!editorUri) { - return false; - } - const sessions = this._chatEditingService.editingSessionsObs.read(r); - const session = sessions.find(s => s.entries.read(r).some(e => isEqual(e.modifiedURI, editorUri))); - return session?.state.read(r) === ChatEditingSessionState.StreamingEdits; - }); - - - let actualReadonly: boolean | undefined; - let actualDeco: 'off' | 'editable' | 'on' | undefined; - - this.add(autorun((r) => { - const editor = editorObs.read(r); - if (!editor) { - return; - } - const value = shouldBeReadOnly.read(r); - if (value) { - actualReadonly ??= editor.getOption(EditorOption.readOnly); - actualDeco ??= editor.getOption(EditorOption.renderValidationDecorations); - - editor.updateOptions({ - readOnly: true, - renderValidationDecorations: 'off' - }); - } else { - if (actualReadonly !== undefined && actualDeco !== undefined) { - editor.updateOptions({ - readOnly: actualReadonly, - renderValidationDecorations: actualDeco - }); - actualReadonly = undefined; - actualDeco = undefined; - } - } - })); - } - - public update(editor: ICodeEditor): void { - this.throttledDecorator.trigger(() => this._updateImpl(editor)); - } - - private async _updateImpl(editor: ICodeEditor) { - if (this.isDisposed) { - return; - } - if (editor.getOption(EditorOption.inDiffEditor)) { - this.perEditorDisposables.clear(); - return; - } - const model = editor.getModel(); - if (!model || model !== this.modifiedCell.textModel) { - this.perEditorDisposables.clear(); - return; - } - - const originalModel = this.getOrCreateOriginalModel(editor); - if (!originalModel) { - this.perEditorDisposables.clear(); - return; - } - const version = model.getVersionId(); - const diff = await this._editorWorkerService.computeDiff( - originalModel.uri, - model.uri, - { computeMoves: true, ignoreTrimWhitespace: false, maxComputationTimeMs: Number.MAX_SAFE_INTEGER }, - 'advanced' - ); - - - if (this.isDisposed) { - return; - } - - - if (diff && !diff.identical && originalModel && model === editor.getModel() && editor.getModel()?.getVersionId() === version) { - this._updateWithDiff(editor, originalModel, diff); - } else { - this.perEditorDisposables.clear(); - } - } - - private _originalModel?: ITextModel; - private getOrCreateOriginalModel(editor: ICodeEditor) { - if (!this._originalModel) { - const model = editor.getModel(); - if (!model) { - return; - } - this._originalModel = this.add(this.originalCellModelFactory.getOrCreate(model.uri, this.originalCell.getValue(), model.getLanguageId(), this.modifiedCell.cellKind)).object; - } - return this._originalModel; - } - - private _updateWithDiff(editor: ICodeEditor, originalModel: ITextModel | undefined, diff: IDocumentDiff): void { - if (areDiffsEqual(diff, this.diffForPreviouslyAppliedDecorators)) { - return; - } - this.perEditorDisposables.clear(); - const decorations = editor.createDecorationsCollection(); - this.perEditorDisposables.add(toDisposable(() => { - editor.changeViewZones((viewZoneChangeAccessor) => { - for (const id of this._viewZones) { - viewZoneChangeAccessor.removeZone(id); - } - }); - this._viewZones = []; - decorations.clear(); - this.diffForPreviouslyAppliedDecorators = undefined; - })); - - this.diffForPreviouslyAppliedDecorators = diff; - - const chatDiffAddDecoration = ModelDecorationOptions.createDynamic({ - ...diffAddDecoration, - stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges - }); - const chatDiffWholeLineAddDecoration = ModelDecorationOptions.createDynamic({ - ...diffWholeLineAddDecoration, - stickiness: TrackedRangeStickiness.NeverGrowsWhenTypingAtEdges, - }); - const createOverviewDecoration = (overviewRulerColor: string, minimapColor: string) => { - return ModelDecorationOptions.createDynamic({ - description: 'chat-editing-decoration', - overviewRuler: { color: themeColorFromId(overviewRulerColor), position: OverviewRulerLane.Left }, - minimap: { color: themeColorFromId(minimapColor), position: MinimapPosition.Gutter }, - }); - }; - const modifiedDecoration = createOverviewDecoration(overviewRulerModifiedForeground, minimapGutterModifiedBackground); - const addedDecoration = createOverviewDecoration(overviewRulerAddedForeground, minimapGutterAddedBackground); - const deletedDecoration = createOverviewDecoration(overviewRulerDeletedForeground, minimapGutterDeletedBackground); - - editor.changeViewZones((viewZoneChangeAccessor) => { - for (const id of this._viewZones) { - viewZoneChangeAccessor.removeZone(id); - } - this._viewZones = []; - const modifiedDecorations: IModelDeltaDecoration[] = []; - const mightContainNonBasicASCII = originalModel?.mightContainNonBasicASCII(); - const mightContainRTL = originalModel?.mightContainRTL(); - const renderOptions = RenderOptions.fromEditor(editor); - - for (const diffEntry of diff.changes) { - const originalRange = diffEntry.original; - if (originalModel) { - originalModel.tokenization.forceTokenization(Math.max(1, originalRange.endLineNumberExclusive - 1)); - } - const source = new LineSource( - (originalRange.length && originalModel) ? originalRange.mapToLineArray(l => originalModel.tokenization.getLineTokens(l)) : [], - [], - mightContainNonBasicASCII, - mightContainRTL, - ); - const decorations: InlineDecoration[] = []; - for (const i of diffEntry.innerChanges || []) { - decorations.push(new InlineDecoration( - i.originalRange.delta(-(diffEntry.original.startLineNumber - 1)), - diffDeleteDecoration.className!, - InlineDecorationType.Regular - )); - modifiedDecorations.push({ - range: i.modifiedRange, options: chatDiffAddDecoration - }); - } - if (!diffEntry.modified.isEmpty) { - modifiedDecorations.push({ - range: diffEntry.modified.toInclusiveRange()!, options: chatDiffWholeLineAddDecoration - }); - } - - if (diffEntry.original.isEmpty) { - // insertion - modifiedDecorations.push({ - range: diffEntry.modified.toInclusiveRange()!, - options: addedDecoration - }); - } else if (diffEntry.modified.isEmpty) { - // deletion - modifiedDecorations.push({ - range: new Range(diffEntry.modified.startLineNumber - 1, 1, diffEntry.modified.startLineNumber, 1), - options: deletedDecoration - }); - } else { - // modification - modifiedDecorations.push({ - range: diffEntry.modified.toInclusiveRange()!, - options: modifiedDecoration - }); - } - const domNode = document.createElement('div'); - domNode.className = 'chat-editing-original-zone view-lines line-delete monaco-mouse-cursor-text'; - const result = renderLines(source, renderOptions, decorations, domNode); - - const isCreatedContent = decorations.length === 1 && decorations[0].range.isEmpty() && decorations[0].range.startLineNumber === 1; - if (!isCreatedContent) { - const viewZoneData: IViewZone = { - afterLineNumber: diffEntry.modified.startLineNumber - 1, - heightInLines: result.heightInLines, - domNode, - ordinal: 50000 + 2 // more than https://github.com/microsoft/vscode/blob/bf52a5cfb2c75a7327c9adeaefbddc06d529dcad/src/vs/workbench/contrib/inlineChat/browser/inlineChatZoneWidget.ts#L42 - }; - - this._viewZones.push(viewZoneChangeAccessor.addZone(viewZoneData)); - } - } - - decorations.set(modifiedDecorations); - }); - } -} - -function areDiffsEqual(a: IDocumentDiff | undefined, b: IDocumentDiff | undefined): boolean { - if (a && b) { - if (a.changes.length !== b.changes.length) { - return false; - } - if (a.moves.length !== b.moves.length) { - return false; - } - if (!areLineRangeMappinsEqual(a.changes, b.changes)) { - return false; - } - if (!a.moves.some((move, i) => { - const bMove = b.moves[i]; - if (!areLineRangeMappinsEqual(move.changes, bMove.changes)) { - return true; - } - if (move.lineRangeMapping.changedLineCount !== bMove.lineRangeMapping.changedLineCount) { - return true; - } - if (!move.lineRangeMapping.modified.equals(bMove.lineRangeMapping.modified)) { - return true; - } - if (!move.lineRangeMapping.original.equals(bMove.lineRangeMapping.original)) { - return true; - } - return false; - })) { - return false; - } - return true; - } else if (!a && !b) { - return true; - } else { - return false; - } -} - -function areLineRangeMappinsEqual(a: readonly DetailedLineRangeMapping[], b: readonly DetailedLineRangeMapping[]): boolean { - if (a.length !== b.length) { - return false; - } - if (a.some((c, i) => { - const bChange = b[i]; - if (c.changedLineCount !== bChange.changedLineCount) { - return true; - } - if ((c.innerChanges || []).length !== (bChange.innerChanges || []).length) { - return true; - } - if ((c.innerChanges || []).some((innerC, innerIdx) => { - const bInnerC = bChange.innerChanges![innerIdx]; - if (!innerC.modifiedRange.equalsRange(bInnerC.modifiedRange)) { - return true; - } - if (!innerC.originalRange.equalsRange(bInnerC.originalRange)) { - return true; - } - return false; - })) { - return true; - } - - return false; - })) { - return false; - } - return true; -} diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookChatActionsOverlay.ts b/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookChatActionsOverlay.ts deleted file mode 100644 index c02bbabb0844..000000000000 --- a/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookChatActionsOverlay.ts +++ /dev/null @@ -1,321 +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 { Disposable, DisposableStore, MutableDisposable, toDisposable } from '../../../../../../base/common/lifecycle.js'; -import { CellEditState, getNotebookEditorFromEditorPane, INotebookEditor, INotebookViewModel } from '../../notebookBrowser.js'; -import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; -import { HiddenItemStrategy, MenuWorkbenchToolBar } from '../../../../../../platform/actions/browser/toolbar.js'; -import { MenuId } from '../../../../../../platform/actions/common/actions.js'; -import { ActionViewItem } from '../../../../../../base/browser/ui/actionbar/actionViewItems.js'; -import { ActionRunner, IAction, IActionRunner } from '../../../../../../base/common/actions.js'; -import { $ } from '../../../../../../base/browser/dom.js'; -import { IChatEditingService, IModifiedFileEntry } from '../../../../chat/common/chatEditingService.js'; -import { ACTIVE_GROUP, IEditorService } from '../../../../../services/editor/common/editorService.js'; -import { autorun, autorunWithStore, IObservable, ISettableObservable, observableFromEvent, observableValue } from '../../../../../../base/common/observable.js'; -import { isEqual } from '../../../../../../base/common/resources.js'; -import { CellDiffInfo } from '../../diff/notebookDiffViewModel.js'; -import { AcceptAction, navigationBearingFakeActionId, RejectAction } from '../../../../chat/browser/chatEditing/chatEditingEditorActions.js'; -import { INotebookDeletedCellDecorator } from '../../diff/inlineDiff/notebookDeletedCellDecorator.js'; - -export class NotebookChatActionsOverlayController extends Disposable { - constructor( - private readonly notebookEditor: INotebookEditor, - cellDiffInfo: IObservable, - deletedCellDecorator: INotebookDeletedCellDecorator, - @IChatEditingService private readonly _chatEditingService: IChatEditingService, - @IInstantiationService instantiationService: IInstantiationService, - ) { - super(); - - const notebookModel = observableFromEvent(this.notebookEditor.onDidChangeModel, e => e); - - this._register(autorunWithStore((r, store) => { - const model = notebookModel.read(r); - if (!model) { - return; - } - const sessions = this._chatEditingService.editingSessionsObs.read(r); - const session = sessions.find(s => s.readEntry(model.uri, r)); - if (!session) { - return; - } - - const entries = session.entries.read(r); - const idx = entries.findIndex(e => isEqual(e.modifiedURI, model.uri)); - if (idx >= 0) { - const entry = entries[idx]; - const nextEntry = entries[(idx + 1) % entries.length]; - const previousEntry = entries[(idx - 1 + entries.length) % entries.length]; - store.add(instantiationService.createInstance(NotebookChatActionsOverlay, notebookEditor, entry, cellDiffInfo, nextEntry, previousEntry, deletedCellDecorator)); - } - })); - } -} - -// Copied from src/vs/workbench/contrib/chat/browser/chatEditorOverlay.ts (until we unify these) -export class NotebookChatActionsOverlay extends Disposable { - private readonly focusedDiff = observableValue('focusedDiff', undefined); - private readonly toolbarNode: HTMLElement; - private added: boolean = false; - constructor( - private readonly notebookEditor: INotebookEditor, - entry: IModifiedFileEntry, - cellDiffInfo: IObservable, - nextEntry: IModifiedFileEntry, - previousEntry: IModifiedFileEntry, - deletedCellDecorator: INotebookDeletedCellDecorator, - @IEditorService _editorService: IEditorService, - @IInstantiationService instaService: IInstantiationService, - ) { - super(); - - this._register(notebookEditor.onDidBlurWidget(() => { - if (getNotebookEditorFromEditorPane(_editorService.activeEditorPane) !== notebookEditor) { - this.focusedDiff.set(undefined, undefined); - } - })); - this._register(notebookEditor.onDidChangeActiveEditor(e => { - if (e !== notebookEditor) { - this.focusedDiff.set(undefined, undefined); - } - })); - - this.toolbarNode = $('div'); - this.toolbarNode.classList.add('notebook-chat-editor-overlay-widget'); - this._register(toDisposable(() => this.hide())); - - this._register(autorun(r => { - const diffs = cellDiffInfo.read(r); - if (diffs?.some(d => d.type !== 'unchanged')) { - this.show(); - } else { - this.hide(); - } - })); - - const focusedDiff = this.focusedDiff; - const _toolbar = instaService.createInstance(MenuWorkbenchToolBar, this.toolbarNode, MenuId.ChatEditingEditorContent, { - telemetrySource: 'chatEditor.overlayToolbar', - hiddenItemStrategy: HiddenItemStrategy.Ignore, - toolbarOptions: { - primaryGroup: () => true, - useSeparatorsInPrimaryActions: true - }, - menuOptions: { renderShortTitle: true }, - actionViewItemProvider: (action, options) => { - - if (action.id === navigationBearingFakeActionId) { - return new class extends ActionViewItem { - constructor() { - super(undefined, action, { ...options, icon: false, label: false, keybindingNotRenderedWithLabel: true }); - } - }; - } - - if (action.id === AcceptAction.ID || action.id === RejectAction.ID) { - return new class extends ActionViewItem { - private readonly _reveal = this._store.add(new MutableDisposable()); - constructor() { - super(undefined, action, { ...options, icon: false, label: true, keybindingNotRenderedWithLabel: true }); - } - override set actionRunner(actionRunner: IActionRunner) { - super.actionRunner = actionRunner; - - const store = new DisposableStore(); - - store.add(actionRunner.onWillRun(_e => { - notebookEditor.focus(); - })); - store.add(actionRunner.onDidRun(e => { - if (e.action !== this.action) { - return; - } - if (entry === nextEntry) { - return; - } - - })); - - this._reveal.value = store; - } - override get actionRunner(): IActionRunner { - return super.actionRunner; - } - }; - } - // Override next/previous with our implementation. - if (action.id === 'chatEditor.action.navigateNext' || action.id === 'chatEditor.action.navigatePrevious') { - return new class extends ActionViewItem { - constructor() { - super(undefined, action, { ...options, icon: true, label: false, keybindingNotRenderedWithLabel: true }); - } - override set actionRunner(_: IActionRunner) { - const next = action.id === 'chatEditor.action.navigateNext' ? nextEntry : previousEntry; - const direction = action.id === 'chatEditor.action.navigateNext' ? 'next' : 'previous'; - super.actionRunner = new NextPreviousChangeActionRunner(notebookEditor, cellDiffInfo, entry, next, direction, _editorService, deletedCellDecorator, focusedDiff); - } - override get actionRunner(): IActionRunner { - return super.actionRunner; - } - }; - } - return undefined; - } - - }); - - this._register(_toolbar); - } - - private show() { - if (!this.added) { - this.notebookEditor.getDomNode().appendChild(this.toolbarNode); - this.added = true; - } - } - - private hide() { - if (this.added) { - this.notebookEditor.getDomNode().removeChild(this.toolbarNode); - this.added = false; - } - } - - -} - -class NextPreviousChangeActionRunner extends ActionRunner { - constructor( - private readonly notebookEditor: INotebookEditor, - private readonly cellDiffInfo: IObservable, - private readonly entry: IModifiedFileEntry, - private readonly next: IModifiedFileEntry, - private readonly direction: 'next' | 'previous', - private readonly editorService: IEditorService, - private readonly deletedCellDecorator: INotebookDeletedCellDecorator, - private readonly focusedDiff: ISettableObservable - ) { - super(); - } - protected override async runAction(_action: IAction, _context?: unknown): Promise { - const viewModel = this.notebookEditor.getViewModel(); - const activeCell = this.notebookEditor.activeCellAndCodeEditor; - const cellDiff = this.cellDiffInfo.read(undefined); - if (!viewModel || !cellDiff?.length || (!activeCell && this.focusedDiff.read(undefined))) { - return this.goToNextEntry(); - } - - const nextDiff = this.getNextCellDiff(cellDiff, viewModel); - if (nextDiff && (await this.focusDiff(nextDiff, viewModel))) { - return; - } - - return this.goToNextEntry(); - } - - /** - * @returns `true` if focused to the next diff - */ - private async focusDiff(diff: CellDiffInfo, viewModel: INotebookViewModel) { - if (diff.type === 'delete') { - const top = this.deletedCellDecorator.getTop(diff.originalCellIndex); - if (typeof top === 'number') { - this.focusedDiff.set(diff, undefined); - this.notebookEditor.setScrollTop(top); - return true; - } - } else { - const index = diff.modifiedCellIndex; - this.focusedDiff.set(diff, undefined); - await this.notebookEditor.focusNotebookCell(viewModel.viewCells[index], 'container'); - this.notebookEditor.revealInViewAtTop(viewModel.viewCells[index]); - viewModel.viewCells[index].updateEditState(CellEditState.Editing, 'chatEdit'); - return true; - } - return false; - } - - private getNextCellDiff(cellDiffInfo: CellDiffInfo[], viewModel: INotebookViewModel) { - const activeCell = this.notebookEditor.activeCellAndCodeEditor; - const currentCellIndex = activeCell ? viewModel.viewCells.findIndex(c => c.handle === activeCell[0].handle) : (this.direction === 'next' ? 0 : viewModel.viewCells.length - 1); - if (this.focusedDiff.read(undefined)) { - const changes = cellDiffInfo.filter(d => d.type !== 'unchanged'); - const idx = changes.findIndex(d => d === this.focusedDiff.read(undefined)); - if (idx >= 0) { - const next = this.direction === 'next' ? idx + 1 : idx - 1; - if (next >= 0 && next < changes.length) { - return changes[next]; - } - } - } else if (this.direction === 'next') { - let currentIndex = 0; - let next: CellDiffInfo | undefined; - cellDiffInfo - .forEach((d, i) => { - if (next) { - return; - } - if (d.type === 'insert' || d.type === 'modified') { - if (d.modifiedCellIndex > currentCellIndex) { - next = d; - } - currentIndex = d.modifiedCellIndex; - } else if (d.type === 'unchanged') { - currentIndex = d.modifiedCellIndex; - } else if (currentIndex >= currentCellIndex) { - next = d; - } - }); - if (next) { - return next; - } - } else { - let currentIndex = 0; - let previous: CellDiffInfo | undefined; - cellDiffInfo - .forEach((d, i) => { - if (d.type === 'insert' || d.type === 'modified') { - if (d.modifiedCellIndex < currentCellIndex) { - previous = d; - } - currentIndex = d.modifiedCellIndex; - } else if (d.type === 'unchanged') { - currentIndex = d.modifiedCellIndex; - } else if (currentIndex <= currentCellIndex) { - previous = d; - } - }); - if (previous) { - return previous; - } - } - - if (this.canGoToNextEntry()) { - return; - } - - return this.direction === 'next' ? cellDiffInfo[0] : cellDiffInfo[cellDiffInfo.length - 1]; - } - - - private canGoToNextEntry() { - return this.entry !== this.next; - } - - private async goToNextEntry() { - if (!this.canGoToNextEntry()) { - return; - } - // For now just go to next/previous file. - this.focusedDiff.set(undefined, undefined); - await this.editorService.openEditor({ - resource: this.next.modifiedURI, - options: { - revealIfOpened: false, - revealIfVisible: false, - } - }, ACTIVE_GROUP); - } -} diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookChatEditController.ts b/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookChatEditController.ts deleted file mode 100644 index 8ed24d724ea3..000000000000 --- a/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookChatEditController.ts +++ /dev/null @@ -1,209 +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 { Disposable, dispose, IReference, toDisposable } from '../../../../../../base/common/lifecycle.js'; -import { autorun, derived, derivedWithStore, observableFromEvent, observableValue } from '../../../../../../base/common/observable.js'; -import { IChatEditingService, WorkingSetEntryState } from '../../../../chat/common/chatEditingService.js'; -import { NotebookTextModel } from '../../../common/model/notebookTextModel.js'; -import { INotebookEditor, INotebookEditorContribution } from '../../notebookBrowser.js'; -import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; -import { NotebookCellTextModel } from '../../../common/model/notebookCellTextModel.js'; -import { IConfigurationService } from '../../../../../../platform/configuration/common/configuration.js'; -import { NotebookCellDiffDecorator } from './notebookCellDecorators.js'; -import { INotebookModelSynchronizerFactory, NotebookModelSynchronizer } from './notebookSynchronizer.js'; -import { INotebookOriginalModelReferenceFactory } from './notebookOriginalModelRefFactory.js'; -import { debouncedObservable } from '../../../../../../base/common/observableInternal/utils.js'; -import { CellDiffInfo } from '../../diff/notebookDiffViewModel.js'; -import { NotebookChatActionsOverlayController } from './notebookChatActionsOverlay.js'; -import { IContextKey, IContextKeyService } from '../../../../../../platform/contextkey/common/contextkey.js'; -import { Event } from '../../../../../../base/common/event.js'; -import { ctxNotebookHasEditorModification } from './notebookChatEditContext.js'; -import { NotebookDeletedCellDecorator } from '../../diff/inlineDiff/notebookDeletedCellDecorator.js'; -import { NotebookInsertedCellDecorator } from '../../diff/inlineDiff/notebookInsertedCellDecorator.js'; - -export class NotebookChatEditorControllerContrib extends Disposable implements INotebookEditorContribution { - - public static readonly ID: string = 'workbench.notebook.chatEditorController'; - readonly _serviceBrand: undefined; - constructor( - notebookEditor: INotebookEditor, - @IInstantiationService instantiationService: IInstantiationService, - @IConfigurationService configurationService: IConfigurationService, - - ) { - super(); - this._register(instantiationService.createInstance(NotebookChatEditorController, notebookEditor)); - } -} - - -class NotebookChatEditorController extends Disposable { - private readonly deletedCellDecorator: NotebookDeletedCellDecorator; - private readonly insertedCellDecorator: NotebookInsertedCellDecorator; - private readonly _ctxHasEditorModification: IContextKey; - constructor( - private readonly notebookEditor: INotebookEditor, - @IChatEditingService private readonly _chatEditingService: IChatEditingService, - @INotebookOriginalModelReferenceFactory private readonly originalModelRefFactory: INotebookOriginalModelReferenceFactory, - @INotebookModelSynchronizerFactory private readonly synchronizerFactory: INotebookModelSynchronizerFactory, - @IInstantiationService private readonly instantiationService: IInstantiationService, - @IContextKeyService contextKeyService: IContextKeyService, - ) { - super(); - this._ctxHasEditorModification = ctxNotebookHasEditorModification.bindTo(contextKeyService); - this.deletedCellDecorator = this._register(instantiationService.createInstance(NotebookDeletedCellDecorator, notebookEditor)); - this.insertedCellDecorator = this._register(instantiationService.createInstance(NotebookInsertedCellDecorator, notebookEditor)); - const notebookModel = observableFromEvent(this.notebookEditor.onDidChangeModel, e => e); - const originalModel = observableValue('originalModel', undefined); - // We need to render viewzones only when the viewmodel is attached (i.e. list view is ready). - // https://github.com/microsoft/vscode/issues/234718 - const readyToRenderViewzones = observableValue('viewModelAttached', false); - this._register(Event.once(this.notebookEditor.onDidAttachViewModel)(() => readyToRenderViewzones.set(true, undefined))); - const onDidChangeVisibleRanges = debouncedObservable(observableFromEvent(this.notebookEditor.onDidChangeVisibleRanges, () => this.notebookEditor.visibleRanges), 50); - const decorators = new Map(); - - let updatedCellDecoratorsOnceBefore = false; - let updatedDeletedInsertedDecoratorsOnceBefore = false; - - - const clearDecorators = () => { - dispose(Array.from(decorators.values())); - decorators.clear(); - this.deletedCellDecorator.clear(); - this.insertedCellDecorator.clear(); - }; - - this._register(toDisposable(() => clearDecorators())); - - let notebookSynchronizer: IReference; - const entryObs = derived((r) => { - const model = notebookModel.read(r); - if (!model) { - return; - } - const sessions = this._chatEditingService.editingSessionsObs.read(r); - return sessions.map(s => s.readEntry(model.uri, r)).find(r => !!r); - }).recomputeInitiallyAndOnChange(this._store); - - - this._register(autorun(r => { - const entry = entryObs.read(r); - const model = notebookModel.read(r); - if (!entry || !model || entry.state.read(r) !== WorkingSetEntryState.Modified) { - clearDecorators(); - } - })); - - const notebookDiffInfo = derivedWithStore(this, (r, store) => { - const entry = entryObs.read(r); - const model = notebookModel.read(r); - if (!entry || !model) { - // If entry is undefined, then revert the changes to the notebook. - if (notebookSynchronizer && model) { - notebookSynchronizer.object.revert(); - } - return observableValue<{ - cellDiff: CellDiffInfo[]; - modelVersion: number; - } | undefined>('DefaultDiffIno', undefined); - } - - notebookSynchronizer = notebookSynchronizer || this._register(this.synchronizerFactory.getOrCreate(model)); - this.originalModelRefFactory.getOrCreate(entry, model.viewType).then(ref => originalModel.set(this._register(ref).object, undefined)); - - return notebookSynchronizer.object.diffInfo; - }).recomputeInitiallyAndOnChange(this._store).flatten(); - - const notebookCellDiffInfo = notebookDiffInfo.map(d => d?.cellDiff); - this._register(instantiationService.createInstance(NotebookChatActionsOverlayController, notebookEditor, notebookCellDiffInfo, this.deletedCellDecorator)); - - this._register(autorun(r => { - // If we have a new entry for the file, then clear old decorators. - // User could be cycling through different edit sessions (Undo Last Edit / Redo Last Edit). - entryObs.read(r); - clearDecorators(); - })); - - this._register(autorun(r => { - // If there's no diff info, then we either accepted or rejected everything. - const diffs = notebookDiffInfo.read(r); - if (!diffs || !diffs.cellDiff.length) { - clearDecorators(); - this._ctxHasEditorModification.reset(); - } else { - this._ctxHasEditorModification.set(true); - } - })); - - this._register(autorun(r => { - const entry = entryObs.read(r); - const diffInfo = notebookDiffInfo.read(r); - const modified = notebookModel.read(r); - const original = originalModel.read(r); - onDidChangeVisibleRanges.read(r); - - if (!entry || !modified || !original || !diffInfo) { - return; - } - if (diffInfo && updatedCellDecoratorsOnceBefore && (diffInfo.modelVersion !== modified.versionId)) { - return; - } - - updatedCellDecoratorsOnceBefore = true; - const validDiffDecorators = new Set(); - diffInfo.cellDiff.forEach((diff) => { - if (diff.type === 'modified') { - const modifiedCell = modified.cells[diff.modifiedCellIndex]; - const originalCell = original.cells[diff.originalCellIndex]; - const editor = this.notebookEditor.codeEditors.find(([vm,]) => vm.handle === modifiedCell.handle)?.[1]; - - if (editor) { - const currentDecorator = decorators.get(modifiedCell); - if ((currentDecorator?.modifiedCell !== modifiedCell || currentDecorator?.originalCell !== originalCell)) { - currentDecorator?.dispose(); - const decorator = this.instantiationService.createInstance(NotebookCellDiffDecorator, notebookEditor, modifiedCell, originalCell); - decorators.set(modifiedCell, decorator); - validDiffDecorators.add(decorator); - this._register(editor.onDidDispose(() => { - decorator.dispose(); - if (decorators.get(modifiedCell) === decorator) { - decorators.delete(modifiedCell); - } - })); - } else if (currentDecorator) { - validDiffDecorators.add(currentDecorator); - } - } - } - }); - - // Dispose old decorators - decorators.forEach((v, cell) => { - if (!validDiffDecorators.has(v)) { - v.dispose(); - decorators.delete(cell); - } - }); - })); - - this._register(autorun(r => { - const entry = entryObs.read(r); - const diffInfo = notebookDiffInfo.read(r); - const modified = notebookModel.read(r); - const original = originalModel.read(r); - const ready = readyToRenderViewzones.read(r); - if (!ready || !entry || !modified || !original || !diffInfo) { - return; - } - if (diffInfo && updatedDeletedInsertedDecoratorsOnceBefore && (diffInfo.modelVersion !== modified.versionId)) { - return; - } - updatedDeletedInsertedDecoratorsOnceBefore = true; - this.insertedCellDecorator.apply(diffInfo.cellDiff); - this.deletedCellDecorator.apply(diffInfo.cellDiff, original); - })); - } - -} diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookSynchronizer.ts b/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookSynchronizer.ts deleted file mode 100644 index 5e99759a2c80..000000000000 --- a/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookSynchronizer.ts +++ /dev/null @@ -1,498 +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 { isEqual } from '../../../../../../base/common/resources.js'; -import { Disposable, IReference, ReferenceCollection } from '../../../../../../base/common/lifecycle.js'; -import { IChatEditingService, IModifiedFileEntry } from '../../../../chat/common/chatEditingService.js'; -import { INotebookService } from '../../../common/notebookService.js'; -import { bufferToStream, streamToBuffer, VSBuffer } from '../../../../../../base/common/buffer.js'; -import { NotebookTextModel } from '../../../common/model/notebookTextModel.js'; -import { raceCancellation, ThrottledDelayer } from '../../../../../../base/common/async.js'; -import { CellDiffInfo, computeDiff, prettyChanges } from '../../diff/notebookDiffViewModel.js'; -import { CancellationToken, CancellationTokenSource } from '../../../../../../base/common/cancellation.js'; -import { INotebookEditorWorkerService } from '../../../common/services/notebookWorkerService.js'; -import { CellEditType, ICellDto2, ICellEditOperation, ICellReplaceEdit, NotebookData, NotebookSetting } from '../../../common/notebookCommon.js'; -import { URI } from '../../../../../../base/common/uri.js'; -import { IConfigurationService } from '../../../../../../platform/configuration/common/configuration.js'; -import { EditOperation } from '../../../../../../editor/common/core/editOperation.js'; -import { INotebookLoggingService } from '../../../common/notebookLoggingService.js'; -import { filter } from '../../../../../../base/common/objects.js'; -import { INotebookEditorModelResolverService } from '../../../common/notebookEditorModelResolverService.js'; -import { IChatService } from '../../../../chat/common/chatService.js'; -import { createDecorator, IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; -import { INotebookOriginalModelReferenceFactory } from './notebookOriginalModelRefFactory.js'; -import { autorunWithStore, derived, IObservable, observableValue } from '../../../../../../base/common/observable.js'; -import { SaveReason } from '../../../../../common/editor.js'; -import { ITextModelService } from '../../../../../../editor/common/services/resolverService.js'; -import { SnapshotContext } from '../../../../../services/workingCopy/common/fileWorkingCopy.js'; -import { INotebookEditorService } from '../../services/notebookEditorService.js'; -import { CellEditState } from '../../notebookBrowser.js'; -import { IModelService } from '../../../../../../editor/common/services/model.js'; - - -export const INotebookModelSynchronizerFactory = createDecorator('INotebookModelSynchronizerFactory'); - -export interface INotebookModelSynchronizerFactory { - readonly _serviceBrand: undefined; - getOrCreate(model: NotebookTextModel): IReference; -} - -class NotebookModelSynchronizerReferenceCollection extends ReferenceCollection { - constructor(@IInstantiationService private readonly instantiationService: IInstantiationService) { - super(); - } - protected override createReferencedObject(_key: string, model: NotebookTextModel): NotebookModelSynchronizer { - return this.instantiationService.createInstance(NotebookModelSynchronizer, model); - } - protected override destroyReferencedObject(_key: string, object: NotebookModelSynchronizer): void { - object.dispose(); - } -} - -export class NotebookModelSynchronizerFactory implements INotebookModelSynchronizerFactory { - readonly _serviceBrand: undefined; - private readonly _data: NotebookModelSynchronizerReferenceCollection; - constructor(@IInstantiationService instantiationService: IInstantiationService) { - this._data = instantiationService.createInstance(NotebookModelSynchronizerReferenceCollection); - } - - getOrCreate(model: NotebookTextModel): IReference { - return this._data.acquire(model.uri.toString(), model); - } -} - - -export class NotebookModelSynchronizer extends Disposable { - private readonly throttledUpdateNotebookModel = new ThrottledDelayer(200); - private _diffInfo = observableValue<{ cellDiff: CellDiffInfo[]; modelVersion: number } | undefined>('diffInfo', undefined); - public get diffInfo(): IObservable<{ cellDiff: CellDiffInfo[]; modelVersion: number } | undefined> { - return this._diffInfo; - } - private snapshot?: { bytes: VSBuffer; dirty: boolean }; - private isEditFromUs: boolean = false; - private isTextEditFromUs: boolean = false; - private isReverting = false; - private throttledTextModelUpdate = new ThrottledDelayer(100); - constructor( - private readonly model: NotebookTextModel, - @IChatEditingService _chatEditingService: IChatEditingService, - @INotebookService private readonly notebookService: INotebookService, - @INotebookEditorService private readonly notebookEditorService: INotebookEditorService, - @IChatService chatService: IChatService, - @IModelService private readonly modelService: IModelService, - @ITextModelService private readonly textModelService: ITextModelService, - @INotebookLoggingService private readonly logService: INotebookLoggingService, - @IConfigurationService private readonly configurationService: IConfigurationService, - @INotebookEditorWorkerService private readonly notebookEditorWorkerService: INotebookEditorWorkerService, - @INotebookEditorModelResolverService private readonly notebookModelResolverService: INotebookEditorModelResolverService, - @INotebookOriginalModelReferenceFactory private readonly originalModelRefFactory: INotebookOriginalModelReferenceFactory, - ) { - super(); - - const entryObs = derived((r) => { - const sessions = _chatEditingService.editingSessionsObs.read(r); - return sessions.map(s => s.readEntry(model.uri, r)).find(r => !!r); - }).recomputeInitiallyAndOnChange(this._store); - - - this._register(chatService.onDidPerformUserAction(async e => { - const entry = entryObs.read(undefined); - if (!entry) { - return; - } - if (e.action.kind === 'chatEditingSessionAction' && !e.action.hasRemainingEdits && isEqual(e.action.uri, entry.modifiedURI)) { - if (e.action.outcome === 'accepted') { - await this.accept(entry); - await this.createSnapshot(); - this._diffInfo.set(undefined, undefined); - } - else if (e.action.outcome === 'rejected') { - await this.revertImpl(); - } - } - })); - - const updateNotebookModel = (entry: IModifiedFileEntry, token: CancellationToken) => { - this.throttledUpdateNotebookModel.trigger(() => this.updateNotebookModel(entry, token)); - }; - - let snapshotCreated = false; - this._register(autorunWithStore((r, store) => { - const entry = entryObs.read(r); - if (!entry) { - return; - } - if (!snapshotCreated) { - this.createSnapshot(); - snapshotCreated = true; - } - - const modifiedModel = this.modelService.getModel(entry.modifiedURI); - if (!modifiedModel) { - return; - } - let cancellationToken = store.add(new CancellationTokenSource()); - store.add(modifiedModel.onDidChangeContent(async () => { - const originalModel = this.modelService.getModel(entry.originalURI); - if (originalModel && !this.isTextEditFromUs && !modifiedModel.isDisposed() && !originalModel.isDisposed() && modifiedModel.getValue() !== originalModel.getValue()) { - cancellationToken = store.add(new CancellationTokenSource()); - updateNotebookModel(entry, cancellationToken.token); - } - })); - - updateNotebookModel(entry, cancellationToken.token); - })); - - this._register(model.onDidChangeContent(() => { - // Track changes from the user. - if (!this.isEditFromUs && this.snapshot) { - this.snapshot.dirty = true; - const entry = entryObs.get(); - if (entry) { - this.throttledTextModelUpdate.trigger(() => this.updateTextDocumentModel(entry)); - } - } - })); - } - - private async createSnapshot() { - const [serializer, ref] = await Promise.all([ - this.getNotebookSerializer(), - this.notebookModelResolverService.resolve(this.model.uri) - ]); - - try { - const data: NotebookData = { - metadata: filter(this.model.metadata, key => !serializer.options.transientDocumentMetadata[key]), - cells: [], - }; - - const indentAmount = this.model.metadata.indentAmount || ref.object.notebook.metadata.indentAmount || undefined; - if (typeof indentAmount === 'string' && indentAmount) { - // This is required for ipynb serializer to preserve the whitespace in the notebook. - data.metadata.indentAmount = indentAmount; - } - - let outputSize = 0; - for (const cell of this.model.cells) { - const cellData: ICellDto2 = { - cellKind: cell.cellKind, - language: cell.language, - mime: cell.mime, - source: cell.getValue(), - outputs: [], - internalMetadata: cell.internalMetadata - }; - - const outputSizeLimit = this.configurationService.getValue(NotebookSetting.outputBackupSizeLimit) * 1024; - if (outputSizeLimit > 0) { - cell.outputs.forEach(output => { - output.outputs.forEach(item => { - outputSize += item.data.byteLength; - }); - }); - if (outputSize > outputSizeLimit) { - return; - } - } - - cellData.outputs = !serializer.options.transientOutputs ? cell.outputs : []; - cellData.metadata = filter(cell.metadata, key => !serializer.options.transientCellMetadata[key]); - - data.cells.push(cellData); - } - - const bytes = await serializer.notebookToData(data); - this.snapshot = { bytes, dirty: ref.object.isDirty() }; - } finally { - ref.dispose(); - } - } - - public async revert() { - await this.revertImpl(); - } - - private async revertImpl(): Promise { - if (!this.snapshot || this.isReverting) { - return; - } - this.isReverting = true; - try { - // NOTE: We must save if the notebook model was not already dirty. - // Today ModifiedFileEntry class will save the text model to get rid of dirty indicator. - // If we do not save the notebook model, then ipynb json text document will get saved in ModifiedFileEntry class, - // and that results in ipynb being saved without going to serializer. - // Serializer is responsible for adding new line to ipynb files, and that new line will not be added when saving ipynb text document. - // As a result of this, reverting (creating new edit sessions), result in ipynb files without new line at the end meaning we still end up with a saved ipynb file with changes. - // Hence we must ensure ipynb notebooks are always saved through serializer. - // But do this only if the notebook model was not already dirty. - await this.updateNotebook(this.snapshot.bytes, !this.snapshot.dirty); - } - finally { - this.isReverting = false; - this._diffInfo.set(undefined, undefined); - } - } - - private async updateNotebook(bytes: VSBuffer, saveForRevert: boolean) { - const oldEditIsFromus = this.isEditFromUs; - this.isEditFromUs = true; - const ref = await this.notebookModelResolverService.resolve(this.model.uri); - try { - const serializer = await this.getNotebookSerializer(); - const data = await serializer.dataToNotebook(bytes); - this.model.reset(data.cells, data.metadata, serializer.options); - if (saveForRevert) { - // When reverting/creating a new session ModifiedFileEntry will revert and save changes to ipynb text document first, and save the file. - // This happens in the NotebookSyncrhonizerService which is called from SimpleNotebookEditorModel (NotebookEditorModel.ts). - // However when creating new sessions, the modified File entry will not exist as its a whole new session, - // As a result we aren't able to save the ipynb text document and match the last modified date time. - // Hence the work around implemented in SimpleNotebookEditorModel does not work. - // Thus we must save the file here igorning the modified since time, but only when reverting. - await ref.object.save({ reason: SaveReason.EXPLICIT, force: true, ignoreModifiedSince: true } as any); - } - } finally { - ref.dispose(); - this.isEditFromUs = oldEditIsFromus; - } - } - - private async accept(entry: IModifiedFileEntry) { - const modifiedModel = this.modelService.getModel(entry.modifiedURI); - if (!modifiedModel) { - return; - } - const content = modifiedModel.getValue(); - await this.updateNotebook(VSBuffer.fromString(content), false); - this._diffInfo.set(undefined, undefined); - - // The original notebook model needs to be updated with the latest content. - const stream = await this.notebookService.createNotebookTextDocumentSnapshot(this.model.uri, SnapshotContext.Save, CancellationToken.None); - const originalModel = await this.getOriginalModel(entry); - await this.notebookService.restoreNotebookTextModelFromSnapshot(originalModel.uri, originalModel.viewType, stream); - } - - private async updateTextDocumentModel(entry: IModifiedFileEntry) { - const modifiedModel = this.modelService.getModel(entry.modifiedURI); - if (!modifiedModel) { - return; - } - const stream = await this.notebookService.createNotebookTextDocumentSnapshot(this.model.uri, SnapshotContext.Save, CancellationToken.None); - const buffer = await streamToBuffer(stream); - const text = new TextDecoder().decode(buffer.buffer); - this.isTextEditFromUs = true; - modifiedModel.pushEditOperations(null, [{ range: modifiedModel.getFullModelRange(), text }], () => null); - this.isTextEditFromUs = false; - } - - async getNotebookSerializer() { - const info = await this.notebookService.withNotebookDataProvider(this.model.viewType); - return info.serializer; - } - - private _originalModel?: Promise; - private async getOriginalModel(entry: IModifiedFileEntry): Promise { - if (!this._originalModel) { - this._originalModel = this.originalModelRefFactory.getOrCreate(entry, this.model.viewType).then(ref => { - if (this._store.isDisposed) { - ref.dispose(); - return ref.object; - } else { - return this._register(ref).object; - } - }); - } - return this._originalModel; - } - - private async updateNotebookModel(entry: IModifiedFileEntry, token: CancellationToken) { - const modifiedModel = this.modelService.getModel(entry.modifiedURI); - if (!modifiedModel) { - return; - } - - const modifiedModelVersion = modifiedModel.getVersionId(); - const currentModel = this.model; - const modelVersion = currentModel?.versionId ?? 0; - const modelWithChatEdits = await this.getModifiedModelForDiff(entry, token); - if (!modelWithChatEdits || token.isCancellationRequested || !currentModel) { - return; - } - const originalModel = await this.getOriginalModel(entry); - // This is the total diff from the original model to the model with chat edits. - const cellDiffInfo = (await this.computeDiff(originalModel, modelWithChatEdits, token))?.cellDiffInfo; - // This is the diff from the current model to the model with chat edits. - const cellDiffInfoToApplyEdits = (await this.computeDiff(currentModel, modelWithChatEdits, token))?.cellDiffInfo; - const currentVersion = modifiedModel.getVersionId(); - if (!cellDiffInfo || !cellDiffInfoToApplyEdits || token.isCancellationRequested || currentVersion !== modifiedModelVersion || modelVersion !== currentModel.versionId) { - return; - } - if (cellDiffInfoToApplyEdits.every(d => d.type === 'unchanged')) { - return; - } - - // All edits from here on are from us. - this.isEditFromUs = true; - try { - const edits: ICellReplaceEdit[] = []; - const mappings = new Map(); - - // First Delete. - const deletedIndexes: number[] = []; - await Promise.all(cellDiffInfoToApplyEdits.reverse().map(async diff => { - if (diff.type === 'delete') { - deletedIndexes.push(diff.originalCellIndex); - edits.push({ - editType: CellEditType.Replace, - index: diff.originalCellIndex, - cells: [], - count: 1 - }); - } - })); - if (edits.length) { - currentModel.applyEdits(edits, true, undefined, () => undefined, undefined, false); - edits.length = 0; - } - - const notebookEditor = this.notebookEditorService.retrieveExistingWidgetFromURI(this.model.uri)?.value; - - // Next insert. - cellDiffInfoToApplyEdits.reverse().forEach(diff => { - if (diff.type === 'modified' || diff.type === 'unchanged') { - mappings.set(diff.modifiedCellIndex, diff.originalCellIndex); - } - if (diff.type === 'insert') { - const originalIndex = mappings.get(diff.modifiedCellIndex - 1) ?? 0; - mappings.set(diff.modifiedCellIndex, originalIndex); - const index = currentModel.cells.length ? originalIndex + 1 : originalIndex; - const cell = modelWithChatEdits.cells[diff.modifiedCellIndex]; - const newCell: ICellDto2 = - { - source: cell.getValue(), - cellKind: cell.cellKind, - language: cell.language, - outputs: cell.outputs.map(output => output.asDto()), - mime: cell.mime, - metadata: cell.metadata, - collapseState: cell.collapseState, - internalMetadata: cell.internalMetadata - }; - edits.push({ - editType: CellEditType.Replace, - index, - cells: [newCell], - count: 0 - }); - } - }); - if (edits.length) { - currentModel.applyEdits(edits, true, undefined, () => undefined, undefined, false); - for (const edit of edits.filter(e => e.editType === CellEditType.Replace)) { - const cell = currentModel.cells[edit.index]; - if (cell) { - const cellViewModel = notebookEditor?.getCellByHandle(cell.handle); - cellViewModel?.updateEditState(CellEditState.Editing, 'chatEdit'); - } - } - edits.length = 0; - } - - // Finally update - await Promise.all(cellDiffInfoToApplyEdits.map(async diff => { - if (diff.type === 'modified') { - const cell = currentModel.cells[diff.originalCellIndex]; - // Ensure the models of these cells have been loaded before we update them. - const cellModelRef = await this.textModelService.createModelReference(cell.uri); - try { - const modifiedCell = modelWithChatEdits.cells[diff.modifiedCellIndex]; - if (modifiedCell.cellKind === cell.cellKind) { - const cellViewModel = notebookEditor?.getCellByHandle(cell.handle); - cellViewModel?.updateEditState(CellEditState.Editing, 'chatEdit'); - const textModel = cellModelRef.object.textEditorModel; - textModel.pushEditOperations(null, [ - EditOperation.replace(textModel.getFullModelRange(), modifiedCell.getValue()) - ], () => null); - } else { - const newCellDto: ICellDto2 = - { - source: modifiedCell.getValue(), - cellKind: modifiedCell.cellKind, - language: modifiedCell.language, - outputs: modifiedCell.outputs.map(output => output.asDto()), - mime: modifiedCell.mime, - metadata: modifiedCell.metadata, - collapseState: modifiedCell.collapseState, - internalMetadata: modifiedCell.internalMetadata - }; - const edit: ICellEditOperation = { - editType: CellEditType.Replace, - index: diff.originalCellIndex, - cells: [newCellDto], - count: 1 - }; - currentModel.applyEdits([edit], true, undefined, () => undefined, undefined, false); - const newCell = currentModel.cells[diff.originalCellIndex]; - const cellViewModel = notebookEditor?.getCellByHandle(newCell.handle); - cellViewModel?.updateEditState(CellEditState.Editing, 'chatEdit'); - } - } finally { - cellModelRef.dispose(); - } - } - })); - - if (edits.length) { - currentModel.applyEdits(edits, true, undefined, () => undefined, undefined, false); - } - this._diffInfo.set({ cellDiff: cellDiffInfo, modelVersion: currentModel.versionId }, undefined); - } - finally { - this.isEditFromUs = false; - } - } - private previousUriOfModelForDiff?: URI; - - private async getModifiedModelForDiff(entry: IModifiedFileEntry, token: CancellationToken): Promise { - const modifiedModel = this.modelService.getModel(entry.modifiedURI); - if (!modifiedModel) { - return; - } - const text = modifiedModel.getValue(); - const bytes = VSBuffer.fromString(text); - const uri = entry.modifiedURI.with({ scheme: `NotebookChatEditorController.modifiedScheme${Date.now().toString()}` }); - const stream = bufferToStream(bytes); - if (this.previousUriOfModelForDiff) { - this.notebookService.getNotebookTextModel(this.previousUriOfModelForDiff)?.dispose(); - } - this.previousUriOfModelForDiff = uri; - try { - const model = await this.notebookService.createNotebookTextModel(this.model.viewType, uri, stream); - if (token.isCancellationRequested) { - model.dispose(); - return; - } - this._register(model); - return model; - } catch (ex) { - this.logService.warn('NotebookChatEdit', `Failed to deserialize Notebook for ${uri.toString}, ${ex.message}`); - this.logService.debug('NotebookChatEdit', ex.toString()); - return; - } - } - - async computeDiff(original: NotebookTextModel, modified: NotebookTextModel, token: CancellationToken) { - const diffResult = await raceCancellation(this.notebookEditorWorkerService.computeDiff(original.uri, modified.uri), token); - if (!diffResult || token.isCancellationRequested) { - // after await the editor might be disposed. - return; - } - - prettyChanges(original, modified, diffResult.cellsDiff); - - return computeDiff(original, modified, diffResult); - } -} diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookSynchronizerService.ts b/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookSynchronizerService.ts deleted file mode 100644 index 5d788c4c0b49..000000000000 --- a/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookSynchronizerService.ts +++ /dev/null @@ -1,94 +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 { IFileService } from '../../../../../../platform/files/common/files.js'; -import { IStoredFileWorkingCopy, IStoredFileWorkingCopyModel, StoredFileWorkingCopy } from '../../../../../services/workingCopy/common/storedFileWorkingCopy.js'; -import { IUntitledFileWorkingCopy } from '../../../../../services/workingCopy/common/untitledFileWorkingCopy.js'; -import { IStoredFileWorkingCopySaveParticipantContext, IWorkingCopyFileService } from '../../../../../services/workingCopy/common/workingCopyFileService.js'; -import { IChatEditingService } from '../../../../chat/common/chatEditingService.js'; -import { NotebookFileWorkingCopyModel } from '../../../common/notebookEditorModel.js'; -import { INotebookSynchronizerService } from '../../../common/notebookSynchronizerService.js'; -import { Disposable } from '../../../../../../base/common/lifecycle.js'; -import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; -import { NotebookSaveParticipant } from '../saveParticipants/saveParticipants.js'; -import { CancellationToken } from '../../../../../../base/common/cancellation.js'; -import { IProgress, IProgressStep } from '../../../../../../platform/progress/common/progress.js'; -import { IEditorService } from '../../../../../services/editor/common/editorService.js'; -import { INotebookService } from '../../../common/notebookService.js'; -import { ChatEditingModifiedDocumentEntry } from '../../../../chat/browser/chatEditing/chatEditingModifiedDocumentEntry.js'; -import { SaveReason } from '../../../../../common/editor.js'; - -class NotebookSynchronizerSaveParticipant extends NotebookSaveParticipant { - constructor( - @IEditorService editorService: IEditorService, - @IChatEditingService private readonly _chatEditingService: IChatEditingService, - @IFileService protected readonly _fileService: IFileService, - @INotebookService private readonly _notebookService: INotebookService, - ) { - super(editorService); - } - - override async participate(workingCopy: IStoredFileWorkingCopy, context: IStoredFileWorkingCopySaveParticipantContext, progress: IProgress, token: CancellationToken): Promise { - const sessions = this._chatEditingService.editingSessionsObs.get(); - const session = sessions.find(s => s.getEntry(workingCopy.resource)); - if (!session) { - return; - } - - if (!this._notebookService.hasSupportedNotebooks(workingCopy.resource)) { - return; - } - - const entry = session.getEntry(workingCopy.resource); - - if (entry && entry instanceof ChatEditingModifiedDocumentEntry) { - await entry.docFileEditorModel.save({ reason: SaveReason.EXPLICIT, ignoreModifiedSince: true }); - } - - const inWorkingSet = session.workingSet.has(workingCopy.resource); - - if (!(entry && entry instanceof ChatEditingModifiedDocumentEntry) && !inWorkingSet) { - // file not in working set, no need to continue - return; - } - - if (workingCopy instanceof StoredFileWorkingCopy) { - const metadata = await this._fileService.stat(workingCopy.resource); - if (workingCopy.lastResolvedFileStat) { - workingCopy.lastResolvedFileStat = { - ...workingCopy.lastResolvedFileStat, - ...metadata - }; - } - } - } -} - -export class NotebookSynchronizerService extends Disposable implements INotebookSynchronizerService { - _serviceBrand: undefined; - - constructor( - @IChatEditingService private readonly _chatEditingService: IChatEditingService, - @IFileService protected readonly _fileService: IFileService, - @INotebookService private readonly _notebookService: INotebookService, - @IInstantiationService private readonly _instantiationService: IInstantiationService, - @IWorkingCopyFileService private readonly _workingCopyFileService: IWorkingCopyFileService) { - super(); - this._register(this._workingCopyFileService.addSaveParticipant(this._instantiationService.createInstance(NotebookSynchronizerSaveParticipant))); - } - - async revert(workingCopy: IStoredFileWorkingCopy | IUntitledFileWorkingCopy) { - // check if we have mirror document - const resource = workingCopy.resource; - if (!this._notebookService.hasSupportedNotebooks(workingCopy.resource)) { - return; - } - const sessions = this._chatEditingService.editingSessionsObs.get(); - const entry = sessions.map(s => s.getEntry(resource)).find(r => !!r); - if (entry instanceof ChatEditingModifiedDocumentEntry) { - await entry.docFileEditorModel.revert({ soft: true }); - } - } -} diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts b/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts index d700b962cd94..f2de65d5bdc1 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/find/notebookFindReplaceWidget.ts @@ -330,6 +330,8 @@ export abstract class SimpleFindReplaceWidget extends Widget { ) { super(); + this._register(this._state); + const findFilters = this._configurationService.getValue<{ markupSource: boolean; markupPreview: boolean; diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/multicursor/notebookMulticursor.ts b/src/vs/workbench/contrib/notebook/browser/contrib/multicursor/notebookMulticursor.ts index 8d0938fceac9..116d5d3fab9d 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/multicursor/notebookMulticursor.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/multicursor/notebookMulticursor.ts @@ -31,7 +31,6 @@ import { indentOfLine } from '../../../../../../editor/common/model/textModel.js import { ITextModelService } from '../../../../../../editor/common/services/resolverService.js'; import { ICoordinatesConverter } from '../../../../../../editor/common/viewModel.js'; import { ViewModelEventsCollector } from '../../../../../../editor/common/viewModelEventDispatcher.js'; -import { WordHighlighterContribution } from '../../../../../../editor/contrib/wordHighlighter/browser/wordHighlighter.js'; import { IAccessibilityService } from '../../../../../../platform/accessibility/common/accessibility.js'; import { MenuId, registerAction2 } from '../../../../../../platform/actions/common/actions.js'; import { IConfigurationService } from '../../../../../../platform/configuration/common/configuration.js'; @@ -886,21 +885,6 @@ export class NotebookMultiCursorController extends Disposable implements INotebo cell.decorationIds, newDecorations ); - - /** - * TODO: @Yoyokrazy debt - * goal: draw decorations for occurrence higlight on the cursor blink cycle - * - * Trigger WH with delay: x ms (x = cursor blink cycle) - * -> start = Date() - * -> WordHighlighter -> compute - * -> end = Date() - * -> delay = x - ((end - start) % x) - */ - const matchingEditor = this.notebookEditor.codeEditors.find(cellEditor => cellEditor[0] === cell.cellViewModel); - if (matchingEditor) { - WordHighlighterContribution.get(matchingEditor[1])?.wordHighlighter?.trigger(); - } }); } @@ -1051,7 +1035,7 @@ class NotebookAddMatchToMultiSelectionAction extends NotebookAction { constructor() { super({ id: NOTEBOOK_ADD_FIND_MATCH_TO_SELECTION_ID, - title: localize('addFindMatchToSelection', "Add Selection To Next Find Match"), + title: localize('addFindMatchToSelection', "Add Selection to Next Find Match"), precondition: ContextKeyExpr.and( ContextKeyExpr.equals('config.notebook.multiCursor.enabled', true), NOTEBOOK_IS_ACTIVE_EDITOR, diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookInlineVariables.ts b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookInlineVariables.ts index badfa1415ae0..571038352440 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookInlineVariables.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookInlineVariables.ts @@ -9,28 +9,26 @@ import { Event } from '../../../../../../base/common/event.js'; import { Disposable, IDisposable } from '../../../../../../base/common/lifecycle.js'; import { ResourceMap } from '../../../../../../base/common/map.js'; import { isEqual } from '../../../../../../base/common/resources.js'; -import { format, noBreakWhitespace } from '../../../../../../base/common/strings.js'; -import { Constants } from '../../../../../../base/common/uint.js'; +import { format } from '../../../../../../base/common/strings.js'; import { Position } from '../../../../../../editor/common/core/position.js'; import { Range } from '../../../../../../editor/common/core/range.js'; +import { StandardTokenType } from '../../../../../../editor/common/encodedTokenAttributes.js'; import { InlineValueContext, InlineValueText, InlineValueVariableLookup } from '../../../../../../editor/common/languages.js'; -import { IModelDeltaDecoration, InjectedTextCursorStops, ITextModel } from '../../../../../../editor/common/model.js'; +import { IModelDeltaDecoration, ITextModel } from '../../../../../../editor/common/model.js'; import { ILanguageFeaturesService } from '../../../../../../editor/common/services/languageFeatures.js'; import { localize } from '../../../../../../nls.js'; import { registerAction2 } from '../../../../../../platform/actions/common/actions.js'; import { IConfigurationService } from '../../../../../../platform/configuration/common/configuration.js'; import { ServicesAccessor } from '../../../../../../platform/instantiation/common/instantiation.js'; +import { createInlineValueDecoration } from '../../../../debug/browser/debugEditorContribution.js'; import { IDebugService, State } from '../../../../debug/common/debug.js'; import { NotebookSetting } from '../../../common/notebookCommon.js'; import { ICellExecutionStateChangedEvent, INotebookExecutionStateService, NotebookExecutionType } from '../../../common/notebookExecutionStateService.js'; -import { INotebookKernelMatchResult, INotebookKernelService, VariablesResult } from '../../../common/notebookKernelService.js'; +import { INotebookKernelService, VariablesResult } from '../../../common/notebookKernelService.js'; import { INotebookActionContext, NotebookAction } from '../../controller/coreActions.js'; import { ICellViewModel, INotebookEditor, INotebookEditorContribution } from '../../notebookBrowser.js'; import { registerNotebookContribution } from '../../notebookEditorExtensions.js'; -// value from debug, may need to keep an eye on and shorter to account for cells having a narrower viewport width -const MAX_INLINE_DECORATOR_LENGTH = 150; // Max string length of each inline decorator. If exceeded ... is added - class InlineSegment { constructor(public column: number, public text: string) { } @@ -45,6 +43,8 @@ export class NotebookInlineVariablesController extends Disposable implements INo private currentCancellationTokenSources = new ResourceMap(); + private static readonly MAX_CELL_LINES = 5000; // Skip extremely large cells + constructor( private readonly notebookEditor: INotebookEditor, @INotebookKernelService private readonly notebookKernelService: INotebookKernelService, @@ -136,57 +136,61 @@ export class NotebookInlineVariablesController extends Disposable implements INo const fullCellRange = new Range(1, 1, lastLine, lastColumn); const promises = providers.flatMap(provider => Promise.resolve(provider.provideInlineValues(model, fullCellRange, ctx, token)).then(async (result) => { - if (result) { + if (!result) { + return; + } - let kernel: INotebookKernelMatchResult; - const kernelVars: VariablesResult[] = []; - if (result.some(iv => iv.type === 'variable')) { // if anyone will need a lookup, get vars now to avoid needing to do it multiple times - if (!this.notebookEditor.hasModel()) { - return; // should not happen, a cell will be executed - } - kernel = this.notebookKernelService.getMatchingKernel(this.notebookEditor.textModel); - const variables = kernel.selected?.provideVariables(event.notebook, undefined, 'named', 0, token); - if (!variables) { - return; - } + const notebook = this.notebookEditor.textModel; + if (!notebook) { + return; + } + + const kernel = this.notebookKernelService.getMatchingKernel(notebook); + const kernelVars: VariablesResult[] = []; + if (result.some(iv => iv.type === 'variable')) { // if anyone will need a lookup, get vars now to avoid needing to do it multiple times + if (!this.notebookEditor.hasModel()) { + return; // should not happen, a cell will be executed + } + const variables = kernel.selected?.provideVariables(event.notebook, undefined, 'named', 0, token); + if (variables) { for await (const v of variables) { kernelVars.push(v); } } + } - for (const iv of result) { - let text: string | undefined = undefined; - switch (iv.type) { - case 'text': - text = (iv as InlineValueText).text; - break; - case 'variable': { - const name = (iv as InlineValueVariableLookup).variableName; - if (!name) { - continue; // skip to next var, no valid name to lookup with - } - const value = kernelVars.find(v => v.name === name)?.value; - if (!value) { - continue; - } - text = format('{0} = {1}', name, value); - break; + for (const iv of result) { + let text: string | undefined = undefined; + switch (iv.type) { + case 'text': + text = (iv as InlineValueText).text; + break; + case 'variable': { + const name = (iv as InlineValueVariableLookup).variableName; + if (!name) { + continue; // skip to next var, no valid name to lookup with } - case 'expression': { - continue; // no active debug session, so evaluate would break + const value = kernelVars.find(v => v.name === name)?.value; + if (!value) { + continue; } + text = format('{0} = {1}', name, value); + break; + } + case 'expression': { + continue; // no active debug session, so evaluate would break } + } - if (text) { - const line = iv.range.startLineNumber; - let lineSegments = lineDecorations.get(line); - if (!lineSegments) { - lineSegments = []; - lineDecorations.set(line, lineSegments); - } - if (!lineSegments.some(iv => iv.text === text)) { // de-dupe - lineSegments.push(new InlineSegment(iv.range.startColumn, text)); - } + if (text) { + const line = iv.range.startLineNumber; + let lineSegments = lineDecorations.get(line); + if (!lineSegments) { + lineSegments = []; + lineDecorations.set(line, lineSegments); + } + if (!lineSegments.some(iv => iv.text === text)) { // de-dupe + lineSegments.push(new InlineSegment(iv.range.startColumn, text)); } } } @@ -199,10 +203,18 @@ export class NotebookInlineVariablesController extends Disposable implements INo // sort line segments and concatenate them into a decoration lineDecorations.forEach((segments, line) => { if (segments.length > 0) { - segments = segments.sort((a, b) => a.column - b.column); + segments.sort((a, b) => a.column - b.column); const text = segments.map(s => s.text).join(', '); - inlineDecorations.push(...this.createNotebookInlineValueDecoration(line, text)); - + const editorWidth = cell.layoutInfo.editorWidth; + const fontInfo = cell.layoutInfo.fontInfo; + if (fontInfo && cell.textModel) { + const base = Math.floor((editorWidth - 50) / fontInfo.typicalHalfwidthCharacterWidth); + const lineLength = cell.textModel.getLineLength(line); + const available = Math.max(0, base - lineLength); + inlineDecorations.push(...createInlineValueDecoration(line, text, 'nb', undefined, available)); + } else { + inlineDecorations.push(...createInlineValueDecoration(line, text, 'nb')); + } } }); @@ -227,10 +239,19 @@ export class NotebookInlineVariablesController extends Disposable implements INo return; } + // Skip processing for extremely large cells + if (document.getLineCount() > NotebookInlineVariablesController.MAX_CELL_LINES) { + return; + } + const inlineDecorations: IModelDeltaDecoration[] = []; const processedVars = new Set(); + // Get both function ranges and comment ranges const functionRanges = this.getFunctionRanges(document); + const commentedRanges = this.getCommentedRanges(document); + const ignoredRanges = [...functionRanges, ...commentedRanges]; + const lineDecorations = new Map(); // For each variable name found in the kernel results for (const varName of varNames) { @@ -238,44 +259,71 @@ export class NotebookInlineVariablesController extends Disposable implements INo continue; } - // Look for variable usage globally - const regex = new RegExp(`\\b${varName}\\b`, 'g'); - let lastMatchOutsideFunction: { line: number; column: number } | null = null; + // Look for variable usage globally - using word boundaries to ensure exact matches + const regex = new RegExp(`\\b${varName}\\b(?!\\w)`, 'g'); + let lastMatchOutsideIgnored: { line: number; column: number } | null = null; + let foundMatch = false; + // Scan lines in reverse to find last occurrence first const lines = document.getValue().split('\n'); - for (let lineNumber = 0; lineNumber < lines.length; lineNumber++) { + for (let lineNumber = lines.length - 1; lineNumber >= 0; lineNumber--) { const line = lines[lineNumber]; let match: RegExpExecArray | null; while ((match = regex.exec(line)) !== null) { - const pos = new Position(lineNumber + 1, match.index + 1); - let isInFunction = false; - - // Check if this usage is within any function range - for (const range of functionRanges) { - if (range.containsPosition(pos)) { - isInFunction = true; - break; - } - } + const startIndex = match.index; + const pos = new Position(lineNumber + 1, startIndex + 1); - if (!isInFunction) { - lastMatchOutsideFunction = { + // Check if this position is in any ignored range (function or comment) + if (!this.isPositionInRanges(pos, ignoredRanges)) { + lastMatchOutsideIgnored = { line: lineNumber + 1, - column: match.index + 1 + column: startIndex + 1 }; + foundMatch = true; + break; // Take first match in reverse order (which is last chronologically) } } + + if (foundMatch) { + break; // We found our last valid occurrence, no need to check earlier lines + } } - if (lastMatchOutsideFunction) { + if (lastMatchOutsideIgnored) { const inlineVal = varName + ' = ' + vars.find(v => v.name === varName)?.value; - inlineDecorations.push(...this.createNotebookInlineValueDecoration(lastMatchOutsideFunction.line, inlineVal)); + + let lineSegments = lineDecorations.get(lastMatchOutsideIgnored.line); + if (!lineSegments) { + lineSegments = []; + lineDecorations.set(lastMatchOutsideIgnored.line, lineSegments); + } + if (!lineSegments.some(iv => iv.text === inlineVal)) { // de-dupe + lineSegments.push(new InlineSegment(lastMatchOutsideIgnored.column, inlineVal)); + } } processedVars.add(varName); } + // sort line segments and concatenate them into a decoration + lineDecorations.forEach((segments, line) => { + if (segments.length > 0) { + segments.sort((a, b) => a.column - b.column); + const text = segments.map(s => s.text).join(', '); + const editorWidth = cell.layoutInfo.editorWidth; + const fontInfo = cell.layoutInfo.fontInfo; + if (fontInfo && cell.textModel) { + const base = Math.floor((editorWidth - 50) / fontInfo.typicalHalfwidthCharacterWidth); + const lineLength = cell.textModel.getLineLength(line); + const available = Math.max(0, base - lineLength); + inlineDecorations.push(...createInlineValueDecoration(line, text, 'nb', undefined, available)); + } else { + inlineDecorations.push(...createInlineValueDecoration(line, text, 'nb')); + } + } + }); + if (inlineDecorations.length > 0) { this.updateCellInlineDecorations(cell, inlineDecorations); this.initCellContentListener(cell); @@ -378,6 +426,157 @@ export class NotebookInlineVariablesController extends Disposable implements INo return functionRanges; } + private getCommentedRanges(document: ITextModel): Range[] { + return this._getCommentedRanges(document); + } + + private _getCommentedRanges(document: ITextModel): Range[] { + try { + return this.getCommentedRangesByAccurateTokenization(document); + } catch (e) { + // Fall back to manual parsing if tokenization fails + return this.getCommentedRangesByManualParsing(document); + } + } + + private getCommentedRangesByAccurateTokenization(document: ITextModel): Range[] { + const commentRanges: Range[] = []; + const lineCount = document.getLineCount(); + + // Skip processing for extremely large documents + if (lineCount > NotebookInlineVariablesController.MAX_CELL_LINES) { + return commentRanges; + } + + // Process each line - force tokenization if needed and process tokens in a single pass + for (let lineNumber = 1; lineNumber <= lineCount; lineNumber++) { + // Force tokenization if needed + if (!document.tokenization.hasAccurateTokensForLine(lineNumber)) { + document.tokenization.forceTokenization(lineNumber); + } + + const lineTokens = document.tokenization.getLineTokens(lineNumber); + + // Skip lines with no tokens + if (lineTokens.getCount() === 0) { + continue; + } + + let startCharacter: number | undefined; + + // Check each token in the line + for (let tokenIndex = 0; tokenIndex < lineTokens.getCount(); tokenIndex++) { + const tokenType = lineTokens.getStandardTokenType(tokenIndex); + + if (tokenType === StandardTokenType.Comment || tokenType === StandardTokenType.String || tokenType === StandardTokenType.RegEx) { + if (startCharacter === undefined) { + // Start of a comment or string + startCharacter = lineTokens.getStartOffset(tokenIndex); + } + + const endCharacter = lineTokens.getEndOffset(tokenIndex); + + // Check if this is the end of the comment/string section (either end of line or different token type follows) + const isLastToken = tokenIndex === lineTokens.getCount() - 1; + const nextTokenDifferent = !isLastToken && + lineTokens.getStandardTokenType(tokenIndex + 1) !== tokenType; + + if (isLastToken || nextTokenDifferent) { + // End of comment/string section + commentRanges.push(new Range(lineNumber, startCharacter + 1, lineNumber, endCharacter + 1)); + startCharacter = undefined; + } + } else { + // Reset when we hit a non-comment, non-string token + startCharacter = undefined; + } + } + } + + return commentRanges; + } + + private getCommentedRangesByManualParsing(document: ITextModel): Range[] { + const commentRanges: Range[] = []; + const lines = document.getValue().split('\n'); + const languageId = document.getLanguageId(); + + // Different comment patterns by language + const lineCommentToken = + languageId === 'python' ? '#' : + languageId === 'javascript' || languageId === 'typescript' ? '//' : + null; + + const blockComments = + (languageId === 'javascript' || languageId === 'typescript') ? { start: '/*', end: '*/' } : + null; + + let inBlockComment = false; + let blockCommentStartLine = -1; + let blockCommentStartCol = -1; + + for (let lineNumber = 0; lineNumber < lines.length; lineNumber++) { + const line = lines[lineNumber]; + const trimmedLine = line.trim(); + + // Skip empty lines + if (trimmedLine.length === 0) { + continue; + } + + if (blockComments) { + if (!inBlockComment) { + const startIndex = line.indexOf(blockComments.start); + if (startIndex !== -1) { + inBlockComment = true; + blockCommentStartLine = lineNumber; + blockCommentStartCol = startIndex; + } + } + + if (inBlockComment) { + const endIndex = line.indexOf(blockComments.end); + if (endIndex !== -1) { + commentRanges.push(new Range( + blockCommentStartLine + 1, + blockCommentStartCol + 1, + lineNumber + 1, + endIndex + blockComments.end.length + 1 + )); + inBlockComment = false; + } + continue; + } + } + + if (!inBlockComment && lineCommentToken && line.trimLeft().startsWith(lineCommentToken)) { + const startCol = line.indexOf(lineCommentToken); + commentRanges.push(new Range( + lineNumber + 1, + startCol + 1, + lineNumber + 1, + line.length + 1 + )); + } + } + + // Handle block comment at end of file + if (inBlockComment) { + commentRanges.push(new Range( + blockCommentStartLine + 1, + blockCommentStartCol + 1, + lines.length, + lines[lines.length - 1].length + 1 + )); + } + + return commentRanges; + } + + private isPositionInRanges(position: Position, ranges: Range[]): boolean { + return ranges.some(range => range.containsPosition(position)); + } + private updateCellInlineDecorations(cell: ICellViewModel, decorations: IModelDeltaDecoration[]) { const oldDecorations = this.cellDecorationIds.get(cell) ?? []; this.cellDecorationIds.set(cell, cell.deltaModelDecorations( @@ -392,6 +591,7 @@ export class NotebookInlineVariablesController extends Disposable implements INo return; // should not happen } + // Clear decorations on content change this.cellContentListeners.set(cell.uri, cellModel.onDidChangeContent(() => { this.clearCellInlineDecorations(cell); })); @@ -421,64 +621,16 @@ export class NotebookInlineVariablesController extends Disposable implements INo this._clearNotebookInlineDecorations(); } - // taken from /src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts - private createNotebookInlineValueDecoration(lineNumber: number, contentText: string, column = Constants.MAX_SAFE_SMALL_INTEGER): IModelDeltaDecoration[] { - // If decoratorText is too long, trim and add ellipses. This could happen for minified files with everything on a single line - if (contentText.length > MAX_INLINE_DECORATOR_LENGTH) { - contentText = contentText.substring(0, MAX_INLINE_DECORATOR_LENGTH) + '...'; - } - - return [ - { - range: { - startLineNumber: lineNumber, - endLineNumber: lineNumber, - startColumn: column, - endColumn: column - }, - options: { - description: 'nb-inline-value-decoration-spacer', - after: { - content: noBreakWhitespace, - cursorStops: InjectedTextCursorStops.None - }, - showIfCollapsed: true, - } - }, - { - range: { - startLineNumber: lineNumber, - endLineNumber: lineNumber, - startColumn: column, - endColumn: column - }, - options: { - description: 'nb-inline-value-decoration', - after: { - content: this.replaceWsWithNoBreakWs(contentText), - inlineClassName: 'nb-inline-value', - inlineClassNameAffectsLetterSpacing: true, - cursorStops: InjectedTextCursorStops.None - }, - showIfCollapsed: true, - } - }, - ]; - } - - private replaceWsWithNoBreakWs(str: string): string { - return str.replace(/[ \t]/g, noBreakWhitespace); - } - override dispose(): void { super.dispose(); this._clearNotebookInlineDecorations(); this.currentCancellationTokenSources.forEach(source => source.cancel()); this.currentCancellationTokenSources.clear(); + this.cellContentListeners.forEach(listener => listener.dispose()); + this.cellContentListeners.clear(); } } - registerNotebookContribution(NotebookInlineVariablesController.id, NotebookInlineVariablesController); registerAction2(class ClearNotebookInlineValues extends NotebookAction { diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts index f1df88b5028b..dc86e5ba940d 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookVariablesView.ts @@ -20,7 +20,6 @@ import { IKeybindingService } from '../../../../../../platform/keybinding/common import { WorkbenchAsyncDataTree } from '../../../../../../platform/list/browser/listService.js'; import { IOpenerService } from '../../../../../../platform/opener/common/opener.js'; import { IQuickInputService } from '../../../../../../platform/quickinput/common/quickInput.js'; -import { ITelemetryService } from '../../../../../../platform/telemetry/common/telemetry.js'; import { IThemeService } from '../../../../../../platform/theme/common/themeService.js'; import { IViewPaneOptions, ViewPane } from '../../../../../browser/parts/views/viewPane.js'; import { IViewDescriptorService } from '../../../../../common/views.js'; @@ -64,11 +63,10 @@ export class NotebookVariablesView extends ViewPane { @IQuickInputService protected quickInputService: IQuickInputService, @ICommandService protected commandService: ICommandService, @IThemeService themeService: IThemeService, - @ITelemetryService telemetryService: ITelemetryService, @IHoverService hoverService: IHoverService, @IMenuService private readonly menuService: IMenuService ) { - super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, telemetryService, hoverService); + super(options, keybindingService, contextMenuService, configurationService, contextKeyService, viewDescriptorService, instantiationService, openerService, themeService, hoverService); this._register(this.editorService.onDidActiveEditorChange(() => this.handleActiveEditorChange())); this._register(this.notebookKernelService.onDidNotebookVariablesUpdate(this.handleVariablesChanged.bind(this))); diff --git a/src/vs/workbench/contrib/notebook/browser/controller/chat/notebook.chat.contribution.ts b/src/vs/workbench/contrib/notebook/browser/controller/chat/notebook.chat.contribution.ts index fd5ac883a4cd..c2f474c10399 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/chat/notebook.chat.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/chat/notebook.chat.contribution.ts @@ -23,7 +23,9 @@ import { IChatWidget, IChatWidgetService } from '../../../../chat/browser/chat.j import { ChatInputPart } from '../../../../chat/browser/chatInputPart.js'; import { ChatDynamicVariableModel } from '../../../../chat/browser/contrib/chatDynamicVariables.js'; import { computeCompletionRanges } from '../../../../chat/browser/contrib/chatInputCompletions.js'; -import { ChatAgentLocation, IChatAgentService } from '../../../../chat/common/chatAgents.js'; +import { IChatAgentService } from '../../../../chat/common/chatAgents.js'; +import { ChatAgentLocation } from '../../../../chat/common/constants.js'; +import { ChatContextKeys } from '../../../../chat/common/chatContextKeys.js'; import { IChatRequestPasteVariableEntry } from '../../../../chat/common/chatModel.js'; import { chatVariableLeader } from '../../../../chat/common/chatParserTypes.js'; import { NOTEBOOK_CELL_HAS_OUTPUTS, NOTEBOOK_CELL_OUTPUT_MIME_TYPE_LIST_FOR_CHAT, NOTEBOOK_CELL_OUTPUT_MIMETYPE } from '../../../common/notebookContextKeys.js'; @@ -249,6 +251,7 @@ registerAction2(class CopyCellOutputAction extends Action2 { }, category: NOTEBOOK_ACTIONS_CATEGORY, icon: icons.copyIcon, + precondition: ChatContextKeys.enabled }); } diff --git a/src/vs/workbench/contrib/notebook/browser/controller/chat/notebookChatController.ts b/src/vs/workbench/contrib/notebook/browser/controller/chat/notebookChatController.ts index fd3cd47d6781..2296896d9d91 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/chat/notebookChatController.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/chat/notebookChatController.ts @@ -29,7 +29,7 @@ import { localize } from '../../../../../../nls.js'; import { IContextKey, IContextKeyService } from '../../../../../../platform/contextkey/common/contextkey.js'; import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; import { IStorageService, StorageScope, StorageTarget } from '../../../../../../platform/storage/common/storage.js'; -import { ChatAgentLocation } from '../../../../chat/common/chatAgents.js'; +import { ChatAgentLocation } from '../../../../chat/common/constants.js'; import { ChatModel, IChatModel } from '../../../../chat/common/chatModel.js'; import { IChatService } from '../../../../chat/common/chatService.js'; import { countWords } from '../../../../chat/common/chatWordCounter.js'; diff --git a/src/vs/workbench/contrib/notebook/browser/controller/executeActions.ts b/src/vs/workbench/contrib/notebook/browser/controller/executeActions.ts index 57fc0cfc08f9..b3e1fcdacca0 100644 --- a/src/vs/workbench/contrib/notebook/browser/controller/executeActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/controller/executeActions.ts @@ -20,7 +20,7 @@ import { IDebugService } from '../../../debug/common/debug.js'; import { CTX_INLINE_CHAT_FOCUSED } from '../../../inlineChat/common/inlineChat.js'; import { insertCell } from './cellOperations.js'; import { NotebookChatController } from './chat/notebookChatController.js'; -import { CELL_TITLE_CELL_GROUP_ID, CellToolbarOrder, INotebookActionContext, INotebookCellActionContext, INotebookCellToolbarActionContext, INotebookCommandContext, NOTEBOOK_EDITOR_WIDGET_ACTION_WEIGHT, NotebookAction, NotebookCellAction, NotebookMultiCellAction, cellExecutionArgs, executeNotebookCondition, getContextFromActiveEditor, getContextFromUri, parseMultiCellExecutionArgs } from './coreActions.js'; +import { CELL_TITLE_CELL_GROUP_ID, CellToolbarOrder, INotebookActionContext, INotebookCellActionContext, INotebookCellToolbarActionContext, INotebookCommandContext, NOTEBOOK_EDITOR_WIDGET_ACTION_WEIGHT, NotebookAction, NotebookCellAction, NotebookMultiCellAction, cellExecutionArgs, getContextFromActiveEditor, getContextFromUri, parseMultiCellExecutionArgs } from './coreActions.js'; import { CellEditState, CellFocusMode, EXECUTE_CELL_COMMAND_ID, IFocusNotebookCellOptions, ScrollToRevealBehavior } from '../notebookBrowser.js'; import * as icons from '../notebookIcons.js'; import { CellKind, CellUri, NotebookSetting } from '../../common/notebookCommon.js'; @@ -144,7 +144,6 @@ registerAction2(class ExecuteNotebookAction extends NotebookAction { group: 'navigation', when: ContextKeyExpr.and( NOTEBOOK_IS_ACTIVE_EDITOR, - executeNotebookCondition, ContextKeyExpr.or(NOTEBOOK_INTERRUPTIBLE_KERNEL.toNegated(), NOTEBOOK_HAS_SOMETHING_RUNNING.toNegated()), ContextKeyExpr.notEquals('config.notebook.globalToolbar', true) ) @@ -154,7 +153,6 @@ registerAction2(class ExecuteNotebookAction extends NotebookAction { order: -1, group: 'navigation/execute', when: ContextKeyExpr.and( - executeNotebookCondition, ContextKeyExpr.or( NOTEBOOK_INTERRUPTIBLE_KERNEL.toNegated(), NOTEBOOK_HAS_SOMETHING_RUNNING.toNegated(), diff --git a/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookCellDiffDecorator.ts b/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookCellDiffDecorator.ts index 1a31b09e0e76..8da4d2c230de 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookCellDiffDecorator.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookCellDiffDecorator.ts @@ -21,7 +21,7 @@ import { Range } from '../../../../../../editor/common/core/range.js'; import { NotebookCellTextModel } from '../../../common/model/notebookCellTextModel.js'; import { DetailedLineRangeMapping } from '../../../../../../editor/common/diff/rangeMapping.js'; import { minimapGutterAddedBackground, minimapGutterDeletedBackground, minimapGutterModifiedBackground, overviewRulerAddedForeground, overviewRulerDeletedForeground, overviewRulerModifiedForeground } from '../../../../scm/common/quickDiff.js'; -import { INotebookOriginalCellModelFactory } from '../../contrib/chatEdit/notebookOriginalCellModelFactory.js'; +import { INotebookOriginalCellModelFactory } from './notebookOriginalCellModelFactory.js'; //TODO: allow client to set read-only - chateditsession should set read-only while making changes export class NotebookCellDiffDecorator extends DisposableStore { diff --git a/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookDeletedCellDecorator.ts b/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookDeletedCellDecorator.ts index b95c1b8f9ac2..b75579cab137 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookDeletedCellDecorator.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookDeletedCellDecorator.ts @@ -15,6 +15,11 @@ import { DefaultLineHeight } from '../diffElementViewModel.js'; import { CellDiffInfo } from '../notebookDiffViewModel.js'; import { INotebookEditor } from '../../notebookBrowser.js'; import * as DOM from '../../../../../../base/browser/dom.js'; +import { MenuWorkbenchToolBar, HiddenItemStrategy } from '../../../../../../platform/actions/browser/toolbar.js'; +import { MenuId } from '../../../../../../platform/actions/common/actions.js'; +import { IInstantiationService } from '../../../../../../platform/instantiation/common/instantiation.js'; +import { ServiceCollection } from '../../../../../../platform/instantiation/common/serviceCollection.js'; +import { IContextKeyService } from '../../../../../../platform/contextkey/common/contextkey.js'; const ttPolicy = createTrustedTypesPolicy('notebookRenderer', { createHTML: value => value }); @@ -22,13 +27,16 @@ export interface INotebookDeletedCellDecorator { getTop(deletedIndex: number): number | undefined; } + export class NotebookDeletedCellDecorator extends Disposable implements INotebookDeletedCellDecorator { private readonly zoneRemover = this._register(new DisposableStore()); private readonly createdViewZones = new Map(); private readonly deletedCellInfos = new Map(); constructor( private readonly _notebookEditor: INotebookEditor, + private readonly toolbar: { menuId: MenuId; className: string; telemetrySource?: string; argFactory: (deletedCellIndex: number) => any } | undefined, @ILanguageService private readonly languageService: ILanguageService, + @IInstantiationService private readonly instantiationService: IInstantiationService, ) { super(); } @@ -38,9 +46,13 @@ export class NotebookDeletedCellDecorator extends Disposable implements INoteboo if (!info) { return; } + if (info.previousIndex === -1) { + // deleted cell is before the first real cell + return 0; + } const cells = this._notebookEditor.getCellsInRange({ start: info.previousIndex, end: info.previousIndex + 1 }); if (!cells.length) { - return; + return this._notebookEditor.getLayoutInfo().height + info.offset; } const cell = cells[0]; const cellHeight = this._notebookEditor.getHeightOfElement(cell); @@ -48,10 +60,26 @@ export class NotebookDeletedCellDecorator extends Disposable implements INoteboo return top + cellHeight + info.offset; } + reveal(deletedIndex: number) { + const top = this.getTop(deletedIndex); + if (typeof top === 'number') { + this._notebookEditor.focusContainer(); + this._notebookEditor.revealOffsetInCenterIfOutsideViewport(top); + + const info = this.deletedCellInfos.get(deletedIndex); + + if (info) { + const prevIndex = info.previousIndex === -1 ? 0 : info.previousIndex; + this._notebookEditor.setFocus({ start: prevIndex, end: prevIndex }); + this._notebookEditor.setSelections([{ start: prevIndex, end: prevIndex }]); + } + } + } + public apply(diffInfo: CellDiffInfo[], original: NotebookTextModel): void { this.clear(); - let currentIndex = 0; + let currentIndex = -1; const deletedCellsToRender: { cells: { cell: NotebookCellTextModel; originalIndex: number; previousIndex: number }[]; index: number } = { cells: [], index: 0 }; diffInfo.forEach(diff => { if (diff.type === 'delete') { @@ -86,7 +114,7 @@ export class NotebookDeletedCellDecorator extends Disposable implements INoteboo const rootContainer = document.createElement('div'); const widgets: NotebookDeletedCellWidget[] = []; const heights = await Promise.all(cells.map(async cell => { - const widget = new NotebookDeletedCellWidget(this._notebookEditor, cell.cell.getValue(), cell.cell.language, rootContainer, cell.originalIndex, this.languageService); + const widget = new NotebookDeletedCellWidget(this._notebookEditor, this.toolbar, cell.cell.getValue(), cell.cell.language, rootContainer, cell.originalIndex, this.languageService, this.instantiationService); widgets.push(widget); const height = await widget.render(); this.deletedCellInfos.set(cell.originalIndex, { height, previousIndex: cell.previousIndex, offset: 0 }); @@ -133,14 +161,17 @@ export class NotebookDeletedCellDecorator extends Disposable implements INoteboo export class NotebookDeletedCellWidget extends Disposable { private readonly container: HTMLElement; + // private readonly toolbar: HTMLElement; + constructor( private readonly _notebookEditor: INotebookEditor, - // private readonly _index: number, + private readonly _toolbarOptions: { menuId: MenuId; className: string; telemetrySource?: string; argFactory: (deletedCellIndex: number) => any } | undefined, private readonly code: string, private readonly language: string, container: HTMLElement, - public readonly originalIndex: number, + private readonly _originalIndex: number, @ILanguageService private readonly languageService: ILanguageService, + @IInstantiationService private readonly instantiationService: IInstantiationService, ) { super(); this.container = DOM.append(container, document.createElement('div')); @@ -171,11 +202,36 @@ export class NotebookDeletedCellWidget extends Disposable { + layoutInfo?.contentLeft ? `margin-left: ${layoutInfo}px;` : '' + `white-space: pre;`; - - const rootContainer = this.container; rootContainer.classList.add('code-cell-row'); + + if (this._toolbarOptions) { + const toolbar = document.createElement('div'); + toolbar.className = this._toolbarOptions?.className; + rootContainer.appendChild(toolbar); + + const scopedInstaService = this._register(this.instantiationService.createChild(new ServiceCollection([IContextKeyService, this._notebookEditor.scopedContextKeyService]))); + const toolbarWidget = scopedInstaService.createInstance(MenuWorkbenchToolBar, toolbar, this._toolbarOptions.menuId, { + telemetrySource: this._toolbarOptions.telemetrySource, + hiddenItemStrategy: HiddenItemStrategy.NoHide, + toolbarOptions: { primaryGroup: () => true }, + menuOptions: { + renderShortTitle: true, + arg: this._toolbarOptions.argFactory(this._originalIndex), + }, + }); + + this._store.add(toolbarWidget); + + toolbar.style.position = 'absolute'; + toolbar.style.right = '40px'; + toolbar.style.zIndex = '10'; + toolbar.classList.add('hover'); // Show by default + } + const container = DOM.append(rootContainer, DOM.$('.cell-inner-container')); + container.style.position = 'relative'; // Add this line + const focusIndicatorLeft = DOM.append(container, DOM.$('.cell-focus-indicator.cell-focus-indicator-side.cell-focus-indicator-left')); const cellContainer = DOM.append(container, DOM.$('.cell.code')); DOM.append(focusIndicatorLeft, DOM.$('div.execution-count-label')); diff --git a/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookInlineDiff.ts b/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookInlineDiff.ts index 735e281426d0..c0973dd6f860 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookInlineDiff.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookInlineDiff.ts @@ -10,13 +10,17 @@ import { IInstantiationService } from '../../../../../../platform/instantiation/ import { NotebookCellTextModel } from '../../../common/model/notebookCellTextModel.js'; import { NotebookTextModel } from '../../../common/model/notebookTextModel.js'; import { INotebookEditorWorkerService } from '../../../common/services/notebookWorkerService.js'; -import { CellDiffInfo, computeDiff } from '../notebookDiffViewModel.js'; +import { CellDiffInfo } from '../notebookDiffViewModel.js'; import { INotebookEditorContribution, INotebookEditor } from '../../notebookBrowser.js'; import { registerNotebookContribution } from '../../notebookEditorExtensions.js'; import { NotebookCellDiffDecorator } from './notebookCellDiffDecorator.js'; import { NotebookDeletedCellDecorator } from './notebookDeletedCellDecorator.js'; import { NotebookInsertedCellDecorator } from './notebookInsertedCellDecorator.js'; import { INotebookLoggingService } from '../../../common/notebookLoggingService.js'; +import { computeDiff } from '../../../common/notebookDiff.js'; +import { InstantiationType, registerSingleton } from '../../../../../../platform/instantiation/common/extensions.js'; +import { INotebookOriginalModelReferenceFactory, NotebookOriginalModelReferenceFactory } from './notebookOriginalModelRefFactory.js'; +import { INotebookOriginalCellModelFactory, OriginalNotebookCellModelFactory } from './notebookOriginalCellModelFactory.js'; export class NotebookInlineDiffDecorationContribution extends Disposable implements INotebookEditorContribution { static ID: string = 'workbench.notebook.inlineDiffDecoration'; @@ -79,7 +83,7 @@ export class NotebookInlineDiffDecorationContribution extends Disposable impleme } this.insertedCellDecorator = this.instantiationService.createInstance(NotebookInsertedCellDecorator, this.notebookEditor); - this.deletedCellDecorator = this.instantiationService.createInstance(NotebookDeletedCellDecorator, this.notebookEditor); + this.deletedCellDecorator = this.instantiationService.createInstance(NotebookDeletedCellDecorator, this.notebookEditor, undefined); this._update(); const onVisibleChange = Event.debounce(this.notebookEditor.onDidChangeVisibleRanges, (e) => e, 100, undefined, undefined, undefined, this._store); @@ -162,3 +166,5 @@ export class NotebookInlineDiffDecorationContribution extends Disposable impleme } registerNotebookContribution(NotebookInlineDiffDecorationContribution.ID, NotebookInlineDiffDecorationContribution); +registerSingleton(INotebookOriginalModelReferenceFactory, NotebookOriginalModelReferenceFactory, InstantiationType.Delayed); +registerSingleton(INotebookOriginalCellModelFactory, OriginalNotebookCellModelFactory, InstantiationType.Delayed); diff --git a/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookModifiedCellDecorator.ts b/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookModifiedCellDecorator.ts new file mode 100644 index 000000000000..f2993cbb42eb --- /dev/null +++ b/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookModifiedCellDecorator.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 { Disposable, DisposableStore, toDisposable } from '../../../../../../base/common/lifecycle.js'; +import { CellDiffInfo } from '../notebookDiffViewModel.js'; +import { INotebookEditor } from '../../notebookBrowser.js'; +import { CellKind } from '../../../common/notebookCommon.js'; +import { NotebookCellTextModel } from '../../../common/model/notebookCellTextModel.js'; + +export class NotebookModifiedCellDecorator extends Disposable { + private readonly decorators = this._register(new DisposableStore()); + constructor( + private readonly notebookEditor: INotebookEditor, + ) { + super(); + } + + public apply(diffInfo: CellDiffInfo[]) { + const model = this.notebookEditor.textModel; + if (!model) { + return; + } + + const modifiedMarkdownCells: NotebookCellTextModel[] = []; + for (const diff of diffInfo) { + if (diff.type === 'modified') { + const cell = model.cells[diff.modifiedCellIndex]; + if (cell.cellKind === CellKind.Markup) { + modifiedMarkdownCells.push(cell); + } + } + } + + const ids = this.notebookEditor.deltaCellDecorations([], modifiedMarkdownCells.map(cell => ({ + handle: cell.handle, + options: { outputClassName: 'nb-insertHighlight' } + }))); + + this.clear(); + this.decorators.add(toDisposable(() => { + if (!this.notebookEditor.isDisposed) { + this.notebookEditor.deltaCellDecorations(ids, []); + } + })); + } + public clear() { + this.decorators.clear(); + } +} diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookOriginalCellModelFactory.ts b/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookOriginalCellModelFactory.ts similarity index 100% rename from src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookOriginalCellModelFactory.ts rename to src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookOriginalCellModelFactory.ts diff --git a/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookOriginalModelRefFactory.ts b/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookOriginalModelRefFactory.ts similarity index 100% rename from src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookOriginalModelRefFactory.ts rename to src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookOriginalModelRefFactory.ts diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffActions.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffActions.ts index ce14ccefd3eb..e3438e83bd72 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffActions.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffActions.ts @@ -29,6 +29,7 @@ import { Codicon } from '../../../../../base/common/codicons.js'; import type { URI } from '../../../../../base/common/uri.js'; import { TextEditorSelectionRevealType, type ITextEditorOptions } from '../../../../../platform/editor/common/editor.js'; import product from '../../../../../platform/product/common/product.js'; +import { ctxHasEditorModification, ctxHasRequestInProgress } from '../../../chat/browser/chatEditing/chatEditingEditorContextKeys.js'; // ActiveEditorContext.isEqualTo(SearchEditorConstants.SearchEditorID) @@ -707,7 +708,8 @@ registerAction2(class extends Action2 { group: '1_diff', order: 10, when: ContextKeyExpr.and(ActiveEditorContext.isEqualTo(NotebookTextDiffEditor.ID), - ContextKeyExpr.equals('config.notebook.diff.experimental.toggleInline', true)) + ContextKeyExpr.equals('config.notebook.diff.experimental.toggleInline', true), + ctxHasEditorModification.negate(), ctxHasRequestInProgress.negate()) } } ); diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditor.ts index d581c194e6ad..639e2fcd80d3 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditor.ts @@ -49,6 +49,7 @@ import { INotebookService } from '../../common/notebookService.js'; import { DiffEditorHeightCalculatorService, IDiffEditorHeightCalculatorService } from './editorHeightCalculator.js'; import { IEditorService } from '../../../../services/editor/common/editorService.js'; import { NotebookInlineDiffWidget } from './inlineDiff/notebookInlineDiffWidget.js'; +import { IObservable, observableValue } from '../../../../../base/common/observable.js'; const $ = DOM.$; @@ -153,6 +154,8 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD get isDisposed() { return this._isDisposed; } + private readonly _currentChangedIndex = observableValue(this, -1); + readonly currentChangedIndex: IObservable = this._currentChangedIndex; constructor( group: IEditorGroup, @@ -286,6 +289,8 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD } async toggleInlineView(): Promise { + this._layoutCancellationTokenSource?.dispose(); + this._inlineView = !this._inlineView; if (!this._lastLayoutProperties) { @@ -299,6 +304,9 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD this.layout(this._lastLayoutProperties?.dimension, this._lastLayoutProperties?.position); this.inlineDiffWidget?.hide(); } + + this._layoutCancellationTokenSource = new CancellationTokenSource(); + this.updateLayout(this._layoutCancellationTokenSource.token); } protected createEditor(parent: HTMLElement): void { @@ -561,6 +569,17 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD updateInsets(); })); + this._localStore.add(this._list.onDidChangeFocus((e) => { + if (e.indexes.length && this.notebookDiffViewModel && e.indexes[0] < this.notebookDiffViewModel.items.length) { + const selectedItem = this.notebookDiffViewModel.items[e.indexes[0]]; + const changedItems = this.notebookDiffViewModel.items.filter(item => item.type !== 'unchanged' && item.type !== 'unchangedMetadata' && item.type !== 'placeholder'); + if (selectedItem && selectedItem?.type !== 'placeholder' && selectedItem?.type !== 'unchanged' && selectedItem?.type !== 'unchangedMetadata') { + return this._currentChangedIndex.set(changedItems.indexOf(selectedItem), undefined); + } + } + return this._currentChangedIndex.set(-1, undefined); + })); + this._localStore.add(this._eventDispatcher.onDidChangeCellLayout(() => { updateInsets(); })); @@ -669,8 +688,9 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD this._list.updateElementHeight2(cell, height); }; - if (this.pendingLayouts.has(cell)) { - this.pendingLayouts.get(cell)!.dispose(); + let disposable = this.pendingLayouts.get(cell); + if (disposable) { + this._localStore.delete(disposable); } let r: () => void; @@ -680,11 +700,13 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD relayout(cell, height); r(); }); - - this.pendingLayouts.set(cell, toDisposable(() => { + disposable = toDisposable(() => { layoutDisposable.dispose(); r(); - })); + }); + this._localStore.add(disposable); + + this.pendingLayouts.set(cell, disposable); return new Promise(resolve => { r = resolve; }); } @@ -697,6 +719,33 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD this._list.triggerScrollFromMouseWheelEvent(event); } + firstChange(): void { + if (!this.notebookDiffViewModel) { + return; + } + // go to the first one + const currentViewModels = this.notebookDiffViewModel.items; + const index = currentViewModels.findIndex(vm => vm.type !== 'unchanged' && vm.type !== 'unchangedMetadata' && vm.type !== 'placeholder'); + if (index >= 0) { + this._list.setFocus([index]); + this._list.reveal(index); + } + } + + lastChange(): void { + if (!this.notebookDiffViewModel) { + return; + } + // go to the first one + const currentViewModels = this.notebookDiffViewModel.items; + const item = currentViewModels.slice().reverse().find(vm => vm.type !== 'unchanged' && vm.type !== 'unchangedMetadata' && vm.type !== 'placeholder'); + const index = item ? currentViewModels.indexOf(item) : -1; + if (index >= 0) { + this._list.setFocus([index]); + this._list.reveal(index); + } + } + previousChange(): void { if (!this.notebookDiffViewModel) { return; @@ -712,7 +761,7 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD const currentViewModels = this.notebookDiffViewModel.items; while (prevChangeIndex >= 0) { const vm = currentViewModels[prevChangeIndex]; - if (vm.type !== 'unchanged' && vm.type !== 'placeholder') { + if (vm.type !== 'unchanged' && vm.type !== 'unchangedMetadata' && vm.type !== 'placeholder') { break; } @@ -724,7 +773,7 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD this._list.reveal(prevChangeIndex); } else { // go to the last one - const index = findLastIdx(currentViewModels, vm => vm.type !== 'unchanged' && vm.type !== 'placeholder'); + const index = findLastIdx(currentViewModels, vm => vm.type !== 'unchanged' && vm.type !== 'unchangedMetadata' && vm.type !== 'placeholder'); if (index >= 0) { this._list.setFocus([index]); this._list.reveal(index); @@ -747,7 +796,7 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD const currentViewModels = this.notebookDiffViewModel.items; while (nextChangeIndex < currentViewModels.length) { const vm = currentViewModels[nextChangeIndex]; - if (vm.type !== 'unchanged' && vm.type !== 'placeholder') { + if (vm.type !== 'unchanged' && vm.type !== 'unchangedMetadata' && vm.type !== 'placeholder') { break; } @@ -759,7 +808,7 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD this._list.reveal(nextChangeIndex); } else { // go to the first one - const index = currentViewModels.findIndex(vm => vm.type !== 'unchanged' && vm.type !== 'placeholder'); + const index = currentViewModels.findIndex(vm => vm.type !== 'unchanged' && vm.type !== 'unchangedMetadata' && vm.type !== 'placeholder'); if (index >= 0) { this._list.setFocus([index]); this._list.reveal(index); diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser.ts index 6dee16a30c34..3e435fad6673 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditorBrowser.ts @@ -18,6 +18,7 @@ import { WorkbenchToolBar } from '../../../../../platform/actions/browser/toolba import { DiffEditorWidget } from '../../../../../editor/browser/widget/diffEditor/diffEditorWidget.js'; import { CancellationToken } from '../../../../../base/common/cancellation.js'; import { localize } from '../../../../../nls.js'; +import { IObservable } from '../../../../../base/common/observable.js'; export enum DiffSide { Original = 0, @@ -32,6 +33,7 @@ export interface INotebookTextDiffEditor { notebookOptions: NotebookOptions; readonly textModel?: NotebookTextModel; inlineNotebookEditor: INotebookEditor | undefined; + readonly currentChangedIndex: IObservable; onMouseUp: Event<{ readonly event: MouseEvent; readonly target: IDiffElementViewModelBase }>; onDidScroll: Event; onDidDynamicOutputRendered: Event<{ cell: IGenericCellViewModel; output: ICellOutputViewModel }>; @@ -54,6 +56,8 @@ export interface INotebookTextDiffEditor { focusNextNotebookCell(cell: IGenericCellViewModel, focus: 'editor' | 'container' | 'output'): Promise; updateOutputHeight(cellInfo: ICommonCellInfo, output: ICellOutputViewModel, height: number, isInit: boolean): void; deltaCellOutputContainerClassNames(diffSide: DiffSide, cellId: string, added: string[], removed: string[]): void; + firstChange(): void; + lastChange(): void; previousChange(): void; nextChange(): void; toggleInlineView(): void; diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts index 37a6a4d2b4a9..5cd62062e26f 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffList.ts @@ -511,14 +511,14 @@ export class NotebookTextDiffList extends WorkbenchList div.monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.selected.focused { background-color: ${styles.listFocusAndSelectionBackground}; } `); } if (styles.listFocusAndSelectionForeground) { content.push(` - .monaco-drag-image, + .monaco-drag-image${suffix}, .monaco-list${suffix}:focus > div.monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.selected.focused { color: ${styles.listFocusAndSelectionForeground}; } `); } @@ -551,7 +551,7 @@ export class NotebookTextDiffList extends WorkbenchList div.monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.focused { outline: 1px solid ${styles.listFocusOutline}; outline-offset: -1px; } `); } diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffOverviewRuler.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffOverviewRuler.ts index f24aabe73b4c..912e77093692 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffOverviewRuler.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffOverviewRuler.ts @@ -57,7 +57,7 @@ export class NotebookDiffOverviewRuler extends Themable { })); this._register(this.themeService.onDidColorThemeChange(e => { - const colorChanged = this.applyColors(e); + const colorChanged = this.applyColors(e.theme); if (colorChanged) { this._scheduleRender(); } diff --git a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffViewModel.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffViewModel.ts index 8ec5ec7691c5..6ef165a48213 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffViewModel.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffViewModel.ts @@ -4,7 +4,7 @@ *--------------------------------------------------------------------------------------------*/ import { CancellationToken } from '../../../../../base/common/cancellation.js'; -import { IDiffResult, IDiffChange } from '../../../../../base/common/diff/diff.js'; +import { IDiffResult } from '../../../../../base/common/diff/diff.js'; import { Emitter, type IValueWithChangeEvent } from '../../../../../base/common/event.js'; import { Disposable, DisposableStore, dispose } from '../../../../../base/common/lifecycle.js'; import { Schemas } from '../../../../../base/common/network.js'; @@ -17,11 +17,12 @@ import { DiffElementCellViewModelBase, DiffElementPlaceholderViewModel, IDiffEle import { NotebookDiffEditorEventDispatcher } from './eventDispatcher.js'; import { INotebookDiffViewModel, INotebookDiffViewModelUpdateEvent, NOTEBOOK_DIFF_ITEM_DIFF_STATE, NOTEBOOK_DIFF_ITEM_KIND } from './notebookDiffEditorBrowser.js'; import { NotebookTextModel } from '../../common/model/notebookTextModel.js'; -import { CellUri, INotebookDiffEditorModel, INotebookDiffResult } from '../../common/notebookCommon.js'; +import { CellUri, INotebookDiffEditorModel } from '../../common/notebookCommon.js'; import { INotebookService } from '../../common/notebookService.js'; import { INotebookEditorWorkerService } from '../../common/services/notebookWorkerService.js'; import { IDiffEditorHeightCalculatorService } from './editorHeightCalculator.js'; import { raceCancellation } from '../../../../../base/common/async.js'; +import { computeDiff } from '../../common/notebookDiff.js'; export class NotebookDiffViewModel extends Disposable implements INotebookDiffViewModel, IValueWithChangeEvent { private readonly placeholderAndRelatedCells = new Map(); @@ -415,62 +416,7 @@ export type CellDiffInfo = { modifiedCellIndex: number; type: 'insert'; }; -export function computeDiff(originalModel: NotebookTextModel, modifiedModel: NotebookTextModel, diffResult: INotebookDiffResult) { - const cellChanges = diffResult.cellsDiff.changes; - const cellDiffInfo: CellDiffInfo[] = []; - let originalCellIndex = 0; - let modifiedCellIndex = 0; - - let firstChangeIndex = -1; - - for (let i = 0; i < cellChanges.length; i++) { - const change = cellChanges[i]; - // common cells - - for (let j = 0; j < change.originalStart - originalCellIndex; j++) { - const originalCell = originalModel.cells[originalCellIndex + j]; - const modifiedCell = modifiedModel.cells[modifiedCellIndex + j]; - if (originalCell.getHashValue() === modifiedCell.getHashValue()) { - cellDiffInfo.push({ - originalCellIndex: originalCellIndex + j, - modifiedCellIndex: modifiedCellIndex + j, - type: 'unchanged' - }); - } else { - if (firstChangeIndex === -1) { - firstChangeIndex = cellDiffInfo.length; - } - cellDiffInfo.push({ - originalCellIndex: originalCellIndex + j, - modifiedCellIndex: modifiedCellIndex + j, - type: 'modified' - }); - } - } - const modifiedLCS = computeModifiedLCS(change, originalModel, modifiedModel); - if (modifiedLCS.length && firstChangeIndex === -1) { - firstChangeIndex = cellDiffInfo.length; - } - - cellDiffInfo.push(...modifiedLCS); - originalCellIndex = change.originalStart + change.originalLength; - modifiedCellIndex = change.modifiedStart + change.modifiedLength; - } - - for (let i = originalCellIndex; i < originalModel.cells.length; i++) { - cellDiffInfo.push({ - originalCellIndex: i, - modifiedCellIndex: i - originalCellIndex + modifiedCellIndex, - type: 'unchanged' - }); - } - - return { - cellDiffInfo, - firstChangeIndex - }; -} function isEqual(cellDiffInfo: CellDiffInfo[], viewModels: IDiffElementViewModelBase[], model: INotebookDiffEditorModel) { if (cellDiffInfo.length !== viewModels.length) { return false; @@ -510,40 +456,6 @@ function isEqual(cellDiffInfo: CellDiffInfo[], viewModels: IDiffElementViewModel return true; } - -function computeModifiedLCS(change: IDiffChange, originalModel: NotebookTextModel, modifiedModel: NotebookTextModel) { - const result: CellDiffInfo[] = []; - // modified cells - const modifiedLen = Math.min(change.originalLength, change.modifiedLength); - - for (let j = 0; j < modifiedLen; j++) { - const isTheSame = originalModel.cells[change.originalStart + j].equal(modifiedModel.cells[change.modifiedStart + j]); - result.push({ - originalCellIndex: change.originalStart + j, - modifiedCellIndex: change.modifiedStart + j, - type: isTheSame ? 'unchanged' : 'modified' - }); - } - - for (let j = modifiedLen; j < change.originalLength; j++) { - // deletion - result.push({ - originalCellIndex: change.originalStart + j, - type: 'delete' - }); - } - - for (let j = modifiedLen; j < change.modifiedLength; j++) { - result.push({ - modifiedCellIndex: change.modifiedStart + j, - type: 'insert' - }); - } - - return result; -} - - export abstract class NotebookMultiDiffEditorItem extends MultiDiffEditorItem { constructor( originalUri: URI | undefined, diff --git a/src/vs/workbench/contrib/notebook/browser/media/notebookCellStatusBar.css b/src/vs/workbench/contrib/notebook/browser/media/notebookCellStatusBar.css index 8f5eb5dc052a..64dd58eecd25 100644 --- a/src/vs/workbench/contrib/notebook/browser/media/notebookCellStatusBar.css +++ b/src/vs/workbench/contrib/notebook/browser/media/notebookCellStatusBar.css @@ -68,3 +68,11 @@ .monaco-workbench .notebookOverlay .cell-statusbar-container.is-active-cell .cell-status-item-show-when-active { display: initial; } + +/* Ensure execution status icons always maintain their themed colors */ +.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-statusbar-container .cell-status-item .codicon-notebook-state-success { + color: var(--vscode-notebookStatusSuccessIcon-foreground); +} +.monaco-workbench .notebookOverlay .monaco-list .monaco-list-row .cell-statusbar-container .cell-status-item .codicon-notebook-state-error { + color: var(--vscode-notebookStatusErrorIcon-foreground); +} diff --git a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts index c1cb89476ffc..10a50d99cdf5 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebook.contribution.ts @@ -105,9 +105,6 @@ import './contrib/notebookVariables/notebookInlineVariables.js'; // Diff Editor Contribution import './diff/notebookDiffActions.js'; -// Chat Edit Contributions -import './contrib/chatEdit/contribution.js'; - // Services import { editorOptionsRegistry } from '../../../../editor/common/config/editorOptions.js'; import { NotebookExecutionStateService } from './services/notebookExecutionStateServiceImpl.js'; @@ -135,8 +132,6 @@ import { NotebookMultiDiffEditorInput } from './diff/notebookMultiDiffEditorInpu import { getFormattedMetadataJSON } from '../common/model/notebookCellTextModel.js'; import { INotebookOutlineEntryFactory, NotebookOutlineEntryFactory } from './viewModel/notebookOutlineEntryFactory.js'; import { getFormattedNotebookMetadataJSON } from '../common/model/notebookMetadataTextModel.js'; -import { INotebookSynchronizerService } from '../common/notebookSynchronizerService.js'; -import { NotebookSynchronizerService } from './contrib/chatEdit/notebookSynchronizerService.js'; /*--------------------------------------------------------------------------------------------- */ @@ -882,7 +877,6 @@ registerSingleton(INotebookKeymapService, NotebookKeymapService, InstantiationTy registerSingleton(INotebookLoggingService, NotebookLoggingService, InstantiationType.Delayed); registerSingleton(INotebookCellOutlineDataSourceFactory, NotebookCellOutlineDataSourceFactory, InstantiationType.Delayed); registerSingleton(INotebookOutlineEntryFactory, NotebookOutlineEntryFactory, InstantiationType.Delayed); -registerSingleton(INotebookSynchronizerService, NotebookSynchronizerService, InstantiationType.Delayed); const schemas: IJSONSchemaMap = {}; function isConfigurationPropertySchema(x: IConfigurationPropertySchema | { [path: string]: IConfigurationPropertySchema }): x is IConfigurationPropertySchema { diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts index 743b101a4f30..b289cdb6142c 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts @@ -45,7 +45,6 @@ import { EnablementState } from '../../../services/extensionManagement/common/ex import { IWorkingCopyBackupService } from '../../../services/workingCopy/common/workingCopyBackup.js'; import { streamToBuffer } from '../../../../base/common/buffer.js'; import { ILogService } from '../../../../platform/log/common/log.js'; -import { INotebookEditorWorkerService } from '../common/services/notebookWorkerService.js'; import { IPreferencesService } from '../../../services/preferences/common/preferences.js'; import { IActionViewItemOptions } from '../../../../base/browser/ui/actionbar/actionViewItems.js'; import { StopWatch } from '../../../../base/common/stopwatch.js'; @@ -96,7 +95,6 @@ export class NotebookEditor extends EditorPane implements INotebookEditorPane, I @IExtensionsWorkbenchService private readonly _extensionsWorkbenchService: IExtensionsWorkbenchService, @IWorkingCopyBackupService private readonly _workingCopyBackupService: IWorkingCopyBackupService, @ILogService private readonly logService: ILogService, - @INotebookEditorWorkerService private readonly _notebookEditorWorkerService: INotebookEditorWorkerService, @IPreferencesService private readonly _preferencesService: IPreferencesService ) { super(NotebookEditor.ID, group, telemetryService, themeService, storageService); @@ -146,7 +144,7 @@ export class NotebookEditor extends EditorPane implements INotebookEditorPane, I override getActionViewItem(action: IAction, options: IActionViewItemOptions): IActionViewItem | undefined { if (action.id === SELECT_KERNEL_ID) { // this is being disposed by the consumer - return this._instantiationService.createInstance(NotebooKernelActionViewItem, action, this, options); + return this._register(this._instantiationService.createInstance(NotebooKernelActionViewItem, action, this, options)); } return undefined; } @@ -334,7 +332,7 @@ export class NotebookEditor extends EditorPane implements INotebookEditorPane, I } this._handlePerfMark(perf, input, model.notebook); - this._handlePromptRecommendations(model.notebook); + this._onDidChangeControl.fire(); } catch (e) { this.logService.warn('NotebookEditorWidget#setInput failed', e); if (isEditorOpenError(e)) { @@ -506,24 +504,6 @@ export class NotebookEditor extends EditorPane implements INotebookEditorPane, I }); } - private _handlePromptRecommendations(model: NotebookTextModel) { - this._notebookEditorWorkerService.canPromptRecommendation(model.uri).then(shouldPrompt => { - type WorkbenchNotebookShouldPromptRecommendationClassification = { - owner: 'rebornix'; - comment: 'The notebook file metrics. Used to get a better understanding of if we should prompt for notebook extension recommendations'; - shouldPrompt: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Should we prompt for notebook extension recommendations' }; - }; - - type WorkbenchNotebookShouldPromptRecommendationEvent = { - shouldPrompt: boolean; - }; - - this.telemetryService.publicLog2('notebook/shouldPromptRecommendation', { - shouldPrompt: shouldPrompt - }); - }); - } - override clearInput(): void { this._inputListener.clear(); diff --git a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts index 1f078d4222bc..9e0b6f8cf18b 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditorWidget.ts @@ -856,7 +856,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD styleSheets.push(`.notebookOverlay .monaco-list .monaco-list-row .cell-shadow-container-bottom { top: ${cellBottomMargin}px; }`); styleSheets.push(` - .notebookOverlay .monaco-list .monaco-list-row:has(+ .monaco-list-row.selected) .cell-focus-indicator-bottom { + .notebookOverlay .monaco-list.selection-multiple .monaco-list-row:has(+ .monaco-list-row.selected) .cell-focus-indicator-bottom { height: ${bottomToolbarGap + cellBottomMargin}px; } `); @@ -1614,6 +1614,28 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD } })); + store.add(cell.onCellDecorationsChanged(e => { + e.added.forEach(options => { + if (options.className) { + this.deltaCellContainerClassNames(cell.id, [options.className], []); + } + + if (options.outputClassName) { + this.deltaCellContainerClassNames(cell.id, [options.outputClassName], []); + } + }); + + e.removed.forEach(options => { + if (options.className) { + this.deltaCellContainerClassNames(cell.id, [], [options.className]); + } + + if (options.outputClassName) { + this.deltaCellContainerClassNames(cell.id, [], [options.outputClassName]); + } + }); + })); + return store; } @@ -2321,6 +2343,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD //#region Cell operations/layout API private _pendingLayouts: WeakMap | null = new WeakMap(); + private _layoutDisposables: Set = new Set(); async layoutNotebookCell(cell: ICellViewModel, height: number, context?: CellLayoutContext): Promise { this._debug('layout cell', cell.handle, height); const viewIndex = this._list.getViewIndex(cell); @@ -2353,6 +2376,7 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD return; } + const pendingLayout = this._pendingLayouts?.get(cell); this._pendingLayouts?.delete(cell); if (!this.hasEditorFocus()) { @@ -2371,15 +2395,21 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD this._list.updateElementHeight2(cell, height); deferred.complete(undefined); + if (pendingLayout) { + pendingLayout.dispose(); + this._layoutDisposables.delete(pendingLayout); + } }; if (this._list.inRenderingTransaction) { const layoutDisposable = DOM.scheduleAtNextAnimationFrame(DOM.getWindow(this.getDomNode()), doLayout); - this._pendingLayouts?.set(cell, toDisposable(() => { + const disposable = toDisposable(() => { layoutDisposable.dispose(); deferred.complete(undefined); - })); + }); + this._pendingLayouts?.set(cell, disposable); + this._layoutDisposables.add(disposable); } else { doLayout(); } @@ -3245,6 +3275,8 @@ export class NotebookEditorWidget extends Disposable implements INotebookEditorD this._webview?.dispose(); this._webview = null; + this._layoutDisposables.forEach(d => d.dispose()); + this.notebookEditorService.removeNotebookEditor(this); dispose(this._contributions.values()); this._contributions.clear(); diff --git a/src/vs/workbench/contrib/notebook/browser/services/notebookServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/services/notebookServiceImpl.ts index b1670958f9fa..f2925b1b8c87 100644 --- a/src/vs/workbench/contrib/notebook/browser/services/notebookServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/services/notebookServiceImpl.ts @@ -212,7 +212,7 @@ export class NotebookProviderInfoStore extends Disposable { // untitled notebooks are disposed when they get saved. we should not hold a reference // to such a disposed notebook and therefore dispose the reference as well - ref.object.notebook.onWillDispose(() => { + Event.once(ref.object.notebook.onWillDispose)(() => { ref.dispose(); }); @@ -871,6 +871,11 @@ export class NotebookService extends Disposable implements INotebookService { } hasSupportedNotebooks(resource: URI): boolean { + if (this._models.has(resource)) { + // it might be untitled + return true; + } + const contribution = this.notebookProviderInfoStore.getContributedNotebook(resource); if (!contribution.length) { return false; diff --git a/src/vs/workbench/contrib/notebook/browser/services/notebookWorkerServiceImpl.ts b/src/vs/workbench/contrib/notebook/browser/services/notebookWorkerServiceImpl.ts index 4a0527917e60..d45bdc91ad19 100644 --- a/src/vs/workbench/contrib/notebook/browser/services/notebookWorkerServiceImpl.ts +++ b/src/vs/workbench/contrib/notebook/browser/services/notebookWorkerServiceImpl.ts @@ -65,6 +65,7 @@ class WorkerManager extends Disposable { // this._lastWorkerUsedTime = (new Date()).getTime(); if (!this._editorWorkerClient) { this._editorWorkerClient = new NotebookWorkerClient(this._notebookService, this._modelService); + this._register(this._editorWorkerClient); } return Promise.resolve(this._editorWorkerClient); } diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellDecorations.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellDecorations.ts index 98340a20af7e..9068b002c64b 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellDecorations.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellDecorations.ts @@ -59,22 +59,12 @@ export class CellDecorations extends CellContentPart { e.added.forEach(options => { if (options.className && this.currentCell) { this.rootContainer.classList.add(options.className); - this.notebookEditor.deltaCellContainerClassNames(this.currentCell.id, [options.className], []); - } - - if (options.outputClassName && this.currentCell) { - this.notebookEditor.deltaCellContainerClassNames(this.currentCell.id, [options.outputClassName], []); } }); e.removed.forEach(options => { if (options.className && this.currentCell) { this.rootContainer.classList.remove(options.className); - this.notebookEditor.deltaCellContainerClassNames(this.currentCell.id, [], [options.className]); - } - - if (options.outputClassName && this.currentCell) { - this.notebookEditor.deltaCellContainerClassNames(this.currentCell.id, [], [options.outputClassName]); } }); })); diff --git a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellStatusPart.ts b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellStatusPart.ts index f58ff0aa23fd..a23d2f8b28ed 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellStatusPart.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/cellParts/cellStatusPart.ts @@ -79,7 +79,7 @@ export class CellEditorStatusBar extends CellContentPart { readonly showHover = (options: IHoverDelegateOptions) => { options.position = options.position ?? {}; options.position.hoverPosition = HoverPosition.ABOVE; - return hoverService.showHover(options); + return hoverService.showInstantHover(options); }; readonly placement = 'element'; diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts index fdeaf59ad84e..ff0678e8b251 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookCellList.ts @@ -1036,7 +1036,7 @@ export class NotebookCellList extends WorkbenchList implements ID } const editorAttachedPromise = new Promise((resolve, reject) => { - element.onDidChangeEditorAttachState(() => { + Event.once(element.onDidChangeEditorAttachState)(() => { element.editorAttached ? resolve() : reject(); }); }); @@ -1168,7 +1168,8 @@ export class NotebookCellList extends WorkbenchList implements ID const wrapperBottom = this.getViewScrollBottom(); if (offset < scrollTop || offset > wrapperBottom) { - this.view.setScrollTop(offset - this.view.renderHeight / 2); + const newTop = Math.max(0, offset - this.view.renderHeight / 2); + this.view.setScrollTop(newTop); } } @@ -1346,14 +1347,14 @@ export class NotebookCellList extends WorkbenchList implements ID if (styles.listFocusAndSelectionBackground) { content.push(` - .monaco-drag-image, + .monaco-drag-image${suffix}, .monaco-list${suffix}:focus > div.monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.selected.focused { background-color: ${styles.listFocusAndSelectionBackground}; } `); } if (styles.listFocusAndSelectionForeground) { content.push(` - .monaco-drag-image, + .monaco-drag-image${suffix}, .monaco-list${suffix}:focus > div.monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.selected.focused { color: ${styles.listFocusAndSelectionForeground}; } `); } @@ -1386,7 +1387,7 @@ export class NotebookCellList extends WorkbenchList implements ID if (styles.listFocusOutline) { content.push(` - .monaco-drag-image, + .monaco-drag-image${suffix}, .monaco-list${suffix}:focus > div.monaco-scrollable-element > .monaco-list-rows > .monaco-list-row.focused { outline: 1px solid ${styles.listFocusOutline}; outline-offset: -1px; } `); } diff --git a/src/vs/workbench/contrib/notebook/browser/view/notebookCellListView.ts b/src/vs/workbench/contrib/notebook/browser/view/notebookCellListView.ts index 16a00de19770..7034d6bb0376 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/notebookCellListView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/notebookCellListView.ts @@ -189,9 +189,7 @@ export class NotebookCellsLayout implements IRangeMap { const index = afterPosition - 1; const previousItemPosition = this._prefixSumComputer.getPrefixSum(index); const previousItemSize = this._items[index].size; - const previousWhitespace = this._whitespace.filter(ws => (ws.afterPosition <= afterPosition - 1 && ws.afterPosition > 0)); - const whitespaceBefore = previousWhitespace.reduce((acc, ws) => acc + ws.size, 0); - return previousItemPosition + previousItemSize + whitespaceBeforeFirstItem + this.paddingTop + whitespaceBefore; + return previousItemPosition + previousItemSize + whitespaceBeforeFirstItem + this.paddingTop; } indexAt(position: number): number { diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts index 743deddfce3a..21e6cf46c139 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts @@ -404,6 +404,10 @@ export class BackLayerWebView extends Themable { background-color: var(--theme-notebook-symbol-highlight-background); } + #container .markup > div.nb-insertHighlight { + background-color: var(--vscode-diffEditor-insertedLineBackground, var(--vscode-diffEditor-insertedTextBackground)); + } + #container .nb-symbolHighlight .output_container .output { background-color: var(--theme-notebook-symbol-highlight-background); } @@ -790,7 +794,8 @@ export class BackLayerWebView extends Themable { 'workbench.action.openSettings', '_notebook.selectKernel', // TODO@rebornix explore open output channel with name command - 'jupyter.viewOutput' + 'jupyter.viewOutput', + 'jupyter.createPythonEnvAndSelectController', ], }); return; @@ -930,8 +935,8 @@ export class BackLayerWebView extends Themable { } case 'notebookPerformanceMessage': { this.notebookEditor.updatePerformanceMetadata(data.cellId, data.executionId, data.duration, data.rendererId); - if (data.mimeType && data.outputSize && data.rendererId === 'vscode.builtin-renderer') { - this._sendPerformanceData(data.mimeType, data.outputSize, data.duration); + if (data.outputSize && data.rendererId === 'vscode.builtin-renderer') { + this._sendPerformanceData(data.outputSize, data.duration); } break; } @@ -951,23 +956,20 @@ export class BackLayerWebView extends Themable { return initializePromise.p; } - private _sendPerformanceData(mimeType: string, outputSize: number, renderTime: number) { + private _sendPerformanceData(outputSize: number, renderTime: number) { type NotebookOutputRenderClassification = { owner: 'amunger'; comment: 'Track performance data for output rendering'; - mimeType: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Presentation type of the output.' }; outputSize: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Size of the output data buffer.'; isMeasurement: true }; renderTime: { classification: 'SystemMetaData'; purpose: 'FeatureInsight'; comment: 'Time spent rendering output.'; isMeasurement: true }; }; type NotebookOutputRenderEvent = { - mimeType: string; outputSize: number; renderTime: number; }; const telemetryData = { - mimeType, outputSize, renderTime }; diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts index 6c0a4f516d37..84589a5ea0b7 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/cellRenderer.ts @@ -84,7 +84,7 @@ export class NotebookCellListDelegate extends Disposable implements IListVirtual } } -abstract class AbstractCellRenderer { +abstract class AbstractCellRenderer extends Disposable { protected readonly editorOptions: CellEditorOptions; constructor( @@ -99,11 +99,12 @@ abstract class AbstractCellRenderer { language: string, protected dndController: CellDragAndDropController | undefined ) { - this.editorOptions = new CellEditorOptions(this.notebookEditor.getBaseCellEditorOptions(language), this.notebookEditor.notebookOptions, configurationService); + super(); + this.editorOptions = this._register(new CellEditorOptions(this.notebookEditor.getBaseCellEditorOptions(language), this.notebookEditor.notebookOptions, configurationService)); } - dispose() { - this.editorOptions.dispose(); + override dispose() { + super.dispose(); this.dndController = undefined; } } @@ -199,7 +200,7 @@ export class MarkupCellRenderer extends AbstractCellRenderer implements IListRen editorContainer, foldingIndicator, templateDisposables, - elementDisposables: new DisposableStore(), + elementDisposables: templateDisposables.add(new DisposableStore()), cellParts, toJSON: () => { return {}; } }; @@ -225,7 +226,6 @@ export class MarkupCellRenderer extends AbstractCellRenderer implements IListRen } disposeTemplate(templateData: MarkdownCellRenderTemplate): void { - templateData.elementDisposables.dispose(); templateData.templateDisposables.dispose(); } @@ -366,7 +366,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende outputShowMoreContainer, editor, templateDisposables, - elementDisposables: new DisposableStore(), + elementDisposables: templateDisposables.add(new DisposableStore()), cellParts, toJSON: () => { return {}; } }; @@ -397,7 +397,7 @@ export class CodeCellRenderer extends AbstractCellRenderer implements IListRende } disposeTemplate(templateData: CodeCellRenderTemplate): void { - templateData.templateDisposables.clear(); + templateData.templateDisposables.dispose(); } disposeElement(element: ICellViewModel, index: number, templateData: CodeCellRenderTemplate, height: number | undefined): void { diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts index b18398d54971..073737f5b79b 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewMessages.ts @@ -486,7 +486,6 @@ export interface IPerformanceMessage extends BaseToWebviewMessage { readonly duration: number; readonly rendererId: string; readonly outputSize?: number; - readonly mimeType?: string; } diff --git a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts index ff6f74f9096a..f13d9a5e510b 100644 --- a/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts +++ b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts @@ -19,6 +19,43 @@ import type * as rendererApi from 'vscode-notebook-renderer'; // function. Imports are not allowed. This is stringified and injected into // the webview. +// Minimal HTML sanitizer implementation using browser's DOMParser, +// which removes