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 bfa6fba99e3d..71d98e6c5834 100644 --- a/.npmrc +++ b/.npmrc @@ -1,6 +1,6 @@ disturl="https://electronjs.org/headers" -target="34.2.0" -ms_build_id="11044223" +target="34.3.2" +ms_build_id="11161073" runtime="electron" build_from_source="true" legacy-peer-deps="true" 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/my-endgame.github-issues b/.vscode/notebooks/my-endgame.github-issues index 9d5c8ccdcc54..0fd05ece4853 100644 --- a/.vscode/notebooks/my-endgame.github-issues +++ b/.vscode/notebooks/my-endgame.github-issues @@ -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/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/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 1f198441bc3e..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.196/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.196:build/config/arm.gni - # https://source.chromium.org/chromium/chromium/src/+/refs/tags/132.0.6834.196:build/config/compiler/BUILD.gn - # https://source.chromium.org/chromium/chromium/src/+/refs/tags/132.0.6834.196: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 dcc7bf55c5ca..d390d7a233ca 100644 --- a/build/checksums/electron.txt +++ b/build/checksums/electron.txt @@ -1,75 +1,75 @@ -c0a187acca68906c4a6387e8fabd052cb031ace6132d60a71001d9a0e891958e *chromedriver-v34.2.0-darwin-arm64.zip -fa5a46d752267d8497d375e19079e8b6a8df70c234a79b2d6b48f5862e1a0abc *chromedriver-v34.2.0-darwin-x64.zip -61e03d4fa570976d80f740637f56192b6448a05a73d1fba9717900b29f2b1b4d *chromedriver-v34.2.0-linux-arm64.zip -ec774d9b1a1b828a0db1502a1017fcab1dfed99b1b6b2fd2308dd600a1efa98a *chromedriver-v34.2.0-linux-armv7l.zip -cc15a6e6206485a2d96649ceb60509b9da04fa2811c4824b2e0eb43d1f4b1417 *chromedriver-v34.2.0-linux-x64.zip -9777122f6684180ef375b9b21dcabbc731d8a8befa300d1d47ad954a5b64c1c8 *chromedriver-v34.2.0-mas-arm64.zip -69451fa148b105fec9644646b22ca758a206499574c5816591354835c8056679 *chromedriver-v34.2.0-mas-x64.zip -eb7adc7e720f5e0f1d2c12ecbe886bdc01f2c9aaa3954bd6ebd313750bb18819 *chromedriver-v34.2.0-win32-arm64.zip -cb1973b0c2f5565974d5c2cb51816692f064b6cdc7897fa341d528ba7f9b14bf *chromedriver-v34.2.0-win32-ia32.zip -af5575b4727c3dbe7272016cbbaa8872043f843168a47d86748a50397efb4f77 *chromedriver-v34.2.0-win32-x64.zip -fcc718af2a28fb953290dc971e945818b4dbb293f297e6e25acb669d450cc0dd *electron-api.json -fa47e752e559a6472b87d8907f63296ed8cd53ecf862a4ae113018474d40aa8e *electron-v34.2.0-darwin-arm64-dsym-snapshot.zip -336c3374e721e2379901141be6345459f78d243b037c65b55bac4ae8cb14bfbe *electron-v34.2.0-darwin-arm64-dsym.zip -ac3b9d712d9f036f066d8eba42797117a513e2d250fcc117f0354300b7d5c1e5 *electron-v34.2.0-darwin-arm64-symbols.zip -ee447c17b2ac545e48083113d7e39a916821c1316f60f42cbcbee4fffe7c022a *electron-v34.2.0-darwin-arm64.zip -d7510bc038d06b26690ac9a78fbb256626502303ff7f5b1c2242f64a02832b1b *electron-v34.2.0-darwin-x64-dsym-snapshot.zip -0e95c2bbda00afe78e6229b824e3ffe0cc8612956dc11a5a30380224acdbecae *electron-v34.2.0-darwin-x64-dsym.zip -6d734ef8e8fd007071aae9a13534894dd801f11900f3e73e49cf5352b426e575 *electron-v34.2.0-darwin-x64-symbols.zip -8ef741819c8a5370dabc3b9df5e6ac217366477c5d5c656ed23c800bc984d887 *electron-v34.2.0-darwin-x64.zip -c2f448882a0392ebfd9810058a6a9580b087002c74fca3dcd3cf4ba5c3b27a7c *electron-v34.2.0-linux-arm64-debug.zip -51e887c382593021127593ceba89ad662d3a6de0f748b94fe3a0ce78a7393923 *electron-v34.2.0-linux-arm64-symbols.zip -818c91309da8ff948c43df58a996c05c0d27daa690e1d659355d9db01e351919 *electron-v34.2.0-linux-arm64.zip -c2f448882a0392ebfd9810058a6a9580b087002c74fca3dcd3cf4ba5c3b27a7c *electron-v34.2.0-linux-armv7l-debug.zip -71ec2b7473db766194bcf04648229c4affedce06e150e692745cc72dbc3749c5 *electron-v34.2.0-linux-armv7l-symbols.zip -0c75996c6bf2d37d0441d3e1021386a9348f8d21783d75571cdb10ede606424f *electron-v34.2.0-linux-armv7l.zip -d1f17be8df8ec6dc1a23afd8a7752f0b949c755a493b8a2c1e83f76c629258ca *electron-v34.2.0-linux-x64-debug.zip -d8fa79154b0b663dbd0054d69f53aec326997449ef21943d27225a2d6899660f *electron-v34.2.0-linux-x64-symbols.zip -f12a02d86cc657500978d263ec6d1841b6e2085cd3bd4d30a97cfe14685396f3 *electron-v34.2.0-linux-x64.zip -71ef8bfebb8513a405fd2beb44ad18f802bbac2248f81a5328ddaaa12906d70c *electron-v34.2.0-mas-arm64-dsym-snapshot.zip -437aca6cad3158f15fd852e5913462637052940f812b20ccc10fccc133d5a0d6 *electron-v34.2.0-mas-arm64-dsym.zip -7f253375a7b43d34b770c03153e47185be7a64bc0c10a783993a93144eb35492 *electron-v34.2.0-mas-arm64-symbols.zip -1f601c20430b036b485c7dc02a39a297f0d08905462a4568306c12f299375e2f *electron-v34.2.0-mas-arm64.zip -d32664181804a17f825bf1fbfd8f0cbe01e0b41b284937359e6d9754b3481ed8 *electron-v34.2.0-mas-x64-dsym-snapshot.zip -f6b394b89fb77dfeefdf525739fe8cd9695f8c2ac2251ed7c571a376cde059e9 *electron-v34.2.0-mas-x64-dsym.zip -539328d93e9bc122e6e34faeab6a42f4845c523f796c7a11bbdcfe5e856f6b98 *electron-v34.2.0-mas-x64-symbols.zip -749b6ced7a9d35a719ad98d4c3bf1a08c315c19ce97d2497235d4ac302cda665 *electron-v34.2.0-mas-x64.zip -e843ea4cb7a93686728d056c957c124760886167932ff619b518e45917edf38a *electron-v34.2.0-win32-arm64-pdb.zip -4908423be5f8ad1b5dd737edfd69ee0870460e16bb639bb963b0981bda2628e4 *electron-v34.2.0-win32-arm64-symbols.zip -9d13b2bd61416eec28f43faa399cc5c0dc9e36dec226314bbf397828f55d74de *electron-v34.2.0-win32-arm64-toolchain-profile.zip -1629cec7b5620e6ca3b5305c393ae147d1a3871a8f164d686b7bee3810fc1109 *electron-v34.2.0-win32-arm64.zip -e7182f1ef5ed13187fe12f35133cefb50c59e1d52ada758cbb72813dda575205 *electron-v34.2.0-win32-ia32-pdb.zip -a10f778f62bf060a7e38f5ea75ea472679b9ef4f12767ee0703e6d77effae78a *electron-v34.2.0-win32-ia32-symbols.zip -9d13b2bd61416eec28f43faa399cc5c0dc9e36dec226314bbf397828f55d74de *electron-v34.2.0-win32-ia32-toolchain-profile.zip -96396712a0240f04471f71246b3885a513b08e3f2d40154272d34e59419db7e6 *electron-v34.2.0-win32-ia32.zip -0c55ef5b1a6ea4604e3f0e9f8b5e946944abd1cfc3b974432d40e924a9996812 *electron-v34.2.0-win32-x64-pdb.zip -d6b67cf12edabcc62ae21e7c90ac6b1161dbefe4e6b765c69fc7040096b7d026 *electron-v34.2.0-win32-x64-symbols.zip -9d13b2bd61416eec28f43faa399cc5c0dc9e36dec226314bbf397828f55d74de *electron-v34.2.0-win32-x64-toolchain-profile.zip -a4fdf617dca787b7f1c6f8d0d1cb69c8adb37ee23f9553fe69803f9cad713360 *electron-v34.2.0-win32-x64.zip -5ca44a4ee9cc74a56c9556ce3f3774986dbb8b4f4953088c7f4577bfba4356a8 *electron.d.ts -86247a6815cc98f321374edcbfc0ab7ee79bcb13a5c25213f42151e4059ec690 *ffmpeg-v34.2.0-darwin-arm64.zip -e94f4707a91194f97d0451f9d5a27982c873a5c13d83d20926ce6fdb613f536c *ffmpeg-v34.2.0-darwin-x64.zip -947d7b7cb035eab94cc15b602dfa8a925cf238c1d9225c01732fe0c41f59c571 *ffmpeg-v34.2.0-linux-arm64.zip -64d74b6b94ac4fd755b97e0ec8d50af9cd73810fdb52f6c081b13a3337ace0ea *ffmpeg-v34.2.0-linux-armv7l.zip -a62cb20c5e5f2ba6f1df6f1bc406cc30f7ed44fe6380b506afa333d1b4ad7a87 *ffmpeg-v34.2.0-linux-x64.zip -715bbb3d193b1ca57d4b329edd05cd6b125dc188cdb73d1c66f220f62c8562e3 *ffmpeg-v34.2.0-mas-arm64.zip -866bf47106e8c3e50f046783eb8f5c756069c6de962a46f4edc7a5ee7e4593ee *ffmpeg-v34.2.0-mas-x64.zip -71a2a6c4cca15ccbcbf8912f5d73f855b0ca79804f941f68527ae808f8163957 *ffmpeg-v34.2.0-win32-arm64.zip -399f841cca166781078ca42c8ea060e3d5850ec6cb79716186d0806f3ce20842 *ffmpeg-v34.2.0-win32-ia32.zip -afad2a44f20a0c0c01fb1fea637f3f821842399593c9961c74d650ca12d32cbb *ffmpeg-v34.2.0-win32-x64.zip -c95fdc9dba05aa68aeccb69d4c34f0cb1fb98d7f5291d974d0b638488693655f *hunspell_dictionaries.zip -0abe74138afdb6e45a085d77407659f13c75ab96f694313d4e98bd662f9c6df2 *libcxx-objects-v34.2.0-linux-arm64.zip -3d0cbf6fb150b006428eab78235856d2204d5e93ca85f162e429b4c8bd9b0d3b *libcxx-objects-v34.2.0-linux-armv7l.zip -3a5491e32cec825499919be1b8bf0550d28fe5a31ff00a95572d49a58bb4820a *libcxx-objects-v34.2.0-linux-x64.zip -ff8753d52f759041b8e5462125ee2b96798fe8b5047f8fb8ae60cd07af2fb13d *libcxx_headers.zip -c98cce0091681bc367a48f66c5f4602961aa9cb6dd1a995d8969d6b39ce732f3 *libcxxabi_headers.zip -78a9606190fb227460ddcd276ad540873595d6a82fd1007f42900f53347e3eb1 *mksnapshot-v34.2.0-darwin-arm64.zip -6d4587a36f509356a908b6752de527cfe8a294a2d435b82ea3a98288c3a3a178 *mksnapshot-v34.2.0-darwin-x64.zip -17737bd34f7feefc5143b745f2c4f0e5a41678780e144eb43de41f8d4b9d7852 *mksnapshot-v34.2.0-linux-arm64-x64.zip -565aa9a84d913b7b5eb8d3b868cff151cb8a6c16548233ffd92b2f9bf3789227 *mksnapshot-v34.2.0-linux-armv7l-x64.zip -7332d7864ab4e5ee7fa8d00580d9357f30d0e69d3d1434d67c61f79f666314a4 *mksnapshot-v34.2.0-linux-x64.zip -cc81bfc9894378a9fc8a777429e04cc80860663b3dff8eba1f8cc72559a4f23d *mksnapshot-v34.2.0-mas-arm64.zip -c98230088698638159f6e7e0c5ddde3cb4dba9fa81d76e3c58fc86f96b63ef81 *mksnapshot-v34.2.0-mas-x64.zip -cc7b42943d998e1083ad8119dc2201dd27d709c15aa2a9b78f409485d2927592 *mksnapshot-v34.2.0-win32-arm64-x64.zip -ade9e3126f113318e226f9bdeba068b4383b30a98517a361386eff448459effa *mksnapshot-v34.2.0-win32-ia32.zip -344292ea318dc0e21f37bc7c82d57a57449f0fb278d9868f1b1b597bdcb1f36f *mksnapshot-v34.2.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 d00d52bc25f9..d394605dda3a 100644 --- a/build/checksums/nodejs.txt +++ b/build/checksums/nodejs.txt @@ -1,7 +1,7 @@ -fa76d5b5340f14070ebaa88ef8faa28c1e9271502725e830cb52f0cf5b6493de node-v20.18.2-darwin-arm64.tar.gz -00a16bb0a82a2ad5d00d66b466ae1afa678482283747c27e9bce96668f334744 node-v20.18.2-darwin-x64.tar.gz -319789e8a055ff80793a05e633c8c5c9226050144a09da3747225b4ec56a2a99 node-v20.18.2-linux-arm64.tar.gz -65397a4a63960bda94718099698d2961623e9ef400f60f4c3a71add2268bccfb node-v20.18.2-linux-armv7l.tar.gz -eb5b031bdd728871c3b9a82655dbfa533bc262c0b6da1d09a86842430cef07d4 node-v20.18.2-linux-x64.tar.gz -83e7ad1b8c4d4d9c5e06849c3e8f3a5948a5eb6aa34c5bd973ba700e0386f42c win-arm64/node.exe -8487a277e92282904dfe0f860dbd5d229543e97a858a223fbe9c9b8670bbe170 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 3fbded851903..6b8419291cb6 100644 --- a/build/lib/stylelint/vscode-known-variables.json +++ b/build/lib/stylelint/vscode-known-variables.json @@ -57,7 +57,6 @@ "--vscode-chat-requestBorder", "--vscode-chat-slashCommandBackground", "--vscode-chat-slashCommandForeground", - "--vscode-chatEdits-minimapColor", "--vscode-checkbox-background", "--vscode-checkbox-border", "--vscode-checkbox-foreground", @@ -233,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", @@ -351,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", @@ -466,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", @@ -649,10 +658,6 @@ "--vscode-statusBar-noFolderForeground", "--vscode-statusBarItem-activeBackground", "--vscode-statusBarItem-compactHoverBackground", - "--vscode-statusBarItem-copilotBackground", - "--vscode-statusBarItem-copilotForeground", - "--vscode-statusBarItem-copilotHoverBackground", - "--vscode-statusBarItem-copilotHoverForeground", "--vscode-statusBarItem-errorBackground", "--vscode-statusBarItem-errorForeground", "--vscode-statusBarItem-errorHoverBackground", @@ -779,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", diff --git a/build/linux/dependencies-generator.js b/build/linux/dependencies-generator.js index 39e2b4e317b2..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.196: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 83d2ec78abb0..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.196: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/code.iss b/build/win32/code.iss index cad6e399f66b..853ea31901d8 100644 --- a/build/win32/code.iss +++ b/build/win32/code.iss @@ -34,6 +34,8 @@ ShowLanguageDialog=auto ArchitecturesAllowed={#ArchitecturesAllowed} ArchitecturesInstallIn64BitMode={#ArchitecturesInstallIn64BitMode} WizardStyle=modern +ASLRCompatible=yes +DEPCompatible=yes // We've seen an uptick on broken installations from updates which were unable // to shutdown VS Code. We rely on the fact that the update signals 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 24c00b3731dc..eb5b37d39a75 100644 --- a/cgmanifest.json +++ b/cgmanifest.json @@ -6,7 +6,7 @@ "git": { "name": "chromium", "repositoryUrl": "https://chromium.googlesource.com/chromium/src", - "commitHash": "46a5bd1e987735e0cdc41cf48a7db4988ae73d16" + "commitHash": "5c0cb964bca15fcf41718d54f4b8d70d6b9079de" } }, "licenseDetail": [ @@ -40,7 +40,7 @@ "SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." ], "isOnlyProductionDependency": true, - "version": "132.0.6834.196" + "version": "132.0.6834.210" }, { "component": { @@ -516,11 +516,11 @@ "git": { "name": "nodejs", "repositoryUrl": "https://github.com/nodejs/node", - "commitHash": "53a57efd83a18efe7a84bf1b460acc789139939b" + "commitHash": "4819c99baa28bf2c1baf411ba100c467fec3d486" } }, "isOnlyProductionDependency": true, - "version": "20.18.2" + "version": "20.18.3" }, { "component": { @@ -528,12 +528,12 @@ "git": { "name": "electron", "repositoryUrl": "https://github.com/electron/electron", - "commitHash": "ddc7afd3f006dab49a3b6bf73bf3222b017556d5" + "commitHash": "f98501308a973e0aee2414315b426e5de2c03a60" } }, "isOnlyProductionDependency": true, "license": "MIT", - "version": "34.2.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/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 8e3d288967b3..2b005721dee1 100644 --- a/eslint.config.js +++ b/eslint.config.js @@ -997,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 7a9cb2aded86..f12ed5a6f84f 100644 --- a/extensions/git/package-lock.json +++ b/extensions/git/package-lock.json @@ -10,25 +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", - "picomatch": "2.3.1", - "vscode-uri": "^2.0.0", - "which": "4.0.0" + "file-type": "21.3.0", + "jschardet": "3.1.4", + "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", @@ -39,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" @@ -121,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" @@ -157,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", @@ -230,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" @@ -263,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", @@ -278,136 +328,97 @@ "node": ">=16" } }, - "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==", + "node_modules/jschardet": { + "version": "3.1.4", + "resolved": "https://registry.npmjs.org/jschardet/-/jschardet-3.1.4.tgz", + "integrity": "sha512-/kmVISmrwVwtyYU40iQUOp3SUPk2dhNCMsZBQX0R1/jZ8maaXJ/oZIzUOiyOqcgtLnETFKYChbJ5iDC/eWmFHg==", + "license": "LGPL-2.1+", "engines": { - "node": ">=8" - }, - "funding": { - "type": "github", - "url": "https://github.com/sponsors/Borewit" + "node": ">=0.1.90" } }, + "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" }, @@ -415,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 06f40d948a12..1815af8783d6 100644 --- a/extensions/git/package.json +++ b/extensions/git/package.json @@ -21,7 +21,6 @@ "contribSourceControlInputBoxMenu", "contribSourceControlTitleMenu", "contribViewsWelcome", - "diffCommand", "editSessionIdentityProvider", "quickDiffProvider", "quickInputButtonLocation", @@ -35,7 +34,6 @@ "statusBarItemTooltip", "tabInputMultiDiff", "tabInputTextMerge", - "textDocumentEncoding", "textEditorDiffInformation", "timeline" ], @@ -3323,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%" } } }, @@ -3435,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" } } ], @@ -3563,20 +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", - "picomatch": "2.3.1", - "vscode-uri": "^2.0.0", - "which": "4.0.0" + "file-type": "21.3.0", + "jschardet": "3.1.4", + "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 b09e6d5a6c55..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)); @@ -1628,33 +1636,23 @@ export class CommandCenter { } let modifiedUri = changes.modifiedUri; - let modifiedDocument: TextDocument | undefined; - if (!modifiedUri) { const textEditor = window.activeTextEditor; if (!textEditor) { return; } - - modifiedDocument = textEditor.document; + const modifiedDocument = textEditor.document; modifiedUri = modifiedDocument.uri; } - if (modifiedUri.scheme !== 'file') { return; } - - if (!modifiedDocument) { - modifiedDocument = await workspace.openTextDocument(modifiedUri); - } - const result = changes.originalWithModifiedChanges; - await this.runByRepository(modifiedUri, async (repository, resource) => - await repository.stage(resource, result, modifiedDocument.encoding)); + 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) { @@ -1668,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)}`); @@ -1827,8 +1824,7 @@ export class CommandCenter { const originalDocument = await workspace.openTextDocument(originalUri); const result = applyLineChanges(originalDocument, modifiedDocument, changes); - await this.runByRepository(modifiedUri, async (repository, resource) => - await repository.stage(resource, result, modifiedDocument.encoding)); + await this.runByRepository(modifiedUri, async (repository, resource) => await repository.stage(resource, result)); } @command('git.revertChange') @@ -1849,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) { @@ -1864,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)}`); @@ -1939,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) { @@ -1978,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)}`); @@ -2000,7 +1994,7 @@ export class CommandCenter { this.logger.trace(`[CommandCenter][unstageSelectedRanges] invertedDiffs: ${JSON.stringify(invertedDiffs)}`); const result = applyLineChanges(modifiedDocument, originalDocument, invertedDiffs); - await repository.stage(modifiedUri, result, modifiedDocument.encoding); + await repository.stage(modifiedUri, result); } @command('git.unstageFile') @@ -2175,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.' @@ -2187,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.' @@ -2197,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') @@ -2716,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'); @@ -2936,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), @@ -3041,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); @@ -3050,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) { @@ -3128,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), @@ -3154,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); @@ -3193,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/encoding.ts b/extensions/git/src/encoding.ts new file mode 100644 index 000000000000..c80fb6ee6d5e --- /dev/null +++ b/extensions/git/src/encoding.ts @@ -0,0 +1,100 @@ +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +import * as jschardet from 'jschardet'; + +function detectEncodingByBOM(buffer: Buffer): string | null { + if (!buffer || buffer.length < 2) { + return null; + } + + const b0 = buffer.readUInt8(0); + const b1 = buffer.readUInt8(1); + + // UTF-16 BE + if (b0 === 0xFE && b1 === 0xFF) { + return 'utf16be'; + } + + // UTF-16 LE + if (b0 === 0xFF && b1 === 0xFE) { + return 'utf16le'; + } + + if (buffer.length < 3) { + return null; + } + + const b2 = buffer.readUInt8(2); + + // UTF-8 + if (b0 === 0xEF && b1 === 0xBB && b2 === 0xBF) { + return 'utf8'; + } + + return null; +} + +const IGNORE_ENCODINGS = [ + 'ascii', + 'utf-8', + 'utf-16', + 'utf-32' +]; + +const JSCHARDET_TO_ICONV_ENCODINGS: { [name: string]: string } = { + 'ibm866': 'cp866', + 'big5': 'cp950' +}; + +const MAP_CANDIDATE_GUESS_ENCODING_TO_JSCHARDET: { [key: string]: string } = { + utf8: 'UTF-8', + utf16le: 'UTF-16LE', + utf16be: 'UTF-16BE', + windows1252: 'windows-1252', + windows1250: 'windows-1250', + iso88592: 'ISO-8859-2', + windows1251: 'windows-1251', + cp866: 'IBM866', + iso88595: 'ISO-8859-5', + koi8r: 'KOI8-R', + windows1253: 'windows-1253', + iso88597: 'ISO-8859-7', + windows1255: 'windows-1255', + iso88598: 'ISO-8859-8', + cp950: 'Big5', + shiftjis: 'SHIFT_JIS', + eucjp: 'EUC-JP', + euckr: 'EUC-KR', + gb2312: 'GB2312' +}; + +export function detectEncoding(buffer: Buffer, candidateGuessEncodings: string[]): string | null { + const result = detectEncodingByBOM(buffer); + + if (result) { + return result; + } + + candidateGuessEncodings = candidateGuessEncodings.map(e => MAP_CANDIDATE_GUESS_ENCODING_TO_JSCHARDET[e]).filter(e => !!e); + + const detected = jschardet.detect(buffer, candidateGuessEncodings.length > 0 ? { detectEncodings: candidateGuessEncodings } : undefined); + if (!detected || !detected.encoding) { + return null; + } + + const encoding = detected.encoding; + + // Ignore encodings that cannot guess correctly + // (http://chardet.readthedocs.io/en/latest/supported-encodings.html) + if (0 <= IGNORE_ENCODINGS.indexOf(encoding.toLowerCase())) { + return null; + } + + const normalizedEncodingName = encoding.replace(/[^a-zA-Z0-9]/g, '').toLowerCase(); + const mapped = JSCHARDET_TO_ICONV_ENCODINGS[normalizedEncodingName]; + + return mapped || normalizedEncodingName; +} diff --git a/extensions/git/src/git.ts b/extensions/git/src/git.ts index 29fc38abfed1..4f64607aa6a1 100644 --- a/extensions/git/src/git.ts +++ b/extensions/git/src/git.ts @@ -12,9 +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 { Ref, RefType, Branch, Remote, ForcePushMode, GitErrorCodes, LogOptions, Change, Status, CommitOptions, RefQuery, InitOptions } from './api/git'; +import { detectEncoding } from './encoding'; +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'; @@ -354,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'; @@ -1125,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; @@ -1329,8 +1398,21 @@ export class Repository { .filter(entry => !!entry); } - async buffer(object: string): Promise { - const child = this.stream(['show', '--textconv', 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; + } + + encoding = iconv.encodingExists(encoding) ? encoding : 'utf8'; + + return iconv.decode(stdout, encoding); + } + + 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'); @@ -1382,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); @@ -1393,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({ @@ -1409,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 }> { @@ -1489,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; } @@ -1502,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; } @@ -1515,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; } @@ -1528,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; } @@ -1548,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(); @@ -1640,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 { @@ -1655,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); @@ -1690,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 { @@ -1710,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 { @@ -1924,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]))); } } @@ -1964,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 { @@ -2209,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) { @@ -2229,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); @@ -2550,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]; @@ -2564,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 { @@ -2840,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'); @@ -2879,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 2f164ee5ede1..f32d915d7151 100644 --- a/extensions/git/src/repository.ts +++ b/extensions/git/src/repository.ts @@ -7,6 +7,7 @@ import TelemetryReporter from '@vscode/extension-telemetry'; import * as fs from 'fs'; import * as path from 'path'; import picomatch from 'picomatch'; +import * as iconv from '@vscode/iconv-lite-umd'; import { CancellationError, CancellationToken, CancellationTokenSource, Command, commands, Disposable, Event, EventEmitter, FileDecoration, l10n, LogLevel, LogOutputChannel, Memento, ProgressLocation, ProgressOptions, QuickDiffProvider, RelativePattern, scm, SourceControl, SourceControlInputBox, SourceControlInputBoxValidation, SourceControlInputBoxValidationType, SourceControlResourceDecorations, SourceControlResourceGroup, SourceControlResourceState, TabInputNotebookDiff, TabInputTextDiff, TabInputTextMultiDiff, ThemeColor, Uri, window, workspace, WorkspaceEdit } from 'vscode'; import { ActionButton } from './actionButton'; import { ApiRepository } from './api/api1'; @@ -22,8 +23,9 @@ 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'; const timeout = (millis: number) => new Promise(c => setTimeout(c, millis)); @@ -1220,10 +1222,19 @@ export class Repository implements Disposable { await this.run(Operation.Remove, () => this.repository.rm(resources.map(r => r.fsPath))); } - async stage(resource: Uri, contents: string, encoding: string): Promise { + async stage(resource: Uri, contents: string): Promise { await this.run(Operation.Stage, async () => { - const path = relativePath(this.repository.root, resource.fsPath).replace(/\\/g, '/'); - await this.repository.stage(path, contents, encoding); + const configFiles = workspace.getConfiguration('files', Uri.file(resource.fsPath)); + let encoding = configFiles.get('encoding') ?? 'utf8'; + const autoGuessEncoding = configFiles.get('autoGuessEncoding') === true; + const candidateGuessEncodings = configFiles.get('candidateGuessEncodings') ?? []; + + if (autoGuessEncoding) { + encoding = detectEncoding(Buffer.from(contents), candidateGuessEncodings) ?? encoding; + } + + encoding = iconv.encodingExists(encoding) ? encoding : 'utf8'; + await this.repository.stage(resource.fsPath, contents, encoding); this._onDidChangeOriginalResource.fire(resource); this.closeDiffEditors([], [...resource.fsPath]); @@ -1349,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()), @@ -1388,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 })))); @@ -1521,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) { } @@ -1840,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 @@ -1967,16 +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 { - const content = await this.repository.buffer(`${ref}:${path}`); - return await workspace.decode(content, Uri.file(filePath)); + 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); - const content = await this.repository.buffer(`${ref}:${gitRelativePath}`); - return await workspace.decode(content, Uri.file(filePath)); + const gitFilePath = await this.repository.getGitFilePath(ref, filePath); + return await this.repository.bufferString(ref, gitFilePath, defaultEncoding, autoGuessEncoding, candidateGuessEncodings); } throw err; @@ -1985,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 1d330ea2516c..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", @@ -25,7 +24,6 @@ "../../src/vscode-dts/vscode.proposed.statusBarItemTooltip.d.ts", "../../src/vscode-dts/vscode.proposed.tabInputMultiDiff.d.ts", "../../src/vscode-dts/vscode.proposed.tabInputTextMerge.d.ts", - "../../src/vscode-dts/vscode.proposed.textDocumentEncoding.d.ts", "../../src/vscode-dts/vscode.proposed.textEditorDiffInformation.d.ts", "../../src/vscode-dts/vscode.proposed.timeline.d.ts", "../types/lib.textEncoder.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/html-language-features/server/src/test/jsdocImportFixtures/jsDocTypes.ts b/extensions/html-language-features/server/src/test/jsdocImportFixtures/jsDocTypes.ts new file mode 100644 index 000000000000..938dd5d7b2d5 --- /dev/null +++ b/extensions/html-language-features/server/src/test/jsdocImportFixtures/jsDocTypes.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. + *--------------------------------------------------------------------------------------------*/ + +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 51b9f80eb3a0..6f5157b29ab9 100644 --- a/extensions/markdown-language-features/package.json +++ b/extensions/markdown-language-features/package.json @@ -763,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-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 30878aa11548..a05866b16bc5 100644 --- a/extensions/terminal-suggest/scripts/pullZshBuiltins.ts +++ b/extensions/terminal-suggest/scripts/pullZshBuiltins.ts @@ -3,16 +3,11 @@ * 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; @@ -126,14 +121,14 @@ const shortDescriptions: Map = new Map([ ['ztcp', 'Manipulate TCP sockets'], ]); -const execAsync = promisify(exec); - interface ICommandDetails { description: string; args: string | undefined; shortDescription?: string; } + let zshBuiltinsCommandDescriptionsCache = new Map(); + async function createCommandDescriptionsCache(): Promise { const cachedCommandDescriptions: Map = new Map(); let output = ''; @@ -248,11 +243,12 @@ const main = async () => { console.log('\x1b[31mmissing short description for commands:\n' + missingShortDescription.join('\n') + '\x1b[0m'); } - // Save the cache to a JSON file - const cacheFilePath = path.join(__dirname, '../src/shell/zshBuiltinsCache.json'); + // 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 5f6e0ec5717a..775c5dd32648 100644 --- a/extensions/terminal-suggest/scripts/update-specs.js +++ b/extensions/terminal-suggest/scripts/update-specs.js @@ -14,8 +14,25 @@ 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`); @@ -26,5 +43,15 @@ for (const spec of upstreamSpecs) { 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/upstream/apt.ts b/extensions/terminal-suggest/src/completions/upstream/apt.ts index e3ebbb89a0d4..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 '../../helpers/filepaths'; +import { filepaths } from '../../fig/autocomplete-tools/generators' const packages: Fig.Generator = { // only trigger when the token length transitions to or from 0 @@ -134,6 +134,7 @@ const completionSpec: Fig.Spec = { name: "package", description: "The package you want to install", isVariadic: true, + template: 'filepaths', generators: [ packages, filepaths({ extensions: ["deb"] }) 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 b967b8545732..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 '../../helpers/filepaths'; +import { filepaths } from '../../fig/autocomplete-tools/generators' const completionSpec: Fig.Subcommand = { name: "node", @@ -6,6 +6,7 @@ const completionSpec: Fig.Subcommand = { args: { name: "node script", isScript: true, + template: 'filepaths', generators: filepaths({ extensions: ["mjs", "js", "cjs"], editFileSuggestions: { priority: 76 }, @@ -82,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 2374ee6b5e89..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 '../../helpers/filepaths'; +import { filepaths } from '../../fig/autocomplete-tools/generators' const completionSpec: Fig.Spec = { name: "python", @@ -21,6 +21,7 @@ const completionSpec: Fig.Spec = { }, args: { name: "python script", + template: "filepaths", generators: filepaths({ extensions: ["py"], editFileSuggestions: { priority: 76 }, 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 91e0dfa107a3..25ada5a06aa2 100644 --- a/extensions/terminal-suggest/src/constants.ts +++ b/extensions/terminal-suggest/src/constants.ts @@ -46,7 +46,6 @@ export const upstreamSpecs = [ '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 1262b5967ad1..4aa412ba248c 100644 --- a/extensions/terminal-suggest/src/fig/autocomplete/generators/customSuggestionsGenerator.ts +++ b/extensions/terminal-suggest/src/fig/autocomplete/generators/customSuggestionsGenerator.ts @@ -4,6 +4,7 @@ *--------------------------------------------------------------------------------------------*/ +import { FilepathsOptions } from '../../autocomplete-tools/generators'; import { IFigExecuteExternals } from '../../execute'; import { runCachedGenerator, @@ -15,7 +16,7 @@ export async function getCustomSuggestions( generator: Fig.Generator, context: GeneratorContext, executableExternals: IFigExecuteExternals -): Promise { +): Promise { if (!generator.custom) { return []; } @@ -25,6 +26,10 @@ export async function getCustomSuggestions( return []; } + if (generator.filepathOptions) { + return generator.filepathOptions; + } + const { tokenArray, currentWorkingDirectory, diff --git a/extensions/terminal-suggest/src/fig/autocomplete/generators/scriptSuggestionsGenerator.ts b/extensions/terminal-suggest/src/fig/autocomplete/generators/scriptSuggestionsGenerator.ts index 07e90069d032..bd8463265c49 100644 --- a/extensions/terminal-suggest/src/fig/autocomplete/generators/scriptSuggestionsGenerator.ts +++ b/extensions/terminal-suggest/src/fig/autocomplete/generators/scriptSuggestionsGenerator.ts @@ -28,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 = @@ -46,6 +46,7 @@ export async function getScriptSuggestions( command: commandToRun[0], args: commandToRun.slice(1), cwd: currentWorkingDirectory, + env: environmentVariables }; } else { executeCommandInput = { diff --git a/extensions/terminal-suggest/src/fig/autocomplete/state/generators.ts b/extensions/terminal-suggest/src/fig/autocomplete/state/generators.ts index dca21f8e4d90..4a0b72b55c38 100644 --- a/extensions/terminal-suggest/src/fig/autocomplete/state/generators.ts +++ b/extensions/terminal-suggest/src/fig/autocomplete/state/generators.ts @@ -82,7 +82,7 @@ export const createGeneratorState = ( // } const triggerGenerator = (currentState: GeneratorState, executeExternals: IFigExecuteExternals) => { const { generator, context } = currentState; - let request: Promise; + let request: Promise; if (generator.template) { // TODO: Implement template generators diff --git a/extensions/terminal-suggest/src/fig/execute.ts b/extensions/terminal-suggest/src/fig/execute.ts index ab9792f61378..2c642019b671 100644 --- a/extensions/terminal-suggest/src/fig/execute.ts +++ b/extensions/terminal-suggest/src/fig/execute.ts @@ -20,7 +20,7 @@ export const executeCommandTimeout = async ( ): Promise => { const command = [input.command, ...input.args].join(' '); try { - console.info(`About to run shell command '${command}'`); + console.debug(`About to run shell command '${command}'`); const result = await withTimeout( Math.max(timeout, input.timeout ?? 0), spawnHelper2(input.command, input.args, { diff --git a/extensions/terminal-suggest/src/fig/figInterface.ts b/extensions/terminal-suggest/src/fig/figInterface.ts index 1e283e463559..d9fb84832c07 100644 --- a/extensions/terminal-suggest/src/fig/figInterface.ts +++ b/extensions/terminal-suggest/src/fig/figInterface.ts @@ -16,7 +16,7 @@ import type { ICompletionResource } from '../types'; import { osIsWindows } from '../helpers/os'; import { removeAnyFileExtension } from '../helpers/file'; import type { EnvironmentVariable } from './api-bindings/types'; -import { asArray } from '../terminalSuggestMain'; +import { asArray, availableSpecs } from '../terminalSuggestMain'; import { IFigExecuteExternals } from './execute'; export interface IFigSpecSuggestionsResult { @@ -44,6 +44,7 @@ export async function getFigSuggestions( filesRequested: false, foldersRequested: false, hasCurrentArg: false, + fileExtensions: undefined, items: [], }; for (const spec of specs) { @@ -52,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; } @@ -81,22 +81,25 @@ export async function getFigSuggestions( 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, executeExternals, 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; + result.fileExtensions = completionItemResult.fileExtensions; if (completionItemResult.items) { result.items.push(...completionItemResult.items); } @@ -131,7 +134,7 @@ 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 @@ -143,7 +146,7 @@ async function getFigSpecSuggestions( if (completionItemResult) { filesRequested = completionItemResult.filesRequested; foldersRequested = completionItemResult.foldersRequested; - fileExtensions = completionItemResult.fileExtensions; + fileExtensions = completionItemResult.filesRequested ? parsedArguments.fileExtensions : undefined; } return { @@ -263,11 +266,13 @@ export async function collectCompletionItemResult( } let itemKind = kind; - if (typeof item === 'object' && 'args' in item && (asArray(item.args ?? [])).length > 0) { - itemKind = vscode.TerminalCompletionItemKind.Option; - } const lastArgType: string | undefined = parsedArguments?.annotations.at(-1)?.type; - if (lastArgType === 'option_arg') { + 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; } 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 8db683af892e..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( 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 7718b4a1c162..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,22 +39,8 @@ 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); @@ -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.ts similarity index 99% rename from extensions/terminal-suggest/src/shell/zshBuiltinsCache.json rename to extensions/terminal-suggest/src/shell/zshBuiltinsCache.ts index 62b17d69f0a7..6626192ce987 100644 --- a/extensions/terminal-suggest/src/shell/zshBuiltinsCache.json +++ b/extensions/terminal-suggest/src/shell/zshBuiltinsCache.ts @@ -1,4 +1,10 @@ -{ + +/*--------------------------------------------------------------------------------------------- + * Copyright (c) Microsoft Corporation. All rights reserved. + * Licensed under the MIT License. See License.txt in the project root for license information. + *--------------------------------------------------------------------------------------------*/ + +export const 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.", @@ -534,4 +540,4 @@ "description": ": See the section `The zsh/net/tcp Module` in *zshmodules(1).*", "args": "ztcp" } -} \ No newline at end of file +} 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 5138d5f7f808..ba26dc44035f 100644 --- a/extensions/terminal-suggest/src/terminalSuggestMain.ts +++ b/extensions/terminal-suggest/src/terminalSuggestMain.ts @@ -10,6 +10,7 @@ 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'; @@ -24,22 +25,14 @@ 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(); @@ -50,6 +43,7 @@ export const availableSpecs: Fig.Spec[] = [ cdSpec, codeInsidersCompletionSpec, codeCompletionSpec, + npxCompletionSpec, setLocationSpec, ]; for (const spec of upstreamSpecs) { @@ -70,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); @@ -94,25 +87,46 @@ 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?.value); - const shellGlobals = await getShellGlobals(shellType, commandsInPath?.labels) ?? []; + 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?.value), 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?.value?.HOME) { @@ -122,7 +136,7 @@ export async function activate(context: vscode.ExtensionContext) { } if (result.cwd && (result.filesRequested || result.foldersRequested)) { - return new vscode.TerminalCompletionList(result.items, { filesRequested: result.filesRequested, foldersRequested: result.foldersRequested, fileExtensions: result.fileExtensions, cwd: result.cwd, env: terminal.shellIntegration?.env?.value }); + 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; } @@ -169,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 { @@ -233,7 +247,7 @@ export async function getCompletionItemsFromSpecs( hasCurrentArg ||= result.hasCurrentArg; filesRequested ||= result.filesRequested; foldersRequested ||= result.foldersRequested; - fileExtensions = result.fileExtensions; + fileExtensions ??= result.fileExtensions; if (result.items) { items.push(...result.items); } @@ -277,7 +291,7 @@ 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, fileExtensions, cwd }; @@ -297,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/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/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 d5d84c58f4df..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(); }); } @@ -103,6 +103,20 @@ import { assertNoRpc } from '../utils'; 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'); + }); } test('execution events should fire in order when a command runs', async () => { 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 b75235f4473a..ceb3aea00d53 100644 --- a/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts +++ b/extensions/vscode-api-tests/src/singlefolder-tests/workspace.test.ts @@ -1498,4 +1498,21 @@ suite('vscode API - workspace', () => { 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-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-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 index e870a69ea3f9..a5a8c33bf22b 100644 --- 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 @@ -1,7 +1,7 @@ [ { "c": "const", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -15,7 +15,7 @@ }, { "c": "n", - "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": "null", - "t": "constant.language.null", + "t": "constant.language.null.ts", "r": { "dark_plus": "constant.language: #569CD6", "light_plus": "constant.language: #0000FF", @@ -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": "const", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -85,7 +85,7 @@ }, { "c": "u", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -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": "undefined", - "t": "constant.language.undefined", + "t": "constant.language.undefined.ts", "r": { "dark_plus": "constant.language: #569CD6", "light_plus": "constant.language: #0000FF", @@ -127,7 +127,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.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": "a", - "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": "true", - "t": "constant.language.boolean.true", + "t": "constant.language.boolean.true.ts", "r": { "dark_plus": "constant.language: #569CD6", "light_plus": "constant.language: #0000FF", @@ -197,7 +197,7 @@ }, { "c": "||", - "t": "keyword.operator.logical", + "t": "keyword.operator.logical.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -211,7 +211,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", @@ -225,7 +225,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -239,21 +239,21 @@ }, { "c": "type", - "t": "keyword.control", + "t": "storage.type.type.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" + "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", + "t": "entity.name.type.ts entity.name.type.alias.ts", "r": { "dark_plus": "entity.name.type: #4EC9B0", "light_plus": "entity.name.type: #267F99", @@ -267,7 +267,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -281,12 +281,12 @@ }, { "c": "null", - "t": "support.type.builtin", + "t": "constant.language.null.ts support.type.builtin.ts", "r": { "dark_plus": "support.type: #4EC9B0", "light_plus": "support.type: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", + "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", @@ -295,7 +295,7 @@ }, { "c": "|", - "t": "keyword.operator.type", + "t": "keyword.operator.ts keyword.operator.type.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -309,7 +309,7 @@ }, { "c": "unknown", - "t": "support.type.primitive", + "t": "support.type.ts support.type.primitive.ts", "r": { "dark_plus": "support.type: #4EC9B0", "light_plus": "support.type: #267F99", @@ -323,7 +323,7 @@ }, { "c": "|", - "t": "keyword.operator.type", + "t": "keyword.operator.ts keyword.operator.type.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -337,12 +337,12 @@ }, { "c": "undefined", - "t": "support.type.builtin", + "t": "constant.language.undefined.ts support.type.builtin.ts", "r": { "dark_plus": "support.type: #4EC9B0", "light_plus": "support.type: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", + "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", @@ -351,7 +351,7 @@ }, { "c": "|", - "t": "keyword.operator.type", + "t": "keyword.operator.ts keyword.operator.type.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -365,12 +365,12 @@ }, { "c": "true", - "t": "support.type.builtin", + "t": "constant.language.boolean.true.ts support.type.builtin.ts", "r": { "dark_plus": "support.type: #4EC9B0", "light_plus": "support.type: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", + "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", @@ -379,7 +379,7 @@ }, { "c": "|", - "t": "keyword.operator.type", + "t": "keyword.operator.ts keyword.operator.type.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -393,12 +393,12 @@ }, { "c": "false", - "t": "support.type.builtin", + "t": "constant.language.boolean.false.ts support.type.builtin.ts", "r": { "dark_plus": "support.type: #4EC9B0", "light_plus": "support.type: #267F99", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", + "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", @@ -407,7 +407,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -421,7 +421,7 @@ }, { "c": "function", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -435,7 +435,7 @@ }, { "c": "bar", - "t": "entity.name.function", + "t": "variable.ts entity.name.function.ts", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -449,7 +449,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -463,7 +463,7 @@ }, { "c": "a", - "t": "variable.parameter", + "t": "variable.ts variable.parameter.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -477,7 +477,7 @@ }, { "c": "?", - "t": "keyword.operator.optional", + "t": "punctuation.delimiter.ts keyword.operator.optional.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -491,7 +491,7 @@ }, { "c": ":", - "t": "keyword.operator.type.annotation", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -505,7 +505,7 @@ }, { "c": "string", - "t": "support.type.primitive", + "t": "support.type.ts support.type.primitive.ts", "r": { "dark_plus": "support.type: #4EC9B0", "light_plus": "support.type: #267F99", @@ -519,7 +519,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -533,7 +533,7 @@ }, { "c": ":", - "t": "keyword.operator.type.annotation", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -547,7 +547,7 @@ }, { "c": "string", - "t": "support.type.primitive", + "t": "support.type.ts support.type.primitive.ts", "r": { "dark_plus": "support.type: #4EC9B0", "light_plus": "support.type: #267F99", @@ -561,7 +561,7 @@ }, { "c": "{", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -575,7 +575,7 @@ }, { "c": "return", - "t": "keyword.control", + "t": "keyword.control.ts", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -589,7 +589,7 @@ }, { "c": "'", - "t": "string", + "t": "string.quoted.single.ts punctuation.definition.string.begin.ts punctuation.definition.string.end.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -603,7 +603,7 @@ }, { "c": "'", - "t": "string", + "t": "string.quoted.single.ts punctuation.definition.string.begin.ts punctuation.definition.string.end.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -617,7 +617,7 @@ }, { "c": "}", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -631,7 +631,7 @@ }, { "c": "interface", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -645,7 +645,7 @@ }, { "c": "A", - "t": "entity.name.type", + "t": "entity.name.type.ts entity.name.type.interface.ts", "r": { "dark_plus": "entity.name.type: #4EC9B0", "light_plus": "entity.name.type: #267F99", @@ -659,7 +659,7 @@ }, { "c": "{", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -673,7 +673,7 @@ }, { "c": "b", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -687,7 +687,7 @@ }, { "c": "?", - "t": "keyword.operator.optional", + "t": "punctuation.delimiter.ts keyword.operator.optional.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -701,7 +701,7 @@ }, { "c": ":", - "t": "keyword.operator.type.annotation", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -715,7 +715,7 @@ }, { "c": "2", - "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": "a", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -757,7 +757,7 @@ }, { "c": ":", - "t": "keyword.operator.type.annotation", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -771,7 +771,7 @@ }, { "c": "1", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -785,7 +785,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.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": "const", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -827,7 +827,7 @@ }, { "c": "obj", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -841,7 +841,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -855,7 +855,7 @@ }, { "c": "{", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -869,7 +869,7 @@ }, { "c": "a", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -883,7 +883,7 @@ }, { "c": ":", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -897,7 +897,7 @@ }, { "c": "1", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -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": "}", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -939,7 +939,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -953,7 +953,7 @@ }, { "c": "const", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -967,7 +967,7 @@ }, { "c": "obj1", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -981,7 +981,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.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.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1037,7 +1037,7 @@ }, { "c": "const", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -1051,7 +1051,7 @@ }, { "c": "obj2", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -1065,7 +1065,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1079,7 +1079,7 @@ }, { "c": "{", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1093,7 +1093,7 @@ }, { "c": "...", - "t": "keyword.operator.spread", + "t": "keyword.operator.spread.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1107,7 +1107,7 @@ }, { "c": "obj1", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -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": "function", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -1163,7 +1163,7 @@ }, { "c": "foo", - "t": "entity.name.function", + "t": "variable.ts entity.name.function.ts", "r": { "dark_plus": "entity.name.function: #DCDCAA", "light_plus": "entity.name.function: #795E26", @@ -1177,7 +1177,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1191,7 +1191,7 @@ }, { "c": "param", - "t": "variable.parameter", + "t": "variable.ts variable.parameter.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -1205,7 +1205,7 @@ }, { "c": ":", - "t": "keyword.operator.type.annotation", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1219,7 +1219,7 @@ }, { "c": "string", - "t": "support.type.primitive", + "t": "support.type.ts support.type.primitive.ts", "r": { "dark_plus": "support.type: #4EC9B0", "light_plus": "support.type: #267F99", @@ -1233,7 +1233,7 @@ }, { "c": ",", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1247,7 +1247,7 @@ }, { "c": "...", - "t": "keyword.operator.rest", + "t": "keyword.operator.rest.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1261,7 +1261,7 @@ }, { "c": "rest", - "t": "variable", + "t": "variable.ts variable.parameter.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -1275,7 +1275,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1289,7 +1289,7 @@ }, { "c": "{", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #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": "undefined", - "t": "constant.language.undefined", + "t": "constant.language.undefined.ts", "r": { "dark_plus": "constant.language: #569CD6", "light_plus": "constant.language: #0000FF", @@ -1331,7 +1331,7 @@ }, { "c": "null", - "t": "constant.language.null", + "t": "constant.language.null.ts", "r": { "dark_plus": "constant.language: #569CD6", "light_plus": "constant.language: #0000FF", @@ -1345,7 +1345,7 @@ }, { "c": "NaN", - "t": "constant.language.nan", + "t": "variable.ts constant.language.nan.ts", "r": { "dark_plus": "constant.language: #569CD6", "light_plus": "constant.language: #0000FF", @@ -1359,7 +1359,7 @@ }, { "c": "Infinity", - "t": "constant.language.infinity", + "t": "variable.ts constant.language.infinity.ts", "r": { "dark_plus": "constant.language: #569CD6", "light_plus": "constant.language: #0000FF", @@ -1373,21 +1373,21 @@ }, { "c": "type", - "t": "keyword.control", + "t": "storage.type.type.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" + "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", + "t": "entity.name.type.ts entity.name.type.alias.ts", "r": { "dark_plus": "entity.name.type: #4EC9B0", "light_plus": "entity.name.type: #267F99", @@ -1401,7 +1401,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1415,7 +1415,7 @@ }, { "c": "'", - "t": "string", + "t": "string.quoted.single.ts punctuation.definition.string.begin.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1429,7 +1429,7 @@ }, { "c": "zcxvf", - "t": "string", + "t": "string.quoted.single.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1443,7 +1443,7 @@ }, { "c": "'", - "t": "string", + "t": "string.quoted.single.ts punctuation.definition.string.end.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1457,7 +1457,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1471,21 +1471,21 @@ }, { "c": "type", - "t": "keyword.control", + "t": "storage.type.type.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" + "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", + "t": "entity.name.type.ts entity.name.type.alias.ts", "r": { "dark_plus": "entity.name.type: #4EC9B0", "light_plus": "entity.name.type: #267F99", @@ -1499,7 +1499,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1513,7 +1513,7 @@ }, { "c": "'", - "t": "string", + "t": "string.quoted.single.ts punctuation.definition.string.begin.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1527,7 +1527,7 @@ }, { "c": "one", - "t": "string", + "t": "string.quoted.single.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1541,7 +1541,7 @@ }, { "c": "'", - "t": "string", + "t": "string.quoted.single.ts punctuation.definition.string.end.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1555,7 +1555,7 @@ }, { "c": "|", - "t": "keyword.operator.type", + "t": "keyword.operator.ts keyword.operator.type.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1569,7 +1569,7 @@ }, { "c": "'", - "t": "string", + "t": "string.quoted.single.ts punctuation.definition.string.begin.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1583,7 +1583,7 @@ }, { "c": "two", - "t": "string", + "t": "string.quoted.single.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1597,7 +1597,7 @@ }, { "c": "'", - "t": "string", + "t": "string.quoted.single.ts punctuation.definition.string.end.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1611,7 +1611,7 @@ }, { "c": "&", - "t": "keyword.operator.type", + "t": "keyword.operator.ts keyword.operator.type.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1625,7 +1625,7 @@ }, { "c": "'", - "t": "string", + "t": "string.quoted.single.ts punctuation.definition.string.begin.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1639,7 +1639,7 @@ }, { "c": "three", - "t": "string", + "t": "string.quoted.single.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -1653,7 +1653,7 @@ }, { "c": "'", - "t": "string", + "t": "string.quoted.single.ts punctuation.definition.string.end.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -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": "const", - "t": "storage.type", + "t": "storage.type.ts", "r": { "dark_plus": "storage.type: #569CD6", "light_plus": "storage.type: #0000FF", @@ -1695,7 +1695,7 @@ }, { "c": "obj", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -1709,7 +1709,7 @@ }, { "c": "=", - "t": "keyword.operator.assignment", + "t": "keyword.operator.assignment.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1723,7 +1723,7 @@ }, { "c": "{", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1737,7 +1737,7 @@ }, { "c": "one", - "t": "variable", + "t": "variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -1751,7 +1751,7 @@ }, { "c": ":", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1765,7 +1765,7 @@ }, { "c": "1", - "t": "constant.numeric", + "t": "constant.numeric.ts", "r": { "dark_plus": "constant.numeric: #B5CEA8", "light_plus": "constant.numeric: #098658", @@ -1779,7 +1779,7 @@ }, { "c": "}", - "t": "punctuation", + "t": "punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1793,7 +1793,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1807,21 +1807,21 @@ }, { "c": "type", - "t": "keyword.control", + "t": "storage.type.type.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" + "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", + "t": "entity.name.type.ts entity.name.type.alias.ts", "r": { "dark_plus": "entity.name.type: #4EC9B0", "light_plus": "entity.name.type: #267F99", @@ -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": "Record", - "t": "entity.name.type", + "t": "entity.name.type.ts entity.name.type.ts", "r": { "dark_plus": "entity.name.type: #4EC9B0", "light_plus": "entity.name.type: #267F99", @@ -1863,7 +1863,7 @@ }, { "c": "<", - "t": "punctuation.definition.typeparameters", + "t": "punctuation.definition.typeparameters.begin.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1877,7 +1877,7 @@ }, { "c": "string", - "t": "support.type.primitive", + "t": "support.type.ts support.type.primitive.ts", "r": { "dark_plus": "support.type: #4EC9B0", "light_plus": "support.type: #267F99", @@ -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": "string", - "t": "support.type.primitive", + "t": "support.type.ts support.type.primitive.ts", "r": { "dark_plus": "support.type: #4EC9B0", "light_plus": "support.type: #267F99", @@ -1919,7 +1919,7 @@ }, { "c": ">", - "t": "punctuation.definition.typeparameters", + "t": "punctuation.definition.typeparameters.end.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1933,7 +1933,7 @@ }, { "c": ";", - "t": "punctuation.delimiter", + "t": "punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1947,7 +1947,7 @@ }, { "c": "new", - "t": "keyword.operator.new", + "t": "new.expr.ts keyword.operator.new.ts", "r": { "dark_plus": "keyword.operator.new: #569CD6", "light_plus": "keyword.operator.new: #0000FF", @@ -1961,21 +1961,21 @@ }, { "c": "URL", - "t": "variable.other.constant", + "t": "new.expr.ts variable.ts entity.name.function.ts", "r": { - "dark_plus": "variable.other.constant: #4FC1FF", - "light_plus": "variable.other.constant: #0070C1", + "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.other.constant: #4FC1FF", - "hc_light": "variable.other.constant: #02715D", - "light_modern": "variable.other.constant: #0070C1" + "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": "new.expr.ts punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -1989,7 +1989,7 @@ }, { "c": "'", - "t": "string", + "t": "new.expr.ts string.quoted.single.ts punctuation.definition.string.begin.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -2003,7 +2003,7 @@ }, { "c": "./../../renderer/dist/index.html", - "t": "string", + "t": "new.expr.ts string.quoted.single.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -2017,7 +2017,7 @@ }, { "c": "'", - "t": "string", + "t": "new.expr.ts string.quoted.single.ts punctuation.definition.string.end.ts", "r": { "dark_plus": "string: #CE9178", "light_plus": "string: #A31515", @@ -2031,7 +2031,7 @@ }, { "c": ",", - "t": "punctuation.delimiter", + "t": "new.expr.ts punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2045,7 +2045,7 @@ }, { "c": "import", - "t": "keyword.control", + "t": "new.expr.ts keyword.control.ts", "r": { "dark_plus": "keyword.control: #C586C0", "light_plus": "keyword.control: #AF00DB", @@ -2059,7 +2059,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "new.expr.ts punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2073,7 +2073,7 @@ }, { "c": "meta", - "t": "new.expr", + "t": "new.expr.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2087,7 +2087,7 @@ }, { "c": ".", - "t": "punctuation.delimiter", + "t": "new.expr.ts punctuation.delimiter.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2101,7 +2101,7 @@ }, { "c": "url", - "t": "variable", + "t": "new.expr.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -2115,7 +2115,7 @@ }, { "c": ")", - "t": "punctuation", + "t": "new.expr.ts punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -2129,7 +2129,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-function-inv_ts.json b/extensions/vscode-colorize-tests/test/colorize-tree-sitter-results/test-function-inv_ts.json index e2bc3557ae5c..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": "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": "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": "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": "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": "punctuation", + "t": "new.expr.ts punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -127,7 +127,7 @@ }, { "c": "row", - "t": "variable", + "t": "new.expr.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -141,7 +141,7 @@ }, { "c": ",", - "t": "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": "variable", + "t": "new.expr.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -169,7 +169,7 @@ }, { "c": ",", - "t": "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": "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": "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 9ffdc10c74cc..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": "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": "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": "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": "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,7 +1947,7 @@ }, { "c": ":", - "t": "keyword.operator.type.annotation", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1961,7 +1961,7 @@ }, { "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.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": "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,7 +2381,7 @@ }, { "c": ":", - "t": "keyword.operator.type.annotation", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -2395,7 +2395,7 @@ }, { "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.parameter", + "t": "variable.ts variable.parameter.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -2451,7 +2451,7 @@ }, { "c": "?", - "t": "keyword.operator.optional", + "t": "punctuation.delimiter.ts keyword.operator.optional.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -2465,7 +2465,7 @@ }, { "c": ":", - "t": "keyword.operator.type.annotation", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -2479,7 +2479,7 @@ }, { "c": "number", - "t": "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.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,7 +2619,7 @@ }, { "c": ":", - "t": "keyword.operator.type.annotation", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -2633,7 +2633,7 @@ }, { "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.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.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": "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": "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": "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.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 4e5c11e475e1..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": "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.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.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.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": "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": "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": "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,12 +295,12 @@ }, { "c": "startTime", - "t": "variable", + "t": "string.template.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", "hc_black": "variable: #9CDCFE", "dark_modern": "variable: #9CDCFE", "hc_light": "variable: #001080", @@ -309,7 +309,7 @@ }, { "c": "}", - "t": "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": "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,12 +351,12 @@ }, { "c": "endTime", - "t": "variable", + "t": "string.template.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", "hc_black": "variable: #9CDCFE", "dark_modern": "variable: #9CDCFE", "hc_light": "variable: #001080", @@ -365,7 +365,7 @@ }, { "c": "}", - "t": "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 0f1813112792..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": "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": "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,7 +141,7 @@ }, { "c": ":", - "t": "keyword.operator.type.annotation", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -155,7 +155,7 @@ }, { "c": "any", - "t": "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 005ae69ef887..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": "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": "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": "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": "punctuation", + "t": "new.expr.ts punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -141,7 +141,7 @@ }, { "c": "'", - "t": "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": "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": "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 a34e098b59d0..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": "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,7 +99,7 @@ }, { "c": ":", - "t": "keyword.operator.type.annotation", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -113,7 +113,7 @@ }, { "c": "number", - "t": "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": "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.parameter", + "t": "variable.ts variable.parameter.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -225,7 +225,7 @@ }, { "c": ":", - "t": "keyword.operator.type.annotation", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -239,7 +239,7 @@ }, { "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 58210899070d..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": "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,12 +85,12 @@ }, { "c": "foo", - "t": "variable", + "t": "string.template.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", "hc_black": "variable: #9CDCFE", "dark_modern": "variable: #9CDCFE", "hc_light": "variable: #001080", @@ -99,7 +99,7 @@ }, { "c": "}", - "t": "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": "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": "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": "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,12 +365,12 @@ }, { "c": "a", - "t": "variable", + "t": "string.template.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", "hc_black": "variable: #9CDCFE", "dark_modern": "variable: #9CDCFE", "hc_light": "variable: #001080", @@ -379,7 +379,7 @@ }, { "c": "+", - "t": "keyword.operator.arithmetic", + "t": "string.template.ts keyword.operator.arithmetic.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -393,12 +393,12 @@ }, { "c": "b", - "t": "variable", + "t": "string.template.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", "hc_black": "variable: #9CDCFE", "dark_modern": "variable: #9CDCFE", "hc_light": "variable: #001080", @@ -407,7 +407,7 @@ }, { "c": "}", - "t": "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": "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,12 +449,12 @@ }, { "c": "a", - "t": "variable", + "t": "string.template.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", "hc_black": "variable: #9CDCFE", "dark_modern": "variable: #9CDCFE", "hc_light": "variable: #001080", @@ -463,7 +463,7 @@ }, { "c": "*", - "t": "keyword.operator.arithmetic", + "t": "string.template.ts keyword.operator.arithmetic.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -477,12 +477,12 @@ }, { "c": "b", - "t": "variable", + "t": "string.template.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", - "dark_vs": "default: #D4D4D4", - "light_vs": "default: #000000", + "dark_vs": "string: #CE9178", + "light_vs": "string: #A31515", "hc_black": "variable: #9CDCFE", "dark_modern": "variable: #9CDCFE", "hc_light": "variable: #001080", @@ -491,7 +491,7 @@ }, { "c": "}", - "t": "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 4583b6573635..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", + "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 8a0ca583697f..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,7 +141,7 @@ }, { "c": ":", - "t": "keyword.operator.type.annotation", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -155,7 +155,7 @@ }, { "c": "number", - "t": "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,7 +211,7 @@ }, { "c": ":", - "t": "keyword.operator.type.annotation", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -225,7 +225,7 @@ }, { "c": "number", - "t": "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,7 +281,7 @@ }, { "c": ":", - "t": "keyword.operator.type.annotation", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -295,7 +295,7 @@ }, { "c": "boolean", - "t": "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": "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.parameter", + "t": "variable.ts variable.parameter.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -365,7 +365,7 @@ }, { "c": ":", - "t": "keyword.operator.type.annotation", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -379,7 +379,7 @@ }, { "c": "number", - "t": "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.parameter", + "t": "variable.ts variable.parameter.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -421,7 +421,7 @@ }, { "c": ":", - "t": "keyword.operator.type.annotation", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -435,7 +435,7 @@ }, { "c": "number", - "t": "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.parameter", + "t": "variable.ts variable.parameter.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -477,7 +477,7 @@ }, { "c": ":", - "t": "keyword.operator.type.annotation", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -491,7 +491,7 @@ }, { "c": "boolean", - "t": "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", + "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", + "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", + "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,7 +883,7 @@ }, { "c": ":", - "t": "keyword.operator.type.annotation", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -897,7 +897,7 @@ }, { "c": "number", - "t": "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,7 +953,7 @@ }, { "c": ":", - "t": "keyword.operator.type.annotation", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -967,7 +967,7 @@ }, { "c": "number", - "t": "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,7 +1023,7 @@ }, { "c": ":", - "t": "keyword.operator.type.annotation", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1037,7 +1037,7 @@ }, { "c": "string", - "t": "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,7 +1093,7 @@ }, { "c": ":", - "t": "keyword.operator.type.annotation", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1107,7 +1107,7 @@ }, { "c": "string", - "t": "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,7 +1163,7 @@ }, { "c": ":", - "t": "keyword.operator.type.annotation", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1177,7 +1177,7 @@ }, { "c": "string", - "t": "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,7 +1233,7 @@ }, { "c": ":", - "t": "keyword.operator.type.annotation", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1247,7 +1247,7 @@ }, { "c": "number", - "t": "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,7 +1303,7 @@ }, { "c": ":", - "t": "keyword.operator.type.annotation", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1317,7 +1317,7 @@ }, { "c": "number", - "t": "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,7 +1373,7 @@ }, { "c": ":", - "t": "keyword.operator.type.annotation", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -1387,7 +1387,7 @@ }, { "c": "number", - "t": "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": "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", + "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", + "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", + "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", + "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", + "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", + "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", + "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", + "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", + "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": "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": "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": "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": "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.parameter", + "t": "variable.ts variable.parameter.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -2675,7 +2675,7 @@ }, { "c": ":", - "t": "keyword.operator.type.annotation", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -2689,7 +2689,7 @@ }, { "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": "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", + "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": "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,7 +3095,7 @@ }, { "c": ":", - "t": "keyword.operator.type.annotation", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -3109,7 +3109,7 @@ }, { "c": "void", - "t": "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", + "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": "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.parameter", + "t": "variable.ts variable.parameter.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -3277,7 +3277,7 @@ }, { "c": ":", - "t": "keyword.operator.type.annotation", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -3291,7 +3291,7 @@ }, { "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", + "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": "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": "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": "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": "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", + "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": "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.parameter", + "t": "variable.ts variable.parameter.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -4145,7 +4145,7 @@ }, { "c": ":", - "t": "keyword.operator.type.annotation", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -4159,7 +4159,7 @@ }, { "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": "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": "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": "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": "punctuation", + "t": "new.expr.ts punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -4425,7 +4425,7 @@ }, { "c": "cell", - "t": "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": "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": "variable", + "t": "new.expr.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -4467,7 +4467,7 @@ }, { "c": ",", - "t": "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": "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": "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": "variable", + "t": "new.expr.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -4523,7 +4523,7 @@ }, { "c": ",", - "t": "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": "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": "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": "variable", + "t": "new.expr.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -4579,7 +4579,7 @@ }, { "c": ")", - "t": "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": "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.parameter", + "t": "variable.ts variable.parameter.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -5125,7 +5125,7 @@ }, { "c": ":", - "t": "keyword.operator.type.annotation", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -5139,7 +5139,7 @@ }, { "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": "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": "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.parameter", + "t": "variable.ts variable.parameter.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -6343,7 +6343,7 @@ }, { "c": ":", - "t": "keyword.operator.type.annotation", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -6357,7 +6357,7 @@ }, { "c": "number", - "t": "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.parameter", + "t": "variable.ts variable.parameter.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -6399,7 +6399,7 @@ }, { "c": ":", - "t": "keyword.operator.type.annotation", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -6413,7 +6413,7 @@ }, { "c": "number", - "t": "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", + "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", + "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", + "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": "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.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", + "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", + "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": "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": "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": "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": "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": "punctuation", + "t": "new.expr.ts punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -7855,7 +7855,7 @@ }, { "c": "row", - "t": "variable", + "t": "new.expr.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -7869,7 +7869,7 @@ }, { "c": ",", - "t": "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": "variable", + "t": "new.expr.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -7897,7 +7897,7 @@ }, { "c": ",", - "t": "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": "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": "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": "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": "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.parameter", + "t": "variable.ts variable.parameter.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -8219,7 +8219,7 @@ }, { "c": ":", - "t": "keyword.operator.type.annotation", + "t": "punctuation.delimiter.ts keyword.operator.type.annotation.ts", "r": { "dark_plus": "keyword.operator: #D4D4D4", "light_plus": "keyword.operator: #000000", @@ -8233,7 +8233,7 @@ }, { "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", + "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", + "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", + "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", + "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", + "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", + "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", + "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": "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", + "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", + "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", + "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", + "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", + "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": "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", + "t": "variable.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -9311,7 +9311,7 @@ }, { "c": ":", - "t": "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", + "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", + "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": "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", + "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", + "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", + "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", + "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": "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": "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": "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": "variable", + "t": "new.expr.ts variable.ts", "r": { "dark_plus": "variable: #9CDCFE", "light_plus": "variable: #001080", @@ -9955,7 +9955,7 @@ }, { "c": "(", - "t": "punctuation", + "t": "new.expr.ts punctuation.ts", "r": { "dark_plus": "default: #D4D4D4", "light_plus": "default: #000000", @@ -9969,7 +9969,7 @@ }, { "c": ")", - "t": "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 d4bc1fb28bf6..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/policy-watcher": "^1.3.0", "@vscode/proxy-agent": "^0.32.0", - "@vscode/ripgrep": "^1.15.10", - "@vscode/spdlog": "^0.15.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.3", - "@vscode/vscode-languagedetection": "1.0.21", - "@vscode/windows-mutex": "^0.5.0", - "@vscode/windows-process-tree": "^0.6.0", + "@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.2.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": { @@ -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,132 +3749,52 @@ } }, "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": { @@ -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.2.0", - "resolved": "https://registry.npmjs.org/electron/-/electron-34.2.0.tgz", - "integrity": "sha512-SYwBJNeXBTm1q/ErybQMUBZAYqEreBUqBwTrNkw1rV4YatDZk5Aittpcus3PPeC4UoI/tqmJ946uG8AKHTd6CA==", + "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 925462fb087e..f8bd5abf5c1e 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "code-oss-dev", - "version": "1.98.0", - "distro": "aee970faa436e6fdd88d579c1409364c3fdab6a8", + "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/policy-watcher": "^1.3.0", "@vscode/proxy-agent": "^0.32.0", - "@vscode/ripgrep": "^1.15.10", - "@vscode/spdlog": "^0.15.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.3", - "@vscode/vscode-languagedetection": "1.0.21", - "@vscode/windows-mutex": "^0.5.0", - "@vscode/windows-process-tree": "^0.6.0", + "@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.2.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 1d8b1e877826..e2c53927b15e 100644 --- a/remote/.npmrc +++ b/remote/.npmrc @@ -1,6 +1,6 @@ disturl="https://nodejs.org/dist" -target="20.18.2" -ms_build_id="320948" +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 2349babd03e8..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.32.0", - "@vscode/ripgrep": "^1.15.10", - "@vscode/spdlog": "^0.15.0", - "@vscode/tree-sitter-wasm": "^0.1.3", - "@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.32.0", - "resolved": "https://registry.npmjs.org/@vscode/proxy-agent/-/proxy-agent-0.32.0.tgz", - "integrity": "sha512-n6h2+WVMJ3ByfGUakDbBNpR25J2JpLQabofiTKHIcLpXfxhT5TQSEH4OcjesZZfqw1zDpd7oBgcgqToWIiaBrQ==", + "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.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==", + "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 a25b884dedb9..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.32.0", - "@vscode/ripgrep": "^1.15.10", - "@vscode/spdlog": "^0.15.0", - "@vscode/tree-sitter-wasm": "^0.1.3", - "@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 e13c2c65e521..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.3", - "@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.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==", + "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 78336aa2fa45..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.3", - "@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 c132c9b8b9bb..fdc424e10875 100644 --- a/src/main.ts +++ b/src/main.ts @@ -522,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 f38ab6d17c03..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 df0e343e3efb..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. @@ -387,7 +387,16 @@ export function isManagedHoverTooltipMarkdownString(obj: unknown): obj is IManag return typeof candidate === 'object' && 'markdown' in candidate && 'markdownNotSupportedFallback' in candidate; } -export type IManagedHoverContent = string | IManagedHoverTooltipMarkdownString | HTMLElement | undefined; +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/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/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 070103b838d2..4fa81895fa27 100644 --- a/src/vs/base/common/htmlContent.ts +++ b/src/vs/base/common/htmlContent.ts @@ -70,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/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 b1f8f4e95301..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,7 +186,7 @@ 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._run(); @@ -194,7 +194,7 @@ export class AutorunObserver implements IObserver, IReader } public dispose(): void { - this.disposed = true; + this._disposed = true; for (const o of this._dependencies) { o.removeObserver(this); // Warning: external call! } @@ -205,18 +205,18 @@ export class AutorunObserver implements IObserver, IReader } private _run() { - const emptySet = this.dependenciesToBeRemoved; - this.dependenciesToBeRemoved = this._dependencies; + 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) { @@ -226,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(); } } @@ -247,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; @@ -271,10 +271,10 @@ export class AutorunObserver implements IObserver, IReader } while (this._state !== AutorunState.upToDate); } } finally { - this.updateCount--; + this._updateCount--; } - assertFn(() => this.updateCount >= 0); + assertFn(() => this._updateCount >= 0); } public handlePossibleChange(observable: IObservable): void { @@ -292,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; } @@ -303,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 @@ -312,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 0735be62c234..59d8dc725a72 100644 --- a/src/vs/base/common/observableInternal/derived.ts +++ b/src/vs/base/common/observableInternal/derived.ts @@ -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); @@ -283,17 +283,17 @@ export class Derived extends BaseObservable im } // 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 _recompute() { - const emptySet = this.dependenciesToBeRemoved; - this.dependenciesToBeRemoved = this._dependencies; + 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; @@ -301,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, @@ -396,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); @@ -405,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; @@ -414,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); } @@ -443,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; } @@ -469,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/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/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/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/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/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 21a297a12d85..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'; @@ -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,6 +603,94 @@ export class CodeApplication extends Disposable { // Services const appInstantiationService = await this.initServices(machineId, sqmId, devDeviceId, sharedProcessReady); + // 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 appInstantiationService.invokeFunction(accessor => accessor.get(IProxyAuthService)); @@ -640,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); @@ -1046,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)); @@ -1119,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)); @@ -1189,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); @@ -1222,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 b3166de0437a..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 { @@ -34,15 +38,19 @@ export class FocusTracker extends Disposable { } 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/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 7d249eb7065f..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 { @@ -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/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 3fdecc8717e1..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 331c3f199c5f..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); 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 18d45571ccfe..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,16 +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) + (":") @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) [ "{" @@ -211,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" @@ -264,10 +368,9 @@ "switch" "throw" "try" - "type" "while" "yield" -] @keyword.control +] @keyword.control.ts [ "abstract" @@ -281,7 +384,7 @@ "public" "readonly" "static" -] @storage.modifier +] @storage.modifier.ts [ "=>" @@ -295,73 +398,84 @@ "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) + ("?") @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 [ @@ -369,7 +483,7 @@ (undefined) (true) (false) - ] @support.type.builtin) + ] @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 2df635ed6d9c..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,71 +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(model: ITextModel, 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 { - ranges.push({ newRange: Range.fromPositions(node.newStartPosition, node.newEndPosition), oldRangeLength: node.oldEndIndex - node.oldStartIndex, newRangeStartOffset: node.newStartIndex, newRangeEndOffset: node.newEndIndex }); + nodesInRange = [cursor.currentNode]; } - } - if (ranges.length > 0) { - const lastRange = ranges[ranges.length - 1]; - const maxLine = model.getLineCount(); - if (lastRange.newRange.endLineNumber > maxLine) { - lastRange.newRange = new Range(lastRange.newRange.startLineNumber, lastRange.newRange.startColumn, maxLine, model.getLineMaxColumn(maxLine)); + // 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 { + mergedChanges.push(newChange); } } - return ranges; + return mergedChanges; } private _onDidChangeContentQueue: LimitedQueue = new LimitedQueue(); @@ -354,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(model, 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 }); } 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 1db590850902..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'; @@ -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/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/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 33c8ce333233..d8828c069154 100644 --- a/src/vs/editor/contrib/gotoError/browser/gotoErrorWidget.ts +++ b/src/vs/editor/contrib/gotoError/browser/gotoErrorWidget.ts @@ -26,7 +26,7 @@ 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, IThemeChangeEvent, IThemeService } from '../../../../platform/theme/common/themeService.js'; 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/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/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 40411f027ef7..700bc76bf640 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/model/ghostText.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/model/ghostText.ts @@ -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 efcbda1a33d9..73bfd7977bf9 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsModel.ts @@ -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, @@ -391,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]; @@ -510,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; @@ -546,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; @@ -558,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; @@ -771,7 +782,7 @@ export class InlineCompletionsModel extends Disposable { // TODO: clean this up if we keep it private readonly _inAcceptPartialFlow = observableValue(this, false); - public readonly inAcceptPartialFlow: IObservable = this._inAcceptPartialFlow; + public readonly inPartialAcceptFlow: IObservable = this._inAcceptPartialFlow; public async acceptNextInlineEditPart(editor: ICodeEditor): Promise { if (editor.getModel() !== this.textModel) { throw new BugIndicatingError(); diff --git a/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsSource.ts b/src/vs/editor/contrib/inlineCompletions/browser/model/inlineCompletionsSource.ts index d9d9f0e09912..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 { @@ -737,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 f510bfab8538..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; @@ -370,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 { @@ -382,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 3bcb8b5f61ab..16148bd87b09 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/ghostText/ghostTextView.css +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/ghostText/ghostTextView.css @@ -33,6 +33,8 @@ 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; } 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 fb04686f119f..d592eca09ea4 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/ghostText/ghostTextView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/ghostText/ghostTextView.ts @@ -31,6 +31,7 @@ import { ColumnRange } from '../../utils.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; @@ -81,7 +82,7 @@ export class GhostTextView extends Disposable { return; } const a = e.target.detail.injectedText?.options.attachedData; - if (a instanceof GhostTextAttachedData) { + if (a instanceof GhostTextAttachedData && a.owner === this) { this._onDidClick.fire(e.event); } })); @@ -224,9 +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(), + attachedData: new GhostTextAttachedData(this), }, showIfCollapsed: true, } @@ -255,7 +259,9 @@ export class GhostTextView extends Disposable { ); private readonly _isInlineTextHovered = this._editorObs.isTargetHovered( - p => p.target.type === MouseTargetType.CONTENT_TEXT && p.target.detail.injectedText?.options.attachedData instanceof GhostTextAttachedData, + 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 ); @@ -274,7 +280,9 @@ export class GhostTextView extends Disposable { } } -class GhostTextAttachedData { } +class GhostTextAttachedData { + constructor(public readonly owner: GhostTextView) { } +} interface WidgetDomElement { ghostTextViewWarningWidgetData?: { @@ -377,6 +385,8 @@ export class AdditionalLinesWidget extends Disposable { this._store ); + private hasBeenAccepted = false; + constructor( private readonly _editor: ICodeEditor, private readonly _lines: IObservable<{ @@ -390,12 +400,17 @@ export class AdditionalLinesWidget extends Disposable { ) { 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); this.editorOptionsChanged.read(reader); if (lines) { + this.hasBeenAccepted = false; this.updateLines(lines.lineNumber, lines.additionalLines, lines.minReservedLineCount); } else { this.clear(); @@ -435,7 +450,10 @@ export class AdditionalLinesWidget extends Disposable { renderLines(domNode, tabSize, additionalLines, this._editor.getOptions(), this._isClickable); if (this._isClickable) { - store.add(addDisposableListener(domNode, 'mouseup', (e) => { + 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)); } @@ -469,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); diff --git a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineCompletionsView.ts b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineCompletionsView.ts index 04afc6a88f39..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) => { 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 6624abea5fa5..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._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() - ] : []), - 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 })) - ), - 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 e737ff7aa25f..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,17 +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 { 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 _host: IInlineEditsViewHost, + private readonly _model: IObservable, private readonly _isHoveringOverInlineEdit: IObservable, private readonly _focusIsInMenu: ISettableObservable, @IHoverService private readonly _hoverService: HoverService, @@ -40,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), @@ -47,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 }); })); } } @@ -109,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), }; }); @@ -122,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()) { @@ -131,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(); @@ -146,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; @@ -161,15 +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 docked = this._layout.map(l => l && l.docked).get(); this._editorObs.editor.focus(); if (docked) { - this._host.accept(); + this.model.accept(); } else { - this._host.jump(); + this.model.jump(); } }, tabIndex: 0, @@ -199,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', @@ -222,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 fe29ffa062be..6670696ecdc3 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsView.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsView.ts @@ -4,13 +4,13 @@ *--------------------------------------------------------------------------------------------*/ 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 { autorunWithStore, derived, derivedObservableWithCache, derivedOpts, derivedWithStore, IObservable, IReader, ISettableObservable, mapObservableArrayCached, observableValue } 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'; @@ -19,47 +19,55 @@ 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, @@ -69,8 +77,10 @@ export class InlineEditsView extends Disposable { ...this._wordReplacementViews.read(reader).map(w => w.onDidClick), this._inlineDiffView.onDidClick, )(e => { - e.preventDefault(); - this._host.accept(); + if (this._viewHasBeenShownLongerThan(350)) { + e.preventDefault(); + model.accept(); + } }) ); })); @@ -89,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()?.handleInlineEditShown(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()); @@ -128,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, }; }); @@ -150,37 +161,6 @@ 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 - ?? completionSource?.provider.displayName ?? 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; } - if (m && m.inlineCompletionState.read(reader)?.inlineCompletion?.sourceInlineCompletion.showInlineEditMenu) { return InlineEditTabAction.Accept; } - } - 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(); - }, - jump: () => { - this._model.get()?.jump(); - } - }; - - private readonly _useGutterIndicator = observableCodeEditor(this._editor).getOption(EditorOption.inlineSuggest).map(s => s.edits.useGutterIndicator); - private readonly _indicatorCyclicDependencyCircuitBreaker = observableValue(this, false); protected readonly _indicator = derivedWithStore(this, (reader, store) => { @@ -189,9 +169,9 @@ export class InlineEditsView extends Disposable { } const indicatorDisplayRange = derivedOpts({ owner: this, equalsFn: equalsIfDefined(itemEquals()) }, reader => { - const s = this._model.read(reader)?.inlineCompletionState.read(reader); - if (s && s.inlineCompletion?.sourceInlineCompletion.showInlineEditMenu) { - return LineRange.ofLength(s.primaryGhostText.lineNumber, 1); + const ghostTextIndicator = this._ghostTextIndicator.read(reader); + if (ghostTextIndicator) { + return ghostTextIndicator.lineRange; } const state = this._uiState.read(reader); @@ -201,29 +181,29 @@ export class InlineEditsView extends Disposable { return state?.originalDisplayRange; }); - if (this._useGutterIndicator.read(reader)) { - return store.add(this._instantiationService.createInstance( - InlineEditsGutterIndicator, - this._editorObs, - indicatorDisplayRange, - this._gutterIndicatorOffset, - 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, - )); - } + 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 model; + }); + + return store.add(this._instantiationService.createInstance( + InlineEditsGutterIndicator, + this._editorObs, + indicatorDisplayRange, + this._gutterIndicatorOffset, + modelWithGhostTextSupport, + this._inlineEditsIsHovered, + this._focusIsInMenu, + )); }); private readonly _inlineEditsIsHovered = derived(this, reader => { @@ -245,24 +225,24 @@ export class InlineEditsView extends Disposable { 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, @@ -272,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 => { @@ -292,7 +272,7 @@ export class InlineEditsView extends Disposable { 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)); + return store.add(this._instantiationService.createInstance(InlineEditsWordReplacementView, this._editorObs, e, [e], this._tabAction)); }); protected readonly _lineReplacementView = this._register(this._instantiationService.createInstance(InlineEditsLineReplacementView, @@ -303,32 +283,29 @@ export class InlineEditsView extends Disposable { modifiedLines: s.state.modifiedLines, replacements: s.state.replacements, }) : undefined), - this._host + this._tabAction, )); - private getCacheId(edit: InlineEditWithChanges) { - if (this._model.get()?.inAcceptPartialFlow.get()) { - return `${edit.inlineCompletion.id}_${edit.edit.edits.map(edit => edit.range.toString() + edit.text).join(',')}`; + 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 edit.inlineCompletion.id; + 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 === this.getCacheId(edit); - 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; } @@ -337,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: this.getCacheId(edit), 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 }; } @@ -408,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), }; } @@ -429,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 { @@ -444,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 ffa4cdbaffff..a00fc94d390b 100644 --- a/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViewInterface.ts +++ b/src/vs/editor/contrib/inlineCompletions/browser/view/inlineEdits/inlineEditsViewInterface.ts @@ -7,18 +7,31 @@ 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 56% 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 9b5fcbb5159b..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,7 @@ export class InlineEditsViewAndDiffProducer extends Disposable { // TODO: This c const editOffset = model.inlineEditState.get()?.inlineCompletion.updatedEdit.read(reader); if (!editOffset) { return undefined; } - const offsetEdits = model.inAcceptPartialFlow.read(reader) ? [editOffset.edits[0]] : editOffset.edits; + 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), @@ -42,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( @@ -50,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 f8c6bcf39345..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 @@ -9,22 +9,28 @@ 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, private readonly _edit: IObservable, @@ -32,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), @@ -50,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) { @@ -151,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 494210fa6c7f..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 @@ -10,7 +10,7 @@ import { constObservable, derived, derivedWithStore, IObservable, observableValu 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 { 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'; @@ -23,12 +23,12 @@ 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; @@ -86,18 +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, - true - )); + protected readonly _ghostTextView: GhostTextView; + readonly isHovered: IObservable; constructor( private readonly _editor: ICodeEditor, @@ -106,12 +96,29 @@ 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); })); @@ -210,7 +217,7 @@ export class InlineEditsInsertionView extends Disposable implements IInlineEdits 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; + const left = editorLayout.contentLeft + prefixLeftOffset - horizontalScrollOffset; if (right <= left) { return null; } @@ -222,14 +229,14 @@ export class InlineEditsInsertionView extends Disposable implements IInlineEdits const top = this._editor.getTopForLineNumber(state.lineNumber) - scrollTop + topTrim; const bottom = top + height; - const PADDING = 3; - const overlay = new Rect(left, top, right, bottom).withMargin(PADDING); + const overlay = new Rect(left, top, right, bottom); return { overlay, - horizontalScrollOffset, + contentLeft: editorLayout.contentLeft, minContentWidthRequired: prefixLeftOffset + overlay.width + verticalScrollbarWidth, borderRadius: 4, + padding: 3 }; }).recomputeInitiallyAndOnChange(this._store); @@ -241,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.overlay.getLeftTop(), - width: layoutInfo.overlay.width + 1, - height: layoutInfo.overlay.height + 1, + topLeft: croppedOverlay.getLeftTop(), + width: croppedOverlay.width + 1, + height: croppedOverlay.height + 1, }, - 0, + 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', { @@ -281,6 +290,4 @@ export class InlineEditsInsertionView extends Disposable implements IInlineEdits }, [ [this._foregroundSvg], ]).keepUpdated(this._store); - - readonly isHovered = this._ghostTextView.isHovered; } 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 d952cda8a05b..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 @@ -25,7 +25,7 @@ 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'; @@ -153,7 +153,7 @@ export class InlineEditsLineReplacementView extends Disposable implements IInlin }); 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; } @@ -197,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({ @@ -233,7 +233,10 @@ export class InlineEditsLineReplacementView extends Disposable implements IInlin cursor: 'pointer', pointerEvents: 'auto', }, - onmouseup: (e) => this._onDidClick.fire(new StandardMouseEvent(getWindow(e), e)), + onmousedown: e => { + e.preventDefault(); // This prevents that the editor loses focus + }, + onclick: (e) => this._onDidClick.fire(new StandardMouseEvent(getWindow(e), e)), }, [ n.div({ style: { @@ -284,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(); 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 12648f46114c..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 @@ -23,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'; @@ -64,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, ) { @@ -123,8 +123,11 @@ 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' }, + onmousedown: e => { + e.preventDefault(); // This prevents that the editor loses focus + }, onclick: (e) => { this._onDidClick.fire(new StandardMouseEvent(getWindow(e), e)); } @@ -505,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 d3ee68c60cc3..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 @@ -14,9 +14,9 @@ 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 { 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 6303d7955811..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 @@ -7,7 +7,7 @@ import { getWindow, n, ObserverNodeWithElement } from '../../../../../../../base 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'; @@ -22,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'; @@ -47,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(); @@ -162,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({ @@ -186,6 +186,9 @@ export class InlineEditsWordReplacementView extends Disposable implements IInlin cursor: 'pointer', pointerEvents: 'auto', }, + 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 414f455553d7..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 @@ -10,24 +10,19 @@ import { autorunWithStore, derived, IObservable, observableFromEvent } from '../ 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 } 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; } @@ -41,7 +36,9 @@ export class OriginalEditorInlineDiffView extends Disposable implements IInlineE readonly onDidClick = this._onDidClick.event; readonly isHovered = observableCodeEditor(this._originalEditor).isTargetHovered( - p => p.target.type === MouseTargetType.CONTENT_TEXT && p.target.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 ); @@ -64,68 +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) { + 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 => { @@ -133,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; @@ -263,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, @@ -280,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 9427e0b23a09..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); 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/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/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 fbaa5530d893..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; } @@ -1041,3 +1044,4 @@ export class SuggestContentWidget implements IContentWidget { this._position = position; } } + diff --git a/src/vs/editor/contrib/symbolIcons/browser/symbolIcons.css b/src/vs/editor/contrib/symbolIcons/browser/symbolIcons.css index a025a03ac2aa..475006090b4e 100644 --- a/src/vs/editor/contrib/symbolIcons/browser/symbolIcons.css +++ b/src/vs/editor/contrib/symbolIcons/browser/symbolIcons.css @@ -11,10 +11,6 @@ .monaco-workbench .codicon.codicon-symbol-class { color: var(--vscode-symbolIcon-classForeground); } .monaco-editor .codicon.codicon-symbol-method, .monaco-workbench .codicon.codicon-symbol-method { color: var(--vscode-symbolIcon-methodForeground); } -.monaco-editor .codicon.codicon-symbol-method-arrow, -.monaco-workbench .codicon.codicon-symbol-method-arrow { color: var(--vscode-symbolIcon-methodArrowForeground); } -.monaco-editor .codicon.codicon-flag, -.monaco-workbench .codicon.codicon-flag { color: var(--vscode-symbolIcon-flagForeground); } .monaco-editor .codicon.codicon-symbol-color, .monaco-workbench .codicon.codicon-symbol-color { color: var(--vscode-symbolIcon-colorForeground); } .monaco-editor .codicon.codicon-symbol-constant, diff --git a/src/vs/editor/contrib/symbolIcons/browser/symbolIcons.ts b/src/vs/editor/contrib/symbolIcons/browser/symbolIcons.ts index 0cd566196475..a2795f7541ce 100644 --- a/src/vs/editor/contrib/symbolIcons/browser/symbolIcons.ts +++ b/src/vs/editor/contrib/symbolIcons/browser/symbolIcons.ts @@ -36,9 +36,6 @@ export const SYMBOL_ICON_ENUMERATOR_FOREGROUND = registerColor('symbolIcon.enume hcLight: '#D67E00' }, localize('symbolIcon.enumeratorForeground', 'The foreground color for enumerator symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); - -export const SYMBOL_ICON_FLAG_FOREGROUND = registerColor('symbolIcon.flagForeground', SYMBOL_ICON_ENUMERATOR_FOREGROUND, localize('symbolIcon.enumeratorForeground', 'The foreground color for enumerator symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); - export const SYMBOL_ICON_ENUMERATOR_MEMBER_FOREGROUND = registerColor('symbolIcon.enumeratorMemberForeground', { dark: '#75BEFF', light: '#007ACC', @@ -89,8 +86,6 @@ export const SYMBOL_ICON_METHOD_FOREGROUND = registerColor('symbolIcon.methodFor hcLight: '#652D90' }, localize('symbolIcon.methodForeground', 'The foreground color for method symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); -export const SYMBOL_ICON_METHOD_ARROW_FOREGROUND = registerColor('symbolIcon.methodArrowForeground', SYMBOL_ICON_METHOD_FOREGROUND, localize('symbolIcon.methodArrowForeground', 'The foreground color for method with an arrow symbols. These symbols appear in the suggest widget.')); - export const SYMBOL_ICON_MODULE_FOREGROUND = registerColor('symbolIcon.moduleForeground', foreground, localize('symbolIcon.moduleForeground', 'The foreground color for module symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); export const SYMBOL_ICON_NAMESPACE_FOREGROUND = registerColor('symbolIcon.namespaceForeground', foreground, localize('symbolIcon.namespaceForeground', 'The foreground color for namespace symbols. These symbols appear in the outline, breadcrumb, and suggest widget.')); 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/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/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/actions/browser/menuEntryActionViewItem.ts b/src/vs/platform/actions/browser/menuEntryActionViewItem.ts index 8971195a1c9d..2e86c3bcdea8 100644 --- a/src/vs/platform/actions/browser/menuEntryActionViewItem.ts +++ b/src/vs/platform/actions/browser/menuEntryActionViewItem.ts @@ -167,7 +167,7 @@ function fillInActions( export interface IMenuEntryActionViewItemOptions { draggable?: boolean; - keybinding?: string; + keybinding?: string | null; hoverDelegate?: IHoverDelegate; keybindingNotRenderedWithLabel?: boolean; } @@ -462,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), 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/environment/common/argv.ts b/src/vs/platform/environment/common/argv.ts index cdcb8ee0a053..1efc1b5de457 100644 --- a/src/vs/platform/environment/common/argv.ts +++ b/src/vs/platform/environment/common/argv.ts @@ -93,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 7ac132ae5cdf..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 }, @@ -179,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' }, @@ -410,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 f431353d8a07..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,7 +559,6 @@ export type InstallOptions = { donotVerifySignature?: boolean; operation?: InstallOperation; profileLocation?: URI; - installOnlyNewlyAddedFromExtensionPack?: boolean; productVersion?: IProductVersion; keepExisting?: boolean; downloadExtensionsLocally?: boolean; 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/node/extensionManagementService.ts b/src/vs/platform/extensionManagement/node/extensionManagementService.ts index 35838459323b..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'; @@ -54,6 +54,7 @@ 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 { @@ -86,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, @@ -214,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); } @@ -328,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 && !(isLinux && this.productService.quality === 'stable')) { + 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) { @@ -352,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); } @@ -624,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) { @@ -633,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); @@ -678,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); @@ -861,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, @@ -873,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 }); } })); } @@ -1033,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 6d62272e988c..18efd7a0934d 100644 --- a/src/vs/platform/extensions/common/extensionsApiProposals.ts +++ b/src/vs/platform/extensions/common/extensionsApiProposals.ts @@ -33,14 +33,11 @@ const _allApiProposals = { }, chatParticipantPrivate: { proposal: 'https://raw.githubusercontent.com/microsoft/vscode/main/src/vscode-dts/vscode.proposed.chatParticipantPrivate.d.ts', - version: 4 + 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', }, @@ -154,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', @@ -229,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', }, @@ -334,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', }, @@ -358,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 e36ae7bcee57..33666b18f45b 100644 --- a/src/vs/platform/files/common/fileService.ts +++ b/src/vs/platform/files/common/fileService.ts @@ -504,7 +504,7 @@ export class FileService extends Disposable implements IFileService { buffer = await this.peekBufferForWriting(provider, bufferOrReadableOrStream); if (buffer instanceof VSBuffer && buffer.byteLength === stat.size) { try { - const { value } = await this.readFile(resource); + const { value } = await this.readFile(resource, { limits: { size: stat.size } }); if (buffer.equals(value)) { return { stat, buffer }; // allow writing since contents are identical } 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/hover/browser/hover.ts b/src/vs/platform/hover/browser/hover.ts index f2651c086d15..01fcf3cb7a34 100644 --- a/src/vs/platform/hover/browser/hover.ts +++ b/src/vs/platform/hover/browser/hover.ts @@ -79,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: { 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 602a67ea0d46..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; } 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 049c19d1ec57..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; diff --git a/src/vs/platform/native/electron-main/nativeHostMainService.ts b/src/vs/platform/native/electron-main/nativeHostMainService.ts index c2241a9a4524..6324e98e290f 100644 --- a/src/vs/platform/native/electron-main/nativeHostMainService.ts +++ b/src/vs/platform/native/electron-main/nativeHostMainService.ts @@ -73,6 +73,60 @@ export class NativeHostMainService extends Disposable implements INativeHostMain @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); + } } @@ -85,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 @@ -260,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(); 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/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/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/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 1b6f70a42637..17b2429ff8fa 100644 --- a/src/vs/platform/terminal/common/capabilities/capabilities.ts +++ b/src/vs/platform/terminal/common/capabilities/capabilities.ts @@ -214,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; @@ -239,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. @@ -345,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/commandDetectionCapability.ts b/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts index 852d6a46115b..7237d1270d09 100644 --- a/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts +++ b/src/vs/platform/terminal/common/capabilities/commandDetectionCapability.ts @@ -33,6 +33,8 @@ export class CommandDetectionCapability extends Disposable implements ICommandDe private _dimensions: ITerminalDimensions; private __isCommandStorageDisabled: boolean = false; private _handleCommandStartOptions?: IHandleCommandOptions; + private _hasRichCommandDetection: boolean = false; + get hasRichCommandDetection() { return this._hasRichCommandDetection; } private _ptyHeuristicsHooks: ICommandDetectionHeuristicsHooks; private readonly _ptyHeuristics: MandatoryMutableDisposable; @@ -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, @@ -223,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; } @@ -403,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(), }; @@ -412,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 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 eed96c0f8f22..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. */ @@ -321,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, @@ -363,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) { @@ -383,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(); @@ -444,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; @@ -553,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, ''); @@ -607,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(); @@ -641,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` @@ -663,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); @@ -680,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/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/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 a1ec3fed95da..755533e9f73b 100644 --- a/src/vs/platform/update/electron-main/abstractUpdateService.ts +++ b/src/vs/platform/update/electron-main/abstractUpdateService.ts @@ -62,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 @@ -80,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) { @@ -172,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(); }); @@ -199,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 a7e7bb4a44b3..fe2c6dd24539 100644 --- a/src/vs/platform/update/electron-main/updateService.darwin.ts +++ b/src/vs/platform/update/electron-main/updateService.darwin.ts @@ -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)); } @@ -130,7 +130,7 @@ export class DarwinUpdateService extends AbstractUpdateService implements IRelau } 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.snap.ts b/src/vs/platform/update/electron-main/updateService.snap.ts index 4ca2a34a7a92..d591a8ef9922 100644 --- a/src/vs/platform/update/electron-main/updateService.snap.ts +++ b/src/vs/platform/update/electron-main/updateService.snap.ts @@ -48,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)) diff --git a/src/vs/platform/update/electron-main/updateService.win32.ts b/src/vs/platform/update/electron-main/updateService.win32.ts index db92de2f1987..9272c047ab7f 100644 --- a/src/vs/platform/update/electron-main/updateService.win32.ts +++ b/src/vs/platform/update/electron-main/updateService.win32.ts @@ -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'); 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/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/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 e230d1a09a5c..ddf6d46f7db6 100644 --- a/src/vs/platform/window/common/window.ts +++ b/src/vs/platform/window/common/window.ts @@ -152,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; @@ -179,6 +180,12 @@ export const enum TitlebarStyle { CUSTOM = 'custom', } +export const enum WindowControlsStyle { + NATIVE = 'native', + CUSTOM = 'custom', + HIDDEN = 'hidden' +} + export const enum CustomTitleBarVisibility { AUTO = 'auto', WINDOWED = 'windowed', @@ -225,6 +232,20 @@ export function getTitleBarStyle(configurationService: IConfigurationService): T 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 WindowControlsStyle.NATIVE; // default to native on all OS +} + export const DEFAULT_CUSTOM_TITLEBAR_HEIGHT = 35; // includes space for command center export function useWindowControlsOverlay(configurationService: IConfigurationService): boolean { @@ -236,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 } 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 d2ca2f8f6c7a..594b83643530 100644 --- a/src/vs/platform/windows/electron-main/windowsMainService.ts +++ b/src/vs/platform/windows/electron-main/windowsMainService.ts @@ -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(); } 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/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 510f26d520ab..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'; @@ -138,8 +139,8 @@ export class MainThreadChatAgents2 extends Disposable implements MainThreadChatA const inputValue = widget?.inputEditor.getValue() ?? ''; const location = widget.location; - const toolsAgentModeEnabled = this._chatAgentService.toolsAgentModeEnabled; - this._chatService.transferChatSession({ sessionId, inputValue, location, toolsAgentModeEnabled }, URI.revive(toWorkspace)); + 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 { @@ -174,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/mainThreadLanguageFeatures.ts b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts index 8c7d32de8273..9aadd0d679ae 100644 --- a/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts +++ b/src/vs/workbench/api/browser/mainThreadLanguageFeatures.ts @@ -1039,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; } @@ -1051,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; }; @@ -1061,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; } @@ -1145,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/mainThreadTerminalShellIntegration.ts b/src/vs/workbench/api/browser/mainThreadTerminalShellIntegration.ts index 44cc83b1116e..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)); 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 78e34e821fa1..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, extHostDiagnostics)); 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); @@ -1355,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); } }; @@ -1508,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); } }; @@ -1652,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, @@ -1806,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 6654da09e906..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'; @@ -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 { @@ -2552,6 +2554,7 @@ export interface ExtHostTaskShape { $onDidStartTaskProcess(value: tasks.ITaskProcessStartedDTO): void; $onDidEndTaskProcess(value: tasks.ITaskProcessEndedDTO): void; $OnDidEndTask(execution: tasks.ITaskExecutionDTO): void; + $onDidChangeTaskTerminalStatus(status: tasks.ITaskStatusDTO): void; $resolveVariables(workspaceFolder: UriComponents, toResolve: { process?: { name: string; cwd?: string }; variables: string[] }): Promise<{ process?: string; variables: { [key: string]: string } }>; $jsonTasksSupported(): Promise; $findExecutable(command: string, cwd?: string, paths?: string[]): Promise; @@ -2965,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; @@ -3111,6 +3129,7 @@ export const MainContext = { MainThreadTimeline: createProxyIdentifier('MainThreadTimeline'), MainThreadTesting: createProxyIdentifier('MainThreadTesting'), MainThreadLocalization: createProxyIdentifier('MainThreadLocalizationShape'), + MainThreadMcp: createProxyIdentifier('MainThreadMcpShape'), MainThreadAiRelatedInformation: createProxyIdentifier('MainThreadAiRelatedInformation'), MainThreadAiEmbeddingVector: createProxyIdentifier('MainThreadAiEmbeddingVector') }; @@ -3182,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 d053ad5e5ea8..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,26 +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'; -import { ExtHostDiagnostics } from './extHostDiagnostics.js'; class ChatAgentResponseStream { @@ -327,6 +328,7 @@ export class ExtHostChatAgents2 extends Disposable implements ExtHostChatAgentsS private readonly _documents: ExtHostDocuments, private readonly _languageModels: ExtHostLanguageModels, private readonly _diagnostics: ExtHostDiagnostics, + private readonly _tools: ExtHostLanguageModelTools ) { super(); this._proxy = mainContext.getProxy(MainContext.MainThreadChatAgents2); @@ -404,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'), this.getDiagnosticsWhenEnabled(detector.extension)); + 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, @@ -488,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'), this.getDiagnosticsWhenEnabled(agent.extension)); + 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); @@ -547,6 +557,14 @@ export class ExtHostChatAgents2 extends Disposable implements ExtHostChatAgentsS 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)[] = []; @@ -557,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, this.getDiagnosticsWhenEnabled(extension))); + .map(v => typeConvert.ChatPromptReference.to(v, this.getDiagnosticsWhenEnabled(extension))); const toolReferences = h.request.variables.variables .filter(v => v.isTool) .map(typeConvert.ChatLanguageModelToolReference.to); @@ -664,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) { @@ -721,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(); @@ -769,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; @@ -835,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; }); @@ -940,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/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 4a785ef16ded..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); } } diff --git a/src/vs/workbench/api/common/extHostLanguageModelTools.ts b/src/vs/workbench/api/common/extHostLanguageModelTools.ts index 7ef25620b846..7c6ad141c6e9 100644 --- a/src/vs/workbench/api/common/extHostLanguageModelTools.ts +++ b/src/vs/workbench/api/common/extHostLanguageModelTools.ts @@ -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 { @@ -111,6 +112,7 @@ 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; 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 c2d7b9e31729..5ab8edeb955a 100644 --- a/src/vs/workbench/api/common/extHostTerminalShellIntegration.ts +++ b/src/vs/workbench/api/common/extHostTerminalShellIntegration.ts @@ -142,15 +142,23 @@ 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: vscode.TerminalShellIntegrationEnvironment | undefined; private _cwd: URI | undefined; @@ -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, @@ -177,7 +187,13 @@ class InternalTerminalShellIntegration extends Disposable { return that._cwd; }, get env(): vscode.TerminalShellIntegrationEnvironment | undefined { - return that._env; + 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,17 +307,28 @@ 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); + 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._currentExecution === currentExecution) { + if (this._pendingEndingExecution === currentExecution) { this._onDidRequestEndExecution.fire({ terminal: this._terminal, shellIntegration: this.value, execution: currentExecution.value, exitCode }); - this._currentExecution = undefined; + this._pendingEndingExecution = undefined; } }); } @@ -277,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, @@ -303,7 +387,7 @@ class InternalTerminalShellExecution { private _createDataStream(): AsyncIterable { if (!this._dataStream) { - if (this._ended) { + if (this._isEnded) { return AsyncIterableObject.EMPTY; } this._dataStream = new ShellExecutionDataStream(); @@ -312,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 { @@ -320,12 +406,15 @@ class InternalTerminalShellExecution { this._commandLine = commandLine; } this._dataStream?.endExecution(); - this._dataStream = undefined; - this._ended = true; + this._isEnded = true; } async flush(): Promise { - await this._dataStream?.flush(); + if (this._dataStream) { + await this._dataStream.flush(); + this._dataStream.dispose(); + this._dataStream = undefined; + } } } @@ -362,3 +451,39 @@ class ShellExecutionDataStream extends Disposable { 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 edd8590eedda..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, diagnostics: readonly [vscode.Uri, readonly vscode.Diagnostic[]][]): 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, diagnostics)), + 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,7 +2828,7 @@ export namespace ChatLocation { } export namespace ChatPromptReference { - export function to(variable: IChatRequestVariableEntry, hasReadonlyProposal: boolean, diagnostics: readonly [vscode.Uri, readonly vscode.Diagnostic[]][]): vscode.ChatPromptReference { + 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'); @@ -2857,7 +2871,6 @@ export namespace ChatPromptReference { range: variable.range && [variable.range.start, variable.range.endExclusive], 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 6e2325be5d2b..9d28387e9cc9 100644 --- a/src/vs/workbench/api/common/extHostTypes.ts +++ b/src/vs/workbench/api/common/extHostTypes.ts @@ -2216,6 +2216,48 @@ export enum TaskRevealKind { Never = 3 } +export enum TaskEventKind { + /** Indicates a task's properties or configuration have changed */ + Changed = 'changed', + + /** Indicates a task has begun executing */ + ProcessStarted = 'processStarted', + + /** Indicates a task process has completed */ + ProcessEnded = 'processEnded', + + /** Indicates a task was terminated, either by user action or by the system */ + Terminated = 'terminated', + + /** Indicates a task has started running */ + Start = 'start', + + /** Indicates a task has acquired all needed input/variables to execute */ + AcquiredInput = 'acquiredInput', + + /** Indicates a dependent task has started */ + DependsOnStarted = 'dependsOnStarted', + + /** Indicates a task is actively running/processing */ + Active = 'active', + + /** Indicates a task is paused/waiting but not complete */ + Inactive = 'inactive', + + /** Indicates a task has completed fully */ + End = 'end', + + /** Indicates the task's problem matcher has started */ + ProblemMatcherStarted = 'problemMatcherStarted', + + /** Indicates the task's problem matcher has ended */ + ProblemMatcherEnded = 'problemMatcherEnded', + + /** Indicates the task's problem matcher has found errors */ + ProblemMatcherFoundErrors = 'problemMatcherFoundErrors' +} + + export enum TaskPanelKind { Shared = 1, @@ -2902,9 +2944,9 @@ export class DataTransferFile implements vscode.DataTransferFile { @es5ClassCompat export class DataTransfer implements vscode.DataTransfer { - #items = new Map(); + #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) { @@ -2915,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); @@ -4750,6 +4792,12 @@ export class PreparedTerminalToolInvocation { ) { } } +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 { @@ -4991,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/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/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/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/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 978c3f94059d..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; @@ -841,13 +838,11 @@ export class MultiEditorTabsControl extends EditorTabsControl { // 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/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 389569cf5a7c..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 .items-container > .statusbar-item.copilot-kind { - color: var(--vscode-statusBarItem-copilotForeground); - background-color: var(--vscode-statusBarItem-copilotBackground); -} - -.monaco-workbench .part.statusbar > .items-container > .statusbar-item.copilot-kind a:hover:not(.disabled) { - color: var(--vscode-statusBarItem-copilotHoverForeground); - background-color: var(--vscode-statusBarItem-copilotHoverBackground) !important; -} diff --git a/src/vs/workbench/browser/parts/statusbar/statusbarItem.ts b/src/vs/workbench/browser/parts/statusbar/statusbarItem.ts index f7375016f1a0..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); @@ -133,7 +132,7 @@ export class StatusbarEntryItem extends Disposable { const hoverContents = isMarkdownString(hoverTooltip) ? { markdown: hoverTooltip, markdownNotSupportedFallback: undefined } : hoverTooltip; if (this.hover) { - this.hover.update(typeof hoverContents === 'function' ? hoverContents() : hoverContents, hoverOptions); + this.hover.update(hoverContents, hoverOptions); } else { this.hover = this._register(this.hoverService.setupManagedHover(this.hoverDelegate, this.container, hoverContents, hoverOptions)); } diff --git a/src/vs/workbench/browser/parts/statusbar/statusbarModel.ts b/src/vs/workbench/browser/parts/statusbar/statusbarModel.ts index 4f4a138dbfb7..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) { @@ -353,6 +356,11 @@ 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(...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 2939ef90e130..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, isHTMLElement } 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,7 +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 { isManagedHoverTooltipMarkdownString } from '../../../../base/browser/ui/hover/hover.js'; +import { isManagedHoverTooltipHTMLElement, isManagedHoverTooltipMarkdownString } from '../../../../base/browser/ui/hover/hover.js'; export interface IStatusbarEntryContainer extends IDisposable { @@ -133,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; @@ -146,30 +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', { - instantHover: true, - dynamicDelay(content) { - if ( - typeof content === 'function' || - isHTMLElement(content) || - (isManagedHoverTooltipMarkdownString(content) && typeof content.markdown === 'function') - ) { - // 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 - } - } - ))); + private readonly hoverDelegate: WorkbenchHoverDelegate; private readonly compactEntriesDisposable = this._register(new MutableDisposable()); private readonly styleOverrides = new Set(); @@ -179,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(); } @@ -323,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); } @@ -404,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 @@ -501,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 @@ -751,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; @@ -763,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 @@ -771,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 @@ -801,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) { @@ -905,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 { @@ -919,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 718037ce3e67..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'); } } } diff --git a/src/vs/workbench/browser/parts/titlebar/windowTitle.ts b/src/vs/workbench/browser/parts/titlebar/windowTitle.ts index aa7cb6da5c35..9d901f2ffe88 100644 --- a/src/vs/workbench/browser/parts/titlebar/windowTitle.ts +++ b/src/vs/workbench/browser/parts/titlebar/windowTitle.ts @@ -130,12 +130,7 @@ export class WindowTitle extends Disposable { this.titleUpdater.schedule(); } })); - this._register(this.accessibilityService.onDidChangeScreenReaderOptimized(() => { - if (this.accessibilityService.isScreenReaderOptimized() && !this.titleIncludesEditorState - || !this.accessibilityService.isScreenReaderOptimized() && this.titleIncludesEditorState) { - this.titleUpdater.schedule(); - } - })); + this._register(this.accessibilityService.onDidChangeScreenReaderOptimized(() => this.titleUpdater.schedule())); } private onConfigurationChanged(event: IConfigurationChangeEvent): void { @@ -413,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 9a152cef023d..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'; 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/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 6c725fcfcba5..52e7ba54e435 100644 --- a/src/vs/workbench/common/theme.ts +++ b/src/vs/workbench/common/theme.ts @@ -599,17 +599,6 @@ export const EXTENSION_BADGE_REMOTE_BACKGROUND = registerColor('extensionBadge.r export const EXTENSION_BADGE_REMOTE_FOREGROUND = registerColor('extensionBadge.remoteForeground', ACTIVITY_BAR_BADGE_FOREGROUND, localize('extensionBadge.remoteForeground', "Foreground color for the remote badge in the extensions view.")); -// < --- Copilot --- > - -export const STATUS_BAR_COPILOT_ITEM_BACKGROUND = registerColor('statusBarItem.copilotBackground', STATUS_BAR_REMOTE_ITEM_BACKGROUND, localize('statusBarItemCopilotBackground', "Background color for the Copilot indicator on the status bar.")); - -export const STATUS_BAR_COPILOT_ITEM_FOREGROUND = registerColor('statusBarItem.copilotForeground', STATUS_BAR_REMOTE_ITEM_FOREGROUND, localize('statusBarItemCopilotForeground', "Foreground color for the Copilot indicator on the status bar.")); - -export const STATUS_BAR_COPILOT_ITEM_HOVER_FOREGROUND = registerColor('statusBarItem.copilotHoverForeground', STATUS_BAR_REMOTE_ITEM_HOVER_FOREGROUND, localize('statusBarCopilotItemHoverForeground', "Foreground color for the Copilot indicator on the status bar when hovering.")); - -export const STATUS_BAR_COPILOT_ITEM_HOVER_BACKGROUND = registerColor('statusBarItem.copilotHoverBackground', STATUS_BAR_REMOTE_ITEM_HOVER_BACKGROUND, localize('statusBarAiItemHoverBackground', "Background color for the Copilot indicator on the status bar when hovering.")); - - // < --- Side Bar --- > export const SIDE_BAR_BACKGROUND = registerColor('sideBar.background', { @@ -812,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/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/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 c6c90a3691a9..e416d5d651f7 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatActions.ts @@ -8,21 +8,25 @@ import { coalesce } from '../../../../../base/common/arrays.js'; import { Codicon } from '../../../../../base/common/codicons.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,42 +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, EditsViewId, IChatWidget, IChatWidgetService, showChatView, showCopilotView } from '../chat.js'; import { IChatEditorOptions } from '../chatEditor.js'; import { ChatEditorInput } from '../chatEditorInput.js'; -import { IChatQuotasService } from '../../common/chatQuotasService.js'; import { ChatViewPane } from '../chatViewPane.js'; import { convertBufferToScreenshotVariable } from '../contrib/screenshot.js'; import { clearChatEditor } from './chatClear.js'; -import { IDialogService } from '../../../../../platform/dialogs/common/dialogs.js'; -import { language } from '../../../../../base/common/platform.js'; -import { MarkdownString } from '../../../../../base/common/htmlContent.js'; -import { IWorkbenchLayoutService, Parts } from '../../../../services/layout/browser/layoutService.js'; -import { IViewDescriptorService, ViewContainerLocation } from '../../../../common/views.js'; export const CHAT_CATEGORY = localize2('chat.category', 'Chat'); export const CHAT_OPEN_ACTION_ID = 'workbench.action.chat.open'; -export const CHAT_OPEN_ACTION_LABEL = localize2('openChat', "Open Chat"); - export const CHAT_SETUP_ACTION_ID = 'workbench.action.chat.triggerSetup'; -export const CHAT_SETUP_ACTION_LABEL = localize2('triggerChatSetup', "Use AI Features with Copilot for Free..."); - -export const TOGGLE_CHAT_ACTION_ID = 'workbench.action.chat.toggle'; -export const TOGGLE_CHAT_ACTION_LABEL = localize('toggleChat', "Toggle Chat"); +const TOGGLE_CHAT_ACTION_ID = 'workbench.action.chat.toggle'; export interface IChatViewOpenOptions { /** @@ -96,14 +93,6 @@ export interface IChatViewOpenRequestEntry { response: string; } -MenuRegistry.appendMenuItem(MenuId.ViewTitle, { - command: { - id: 'update.showCurrentReleaseNotes', - title: localize2('chat.releaseNotes.label', "Show Release Notes"), - }, - when: ContextKeyExpr.equals('view', ChatViewId) -}); - export const OPEN_CHAT_QUOTA_EXCEEDED_DIALOG = 'workbench.action.chat.openQuotaExceededDialog'; export function registerChatActions() { @@ -112,10 +101,11 @@ export function registerChatActions() { constructor() { super({ id: CHAT_OPEN_ACTION_ID, - title: CHAT_OPEN_ACTION_LABEL, + 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, @@ -366,7 +356,7 @@ export function registerChatActions() { category: CHAT_CATEGORY, menu: { id: MenuId.ChatInput, - when: ChatContextKeys.location.isEqualTo(ChatAgentLocation.Panel), + when: ChatContextKeys.chatMode.isEqualTo(ChatMode.Chat), group: 'navigation', order: 1 } @@ -514,34 +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); + override async run(accessor: ServicesAccessor): Promise { + const openerService = accessor.get(IOpenerService); + openerService.open(URI.parse(defaultChat.manageSettingsUrl)); + } + }); registerAction2(class ShowExtensionsUsingCopilot extends Action2 { @@ -551,6 +542,7 @@ export function registerChatActions() { title: localize2('showCopilotUsageExtensions', "Show Extensions using Copilot"), f1: true, category: EXTENSIONS_CATEGORY, + precondition: ChatContextKeys.enabled }); } @@ -566,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', @@ -577,7 +569,7 @@ export function registerChatActions() { override async run(accessor: ServicesAccessor): Promise { const commandService = accessor.get(ICommandService); - commandService.executeCommand('github.copilot.toggleStatusMenu'); + commandService.executeCommand(defaultChat.completionsMenuCommand); } }); @@ -591,20 +583,20 @@ export function registerChatActions() { } override async run(accessor: ServicesAccessor) { - const chatQuotasService = accessor.get(IChatQuotasService); + 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 } = chatQuotasService.quotas; + 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(chatQuotasService.quotas.quotaResetDate)); + 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(chatQuotasService.quotas.quotaResetDate)); + 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(chatQuotasService.quotas.quotaResetDate)); + 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"); @@ -619,7 +611,7 @@ export function registerChatActions() { buttons: [ { label: localize('upgradePro', "Upgrade to Copilot Pro"), - run: () => commandService.executeCommand('workbench.action.chat.upgradePlan') + run: () => commandService.executeCommand('workbench.action.chat.upgradePlan', 'chat-dialog') }, ], custom: { @@ -650,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 @@ -697,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; @@ -716,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) { + if (!chatExtensionInstalled && !setupFromDialog) { primaryActionId = CHAT_SETUP_ACTION_ID; - primaryActionTitle = CHAT_SETUP_ACTION_LABEL.value; + primaryActionTitle = localize('triggerChatSetup', "Use AI Features with Copilot for Free..."); primaryActionIcon = Codicon.copilot; - } else { - if (signedOut) { - primaryActionId = TOGGLE_CHAT_ACTION_ID; - primaryActionTitle = localize('signInToChatSetup', "Sign in to Use Copilot..."); - primaryActionIcon = Codicon.copilotNotConnected; - } 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 = TOGGLE_CHAT_ACTION_ID; - primaryActionTitle = TOGGLE_CHAT_ACTION_LABEL; - 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, @@ -748,9 +744,10 @@ export class CopilotTitleBarMenuRendering extends Disposable implements IWorkben icon: primaryActionIcon, }, undefined, undefined, undefined, undefined), dropdownAction, action.actions, '', { ...options, skipTelemetry: true }); }, Event.any( - agentService.onDidChangeAgents, - chatQuotasService.onDidChangeQuotaExceeded, - 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 @@ -758,12 +755,7 @@ export class CopilotTitleBarMenuRendering extends Disposable implements IWorkben } } -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."); - } +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/chatAttachPromptAction.ts b/src/vs/workbench/contrib/chat/browser/actions/chatAttachPromptAction/chatAttachPromptAction.ts index 516f4d699cd8..a5c08661755e 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatAttachPromptAction/chatAttachPromptAction.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatAttachPromptAction/chatAttachPromptAction.ts @@ -5,7 +5,6 @@ import { CHAT_CATEGORY } from '../chatActions.js'; import { localize2 } from '../../../../../../nls.js'; -import { showHowToCreateLink } from './dialogs/showHowToCreate.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'; @@ -14,6 +13,7 @@ import { IViewsService } from '../../../../../services/views/common/viewsService 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. @@ -24,7 +24,7 @@ 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' + ISelectPromptOptions, 'resource' | 'widget' | 'viewsService' > { } /** @@ -36,6 +36,7 @@ export class AttachPromptAction extends Action2 { id: ATTACH_PROMPT_ACTION_ID, title: localize2('workbench.action.chat.attach.prompt.label', "Use Prompt"), f1: false, + precondition: ChatContextKeys.enabled, category: CHAT_CATEGORY, }); } @@ -53,18 +54,13 @@ export class AttachPromptAction extends Action2 { // find all prompt files in the user workspace const promptFiles = await promptsService.listPromptFiles(); - // if no prompt files found, show instructions on how to create one - if (promptFiles.length === 0) { - return await showHowToCreateLink(openerService, quickInputService); - } - - await askToSelectPrompt( - { - ...options, - promptFiles, - labelService, - viewsService, - quickInputService, - }); + 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 index ff7fd235d96f..79d7ceccd250 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatAttachPromptAction/dialogs/askToSelectPrompt.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatAttachPromptAction/dialogs/askToSelectPrompt.ts @@ -11,8 +11,10 @@ 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'; @@ -45,9 +47,24 @@ export interface ISelectPromptOptions { 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). * @@ -59,16 +76,15 @@ export const askToSelectPrompt = async ( ): Promise => { const { promptFiles, resource, quickInputService, labelService } = options; - // a sanity check - this function must be used only if there are prompt files to show - assert( - promptFiles.length > 0, - 'Prompt files list must not be empty.', - ); - - const fileOptions = promptFiles.map(({ uri }) => { - return createPickItem(uri, labelService); + 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; @@ -82,7 +98,12 @@ export const askToSelectPrompt = async ( // 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(resource, labelService); + 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); } @@ -99,8 +120,21 @@ export const askToSelectPrompt = async ( }); } + /** + * 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 { viewsService } = options; + const { openerService } = options; const quickPick = quickInputService.createQuickPick>(); quickPick.activeItems = activeItem ? [activeItem] : []; @@ -124,22 +158,29 @@ export const askToSelectPrompt = async ( }); disposables.add(quickPick.onDidAccept(async (event) => { - lastActiveWidget = await getChatWidgetObject( - options, - quickPick.keyMods.alt, - viewsService, + 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}'.`, ); - for (const selectedItem of quickPick.selectedItems) { - lastActiveWidget - .attachmentModel - .promptInstructions - .add(selectedItem.value); + // 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) { - return disposables.dispose(); + disposables.dispose(); } })); @@ -155,16 +196,30 @@ export const askToSelectPrompt = async ( * Creates a quick pick item for a prompt. */ const createPickItem = ( - uri: URI, + 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: labelService.getUriLabel(dirname(uri), { relative: true }), - tooltip: uri.fsPath, + description, + tooltip, value: uri, id: uri.toString(), }; @@ -181,33 +236,80 @@ const createPlaceholderText = (options: ISelectPromptOptions): string => { 'Select a prompt to use', ); - // if no widget reference is provided, add the note about - // the `alt`/`option` key modifier users can 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 key = (isWindows || isLinux) ? 'alt' : 'option'; + const altOptionkey = (isWindows || isLinux) ? 'Alt' : 'Option'; - text += ' ' + localize( + const altOptionModifierNote = localize( 'commands.prompts.use.select-dialog.alt-modifier-note', - '(hold `{0}` to use in Edits)', - key, + '{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, - viewsService: IViewsService, ): Promise => { - const { widget } = options; + 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` diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatAttachPromptAction/dialogs/showHowToCreate.ts b/src/vs/workbench/contrib/chat/browser/actions/chatAttachPromptAction/dialogs/showHowToCreate.ts deleted file mode 100644 index 73a55bc5347a..000000000000 --- a/src/vs/workbench/contrib/chat/browser/actions/chatAttachPromptAction/dialogs/showHowToCreate.ts +++ /dev/null @@ -1,47 +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 { localize } from '../../../../../../../nls.js'; -import { URI } from '../../../../../../../base/common/uri.js'; -import { WithUriValue } from '../../../../../../../base/common/types.js'; -import { DOCUMENTATION_URL } from '../../../../common/promptSyntax/constants.js'; -import { IOpenerService } from '../../../../../../../platform/opener/common/opener.js'; -import { IQuickInputService, IQuickPickItem } from '../../../../../../../platform/quickinput/common/quickInput.js'; - -/** - * Shows the dialogs with a link to docs. - */ - -export const showHowToCreateLink = async ( - openerService: IOpenerService, - quickInputService: IQuickInputService -): Promise => { - const docsQuickPick: WithUriValue = { - type: 'item', - label: localize( - 'commands.prompts.use.select-dialog.empty.docs-label', - '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( - 'commands.prompts.use.select-dialog.empty.placeholder', - 'No prompts found.' - ), - canPickMany: false, - }); - - if (!result) { - return; - } - - await openerService.open(result.value); -}; 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 aa90fc63047d..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'; diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatContextActions.ts index 071c06552f4d..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,12 +48,13 @@ 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, 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'; @@ -59,14 +62,17 @@ import { isQuickChat } from '../chatWidget.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/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 | IDiagnosticsQuickPickItem; + IScreenShotQuickPickItem | IRelatedFilesQuickPickItem | IReusablePromptQuickPickItem | IFolderQuickPickItem | IDiagnosticsQuickPickItem; /** * These are the types that we can get out of the quick pick @@ -144,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 { @@ -230,23 +236,28 @@ interface IDiagnosticsQuickPickItemWithFilter extends IQuickPickItem { } /** - * 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]]; @@ -272,7 +283,7 @@ abstract class AttachFileAction extends Action2 { } } -class AttachFileToChatAction extends AttachFileAction { +class AttachFileToChatAction extends AttachResourceAction { static readonly ID = 'workbench.action.chat.attachFile'; @@ -282,18 +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.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(); @@ -304,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'; @@ -314,12 +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.SearchContext, - group: 'z_chat', - order: 2 - }] }); } @@ -365,7 +396,7 @@ class AttachSelectionToChatAction extends Action2 { } } -class AttachFileToEditingSessionAction extends AttachFileAction { +class AttachFileToEditingSessionAction extends AttachResourceAction { static readonly ID = 'workbench.action.edits.attachFile'; @@ -375,18 +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.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(); @@ -397,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'; @@ -407,7 +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)) + precondition: ContextKeyExpr.and( + ChatContextKeys.enabled, + ContextKeyExpr.or(ActiveEditorContext.isEqualTo(TEXT_FILE_EDITOR_ID), TextCompareEditorActiveContext), + ChatContextKeyExprs.unifiedChatEnabled.negate() + ) }); } @@ -432,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: ChatContextKeys.location.isEqualTo(ChatAgentLocation.Panel), - 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); @@ -507,6 +562,7 @@ export class AttachContextAction extends Action2 { name: pick.label, value: pick.filter, kind: 'diagnostic', + icon: pick.icon, ...pick.filter, }); } else if (isIQuickPickItemWithResource(pick) && pick.resource) { @@ -599,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(); @@ -607,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 @@ -673,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'))) { @@ -784,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', @@ -810,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', + id: REUSABLE_PROMPT_PICK_ID, + kind: REUSABLE_PROMPT_PICK_ID, label: localize('chatContext.attach.prompt.label', 'Prompt...'), iconClass: ThemeIcon.asClassName(Codicon.bookmark), + keybinding, }); } @@ -841,28 +907,32 @@ export class AttachContextAction extends Action2 { }), clipboardService, editorService, labelService, viewsService, chatEditingService, hostService, fileService, textModelService, instantiationService, '', context?.placeholder); } - private async _showDiagnosticsPick(instantiationService: IInstantiationService): Promise { - const filter = await instantiationService.invokeFunction(accessor => createMarkersQuickPick(accessor)); - if (!filter) { - return undefined; - } - - return { + private async _showDiagnosticsPick(instantiationService: IInstantiationService, onBackgroundAccept: (item: IChatContextQuickPickItem[]) => void): Promise { + const convert = (item: IDiagnosticVariableEntryFilterData): IDiagnosticsQuickPickItemWithFilter => ({ kind: 'diagnostic-filter', - id: IDiagnosticVariableEntryFilterData.id(filter), - label: IDiagnosticVariableEntryFilterData.label(filter), - 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 (inputItem: IChatContextQuickPickItem, isBackgroundAccept: boolean) => { let item: IChatContextQuickPickItem | undefined = inputItem; if ('kind' in item && item.kind === 'folder') { item = await this._showFolders(instantiationService); } else if ('kind' in item && item.kind === 'diagnostic') { - item = await this._showDiagnosticsPick(instantiationService); + item = await this._showDiagnosticsPick(instantiationService, i => attach(true, ...i)); } if (!item) { @@ -876,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(); } @@ -951,16 +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"), - shortTitle: localize2('workbench.action.chat.editing.attachFiles.shortLabel', "Add Files..."), + 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, - menu: { id: MenuId.ChatInputAttachmentToolbar, group: 'navigation' }, + menu: { + when: ChatContextKeyExprs.inEditsOrUnified, + id: MenuId.ChatInputAttachmentToolbar, + group: 'navigation', + order: 3 + }, icon: Codicon.attach, - precondition: ChatContextKeys.location.isEqualTo(ChatAgentLocation.EditingSession), + 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 } @@ -974,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 4118e2156acd..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({ @@ -549,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/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 c4595b8c4ed3..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,7 +495,7 @@ 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, '', { diff --git a/src/vs/workbench/contrib/chat/browser/actions/chatToolActions.ts b/src/vs/workbench/contrib/chat/browser/actions/chatToolActions.ts index 1f059820435b..5d71ac109d1e 100644 --- a/src/vs/workbench/contrib/chat/browser/actions/chatToolActions.ts +++ b/src/vs/workbench/contrib/chat/browser/actions/chatToolActions.ts @@ -3,15 +3,26 @@ * 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 { localize2 } from '../../../../../nls.js'; -import { Action2, registerAction2 } from '../../../../../platform/actions/common/actions.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 { IChatWidgetService } from '../chat.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'; @@ -24,9 +35,10 @@ class AcceptToolConfirmation extends Action2 { f1: false, category: CHAT_CATEGORY, keybinding: { - when: ChatContextKeys.inChatSession, + when: ContextKeyExpr.and(ChatContextKeys.inChatSession, ChatContextKeys.Editing.hasToolConfirmation), primary: KeyMod.CtrlCmd | KeyCode.Enter, - weight: KeybindingWeight.EditorContrib + // Override chatEditor.action.accept + weight: KeybindingWeight.WorkbenchContrib + 1, }, }); } @@ -49,6 +61,227 @@ class AcceptToolConfirmation extends Action2 { } } +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/promptAttachments/promptAttachmentWidget.ts b/src/vs/workbench/contrib/chat/browser/attachments/promptAttachments/promptAttachmentWidget.ts index 8c1068988f56..ac4fcefcd206 100644 --- a/src/vs/workbench/contrib/chat/browser/attachments/promptAttachments/promptAttachmentWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/attachments/promptAttachments/promptAttachmentWidget.ts @@ -22,8 +22,8 @@ import { ILanguageService } from '../../../../../../editor/common/languages/lang import { FileKind, IFileService } from '../../../../../../platform/files/common/files.js'; import { IMenuService, MenuId } from '../../../../../../platform/actions/common/actions.js'; import { getCleanPromptName } from '../../../../../../platform/prompts/common/constants.js'; -import { ChatPromptAttachmentModel } from '../../chatAttachmentModel/chatPromptAttachmentModel.js'; import { IContextKeyService } from '../../../../../../platform/contextkey/common/contextkey.js'; +import { ChatPromptAttachmentModel } from '../../chatAttachmentModel/chatPromptAttachmentModel.js'; import { IContextMenuService } from '../../../../../../platform/contextview/browser/contextView.js'; import { getDefaultHoverDelegate } from '../../../../../../base/browser/ui/hover/hoverDelegateFactory.js'; import { getFlatContextMenuActions } from '../../../../../../platform/actions/browser/menuEntryActionViewItem.js'; @@ -118,18 +118,18 @@ export class PromptAttachmentWidget 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 PromptAttachmentWidget 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/chat.contribution.ts b/src/vs/workbench/contrib/chat/browser/chat.contribution.ts index ffe914d85ea4..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,18 +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, IChatQuotasService } from '../common/chatQuotasService.js'; import { ChatResponseAccessibleView } from './chatResponseAccessibleView.js'; -import { ChatEntitlementsService, ChatSetupContribution } from './chatSetup.js'; -import { IChatEntitlementsService } from '../common/chatEntitlementsService.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'; @@ -91,13 +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'; -import { registerChatToolActions } from './actions/chatToolActions.js'; -import { ChatStatusBarEntry } from './chatStatus.js'; // Register configuration const configurationRegistry = Registry.as(ConfigurationExtensions.Configuration); @@ -191,27 +199,76 @@ configurationRegistry.registerConfiguration({ description: nls.localize('chat.renderRelatedFiles', "Controls whether related files should be rendered in the chat input."), default: false }, - [PromptsConfig.CONFIG_KEY]: { + '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, @@ -274,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; } @@ -328,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()); @@ -450,6 +541,7 @@ registerWorkbenchContribution2(ChatAgentSettingContribution.ID, ChatAgentSetting 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(); @@ -488,7 +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(IChatEntitlementsService, ChatEntitlementsService, 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 250b763f4518..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'; @@ -155,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'; @@ -191,6 +191,7 @@ export interface IChatWidgetViewOptions { 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 30b1bd350d8d..24317550c93a 100644 --- a/src/vs/workbench/contrib/chat/browser/chatAttachmentModel.ts +++ b/src/vs/workbench/contrib/chat/browser/chatAttachmentModel.ts @@ -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/chatPromptAttachmentsCollection.ts b/src/vs/workbench/contrib/chat/browser/chatAttachmentModel/chatPromptAttachmentsCollection.ts index 047a6a80fc06..a659cb8080b1 100644 --- a/src/vs/workbench/contrib/chat/browser/chatAttachmentModel/chatPromptAttachmentsCollection.ts +++ b/src/vs/workbench/contrib/chat/browser/chatAttachmentModel/chatPromptAttachmentsCollection.ts @@ -55,7 +55,6 @@ export const toChatVariable = ( isSelection: false, enabled: true, isFile: true, - isMarkedReadonly: isPromptSnippet, }; }; 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 61204fc1867e..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'); 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 761e83ff51c9..f95fd944049a 100644 --- a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatConfirmationWidget.ts +++ b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatConfirmationWidget.ts @@ -12,6 +12,8 @@ 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; @@ -41,23 +43,49 @@ abstract class BaseChatConfirmationWidget extends Disposable { constructor( title: string, + rawInput: object | undefined, buttons: IChatConfirmationButton[], @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; this.markdownRenderer = this.instantiationService.createInstance(MarkdownRenderer, {}); - const renderedTitle = this._register(this.markdownRenderer.render(new MarkdownString(title), { + const renderedTitle = this._register(this.markdownRenderer.render(new MarkdownString(title, { supportThemeIcons: true }), { asyncRenderCallback: () => this._onDidChangeHeight.fire(), })); elements.title.append(renderedTitle.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 = this._register(new Button(elements.buttonsContainer, { ...defaultButtonStyles, secondary: buttonData.isSecondary, title: buttonData.tooltip })); @@ -75,10 +103,11 @@ export class ChatConfirmationWidget extends BaseChatConfirmationWidget { constructor( title: string, private readonly message: string | IMarkdownString, + rawInput: object | undefined, buttons: IChatConfirmationButton[], @IInstantiationService instantiationService: IInstantiationService, ) { - super(title, buttons, instantiationService); + super(title, rawInput, buttons, instantiationService); const renderedMessage = this._register(this.markdownRenderer.render( typeof this.message === 'string' ? new MarkdownString(this.message) : this.message, @@ -92,10 +121,11 @@ export class ChatCustomConfirmationWidget extends BaseChatConfirmationWidget { constructor( title: string, messageElement: HTMLElement, + rawInput: object | undefined, buttons: IChatConfirmationButton[], @IInstantiationService instantiationService: IInstantiationService, ) { - super(title, buttons, instantiationService); + super(title, rawInput, buttons, instantiationService); this.renderMessage(messageElement); } } diff --git a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatMarkdownContentPart.ts b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatMarkdownContentPart.ts index d51c29af5b73..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'; @@ -294,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; @@ -315,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'); @@ -375,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')); @@ -387,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); } }; @@ -416,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/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 { toolInvocation.confirmed.complete(button.data); })); 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; diff --git a/src/vs/workbench/contrib/chat/browser/chatContentParts/chatWarningContentPart.ts b/src/vs/workbench/contrib/chat/browser/chatContentParts/chatWarningContentPart.ts index 73ad31e50cf1..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,15 +29,15 @@ 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; 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 da5d68df4447..0de0ecc7d072 100644 --- a/src/vs/workbench/contrib/chat/browser/chatDragAndDrop.ts +++ b/src/vs/workbench/contrib/chat/browser/chatDragAndDrop.ts @@ -10,8 +10,7 @@ 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 { Mutable } from '../../../../base/common/types.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'; @@ -19,7 +18,7 @@ import { ITextModelService } from '../../../../editor/common/services/resolverSe import { localize } from '../../../../nls.js'; import { IDialogService } from '../../../../platform/dialogs/common/dialogs.js'; import { CodeDataTransfers, containsDragType, DocumentSymbolTransferData, extractEditorsDropData, extractMarkerDropData, extractSymbolDropData, IDraggedResourceEditorInput, MarkerTransferData } from '../../../../platform/dnd/browser/dnd.js'; -import { FileType, IFileService, IFileSystemProvider } from '../../../../platform/files/common/files.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'; @@ -30,8 +29,7 @@ import { UntitledTextEditorInput } from '../../../services/untitled/common/untit import { IChatRequestVariableEntry, IDiagnosticVariableEntry, IDiagnosticVariableEntryFilterData, ISymbolVariableEntry } from '../common/chatModel.js'; import { ChatAttachmentModel } from './chatAttachmentModel.js'; import { IChatInputStyles } from './chatInputPart.js'; -import { imageToHash } from './chatPasteProviders.js'; -import { convertStringToUInt8Array, resizeImage } from './imageUtils.js'; +import { resizeImage } from './imageUtils.js'; enum ChatDragAndDropType { FILE_INTERNAL, @@ -50,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); @@ -152,10 +150,6 @@ export class ChatDragAndDrop extends Themable { return; } - this.handleDrop(contexts); - } - - protected handleDrop(contexts: IChatRequestVariableEntry[]): void { this.attachmentModel.addContext(...contexts); } @@ -172,8 +166,8 @@ 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, 'text/html')) { + // return ChatDragAndDropType.HTML; } else if (containsDragType(e, CodeDataTransfers.SYMBOLS)) { return ChatDragAndDropType.SYMBOL; } else if (containsDragType(e, CodeDataTransfers.MARKERS)) { @@ -182,7 +176,7 @@ export class ChatDragAndDrop extends Themable { 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; } @@ -195,7 +189,7 @@ 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'); @@ -245,10 +239,11 @@ export class ChatDragAndDrop extends Themable { return this.resolveSymbolsAttachContext(data); } - if (containsDragType(e, 'text/html')) { - const data = e.dataTransfer?.getData('text/html'); - return data ? this.resolveHTMLAttachContext(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 => { @@ -325,78 +320,66 @@ 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 [{ - id: url.toString(), - name: finalDisplayName, - value: buffer, - isImage, - isFile: false, - isDirectory: false, - }]; - } else { - return [{ - kind: 'link', - id: url.toString(), - name: finalDisplayName, - value: URI.parse(url.toString()), - isFile: false, - isDirectory: false, - }]; - } - } - return []; - } + // 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 => { - const filter: Mutable = {}; + let filter: IDiagnosticVariableEntryFilterData; if (!('severity' in marker)) { - filter.filterUri = URI.revive(marker.uri); - filter.filterSeverity = MarkerSeverity.Warning; + filter = { filterUri: URI.revive(marker.uri), filterSeverity: MarkerSeverity.Warning }; } else { - filter.filterUri = URI.revive(marker.resource); - filter.filterSeverity = marker.severity; - filter.filterRange = { - startLineNumber: marker.startLineNumber, - startColumn: marker.startColumn, - endLineNumber: marker.endLineNumber, - endColumn: marker.endColumn - }; + filter = IDiagnosticVariableEntryFilterData.fromMarker(marker); } - return { - kind: 'diagnostic', - id: IDiagnosticVariableEntryFilterData.id(filter), - name: IDiagnosticVariableEntryFilterData.label(filter), - value: filter, - ...filter, - }; + return IDiagnosticVariableEntryFilterData.toEntry(filter); }); } @@ -425,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); } @@ -441,94 +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; - } - - if (/\.(svg)$/i.test(resource.path)) { - isOmitted = true; - } - return { value: resource, id: resource.toString(), @@ -578,16 +489,16 @@ 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=["']([^"']+)["']/; +// 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 }; - } +// 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 }; -} +// 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 622c070ce969..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); + } - if (!editingSession) { - return; + abstract runEditingSessionAction(accessor: ServicesAccessor, editingSession: IChatEditingSession, chatWidget: IChatWidget, ...args: any[]): any; +} + +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) } ] }); @@ -653,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 e96a4d4aa2be..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; @@ -221,16 +225,7 @@ export class ChatEditingCodeEditorIntegration implements IModifiedFileEntryEdito this._store.add(toDisposable(restoreActualOptions)); 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; - } - } - return false; + return enabledObs.read(r) && Boolean(_entry.isCurrentlyBeingModifiedBy.read(r)); }); this._store.add(autorun(r => { @@ -239,13 +234,11 @@ 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 { diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingEditorActions.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingEditorActions.ts index 2bf35d04e94c..29299765e873 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingEditorActions.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingEditorActions.ts @@ -14,7 +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 { 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'; @@ -22,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 { @@ -72,14 +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: ctxHasRequestInProgress.negate(), + 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 ), }, diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingEditorContextKeys.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingEditorContextKeys.ts index 69f6d04b3d73..d86957b3ca83 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingEditorContextKeys.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingEditorContextKeys.ts @@ -13,7 +13,7 @@ import { IWorkbenchContribution } from '../../../../common/contributions.js'; import { EditorResourceAccessor, SideBySideEditor } from '../../../../common/editor.js'; import { IEditorGroup, IEditorGroupsService } from '../../../../services/editor/common/editorGroupsService.js'; import { IInlineChatSessionService } from '../../../inlineChat/browser/inlineChatSessionService.js'; -import { ChatEditingSessionState, IChatEditingService, IChatEditingSession, IModifiedFileEntry, WorkingSetEntryState } from '../../common/chatEditingService.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")); @@ -120,7 +120,10 @@ class ContextKeyGroup { this._ctxHasEditorModification.set(isInlineChat || entry?.state.read(r) === WorkingSetEntryState.Modified); this._ctxIsGlobalEditingSession.set(session.isGlobalEditingSession); this._ctxReviewModeEnabled.set(entry ? entry.reviewMode.read(r) : false); - this._ctxHasRequestInProgress.set(session.state.read(r) === ChatEditingSessionState.StreamingEdits || isRequestInProgress.read(r) || !entry); + this._ctxHasRequestInProgress.set( + Boolean(entry?.isCurrentlyBeingModifiedBy.read(r)) // any entry changing + || (isInlineChat && isRequestInProgress.read(r)) // inline chat request + ); // number of requests const requestCount = chatModel @@ -154,27 +157,28 @@ export class ObservableEditorSession { @IChatEditingService chatEditingService: IChatEditingService, @IInlineChatSessionService inlineChatService: IInlineChatSessionService ) { - this.value = derived(r => { - 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; - }); - const result = sessionObs.read(r); - if (result) { - return result; + 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 inlineSessionObs = observableFromEvent(this, inlineChatService.onDidChangeSessions, () => inlineChatService.getSession2(uri)); const inlineSession = inlineSessionObs.read(r); - return inlineSession - ? { session: inlineSession.editingSession, entry: inlineSession.editingSession.readEntry(uri, r), isInlineChat: true } - : undefined; + + 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 1a5c9c250b6a..13643c63bf9b 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingEditorOverlay.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingEditorOverlay.ts @@ -5,7 +5,7 @@ import '../media/chatEditingEditorOverlay.css'; import { combinedDisposable, DisposableMap, DisposableStore, MutableDisposable, toDisposable } from '../../../../../base/common/lifecycle.js'; -import { autorun, constObservable, derived, derivedOpts, IObservable, observableFromEvent, observableSignalFromEvent, observableValue, transaction } from '../../../../../base/common/observable.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'; import { IInstantiationService } from '../../../../../platform/instantiation/common/instantiation.js'; import { IChatEditingService, IChatEditingSession, IModifiedFileEntry, WorkingSetEntryState } from '../../common/chatEditingService.js'; @@ -250,7 +250,7 @@ class ChatEditorOverlayWidget { } protected override getTooltip(): string | undefined { - return this._requestMessage.get()?.message; + return this._requestMessage.get()?.message || super.getTooltip(); } }; } @@ -405,25 +405,24 @@ class ChatEditingOverlayController { const { session, entry } = data; - if (!session.isGlobalEditingSession && isInProgress.read(r) && !entry?.isCurrentlyBeingModifiedBy.read(r)) { - // inline chat request - widget.show(session, undefined, { entryIndex: constObservable(0), changeIndex: constObservable(0) }); - 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; - }); + 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.show(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 db6c5e3dbba2..d99896c2ec2e 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,17 +72,10 @@ 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 _edit: OffsetEdit = OffsetEdit.empty; private _isEditFromUs: boolean = false; @@ -93,7 +87,7 @@ export class ChatEditingModifiedDocumentEntry extends AbstractChatEditingModifie 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[] = []; @@ -107,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, @@ -114,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( @@ -126,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 ) @@ -154,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(); @@ -165,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 { @@ -197,7 +204,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(); @@ -207,12 +214,9 @@ export class ChatEditingModifiedDocumentEntry extends AbstractChatEditingModifie this._setDocValue(this.initialContent); } - override async acceptStreamingEditsEnd(tx: ITransaction) { + protected override async _areOriginalAndModifiedIdentical(): Promise { const diff = await this._diffOperation; - super.acceptStreamingEditsEnd(tx); - if (diff?.identical) { - this.accept(tx); - } + return diff ? diff.identical : false; } protected override _resetEditsState(tx: ITransaction): void { @@ -248,21 +252,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: @@ -274,13 +278,10 @@ export class ChatEditingModifiedDocumentEntry extends AbstractChatEditingModifie } } - override acceptStreamingEditsStart(responseModel: IChatResponseModel, tx: ITransaction) { - super.acceptStreamingEditsStart(responseModel, tx); - - // push stack element whenever streaming starts - const request = responseModel.session.getRequests().find(req => req.id === responseModel.requestId); + 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"); - this._undoRedoService.pushElement(new SingleModelEditStackElement(label, 'chat.edit', this.doc, null)); + return new SingleModelEditStackElement(label, 'chat.edit', this.modifiedModel, null); } async acceptAgentEdits(resource: URI, textEdits: (TextEdit | ICellEditOperation)[], isLastEdits: boolean, responseModel: IChatResponseModel): Promise { @@ -309,14 +310,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 { @@ -338,7 +339,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); @@ -352,10 +353,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); @@ -368,7 +369,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; }); @@ -390,38 +391,38 @@ export class ChatEditingModifiedDocumentEntry extends AbstractChatEditingModifie private async _updateDiffInfo(): Promise { - if (this.docSnapshot.isDisposed() || this.doc.isDisposed()) { + 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()) { + 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); @@ -433,7 +434,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 @@ -444,14 +445,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 9f0da9e2a0a6..8a2af37281da 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); + } } + protected abstract _createUndoRedoElement(response: IChatResponseModel): IUndoRedoElement | undefined; + abstract acceptAgentEdits(uri: URI, edits: (TextEdit | ICellEditOperation)[], isLastEdits: boolean, responseModel: IChatResponseModel): Promise; - acceptStreamingEditsEnd(tx: ITransaction) { + 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 f7cdebe88fe5..f8fb3d780092 100644 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedNotebookEntry.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingModifiedNotebookEntry.ts @@ -3,51 +3,40 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -import { RunOnceScheduler } from '../../../../../base/common/async.js'; -import { decodeBase64, encodeBase64, streamToBuffer, VSBuffer } from '../../../../../base/common/buffer.js'; +import { streamToBuffer } from '../../../../../base/common/buffer.js'; import { CancellationToken } from '../../../../../base/common/cancellation.js'; -import { DisposableStore, IReference, toDisposable } from '../../../../../base/common/lifecycle.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, observableValue, autorun, transaction } from '../../../../../base/common/observable.js'; -import { ObservableDisposable } from '../../../../../base/common/observableDisposable.js'; +import { ITransaction, IObservable, observableValue, autorun, transaction, ObservablePromise } from '../../../../../base/common/observable.js'; import { isEqual } from '../../../../../base/common/resources.js'; -import { themeColorFromId } from '../../../../../base/common/themables.js'; import { assertType } from '../../../../../base/common/types.js'; import { URI } from '../../../../../base/common/uri.js'; -import { EditOperation, ISingleEditOperation } from '../../../../../editor/common/core/editOperation.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 { IDocumentDiff, nullDocumentDiff } from '../../../../../editor/common/diff/documentDiffProvider.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 { IModelDeltaDecoration, ITextModel, MinimapPosition, OverviewRulerLane } from '../../../../../editor/common/model.js'; -import { SingleModelEditStackElement } from '../../../../../editor/common/model/editStack.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 { ITextModel } from '../../../../../editor/common/model.js'; import { IModelService } from '../../../../../editor/common/services/model.js'; import { ITextModelService } from '../../../../../editor/common/services/resolverService.js'; -import { IModelContentChangedEvent } from '../../../../../editor/common/textModelEvents.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 { 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 { IEditorPane, SaveReason } from '../../../../common/editor.js'; import { IFilesConfigurationService } from '../../../../services/filesConfiguration/common/filesConfigurationService.js'; import { SnapshotContext } from '../../../../services/workingCopy/common/fileWorkingCopy.js'; -import { ChatEditingNotebookFileSystemProvider } from '../../../notebook/browser/contrib/chatEdit/chatEditingNotebookFileSystemProvider.js'; import { NotebookTextDiffEditor } from '../../../notebook/browser/diff/notebookDiffEditor.js'; import { INotebookTextDiffEditor } from '../../../notebook/browser/diff/notebookDiffEditorBrowser.js'; -import { CellDiffInfo, computeDiff } from '../../../notebook/browser/diff/notebookDiffViewModel.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, NotebookData, NotebookSetting, NotebookTextModelChangedEvent, TransientOptions } from '../../../notebook/common/notebookCommon.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'; @@ -55,14 +44,17 @@ import { INotebookEditorWorkerService } from '../../../notebook/common/services/ import { ChatEditKind, IModifiedFileEntryEditorIntegration, WorkingSetEntryState } from '../../common/chatEditingService.js'; import { IChatResponseModel } from '../../common/chatModel.js'; import { IChatService } from '../../common/chatService.js'; -import { IDocumentDiff2 } from './chatEditingCodeEditorIntegration.js'; -import { AbstractChatEditingModifiedFileEntry, IModifiedEntryTelemetryInfo, ISnapshotEntry, pendingRewriteMinimap } from './chatEditingModifiedFileEntry.js'; -import { ChatEditingNotebookDiffEditorIntegration, ChatEditingNotebookEditorIntegration, countChanges, ICellDiffInfo, sortCellChanges } from './chatEditingNotebookEditorIntegration.js'; -import { ChatEditingSnapshotTextModelContentProvider } from './chatEditingTextModelContentProviders.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 noopKeep = () => Promise.resolve(true); -const noopUndo = () => Promise.resolve(true); +const SnapshotLanguageId = 'VSCodeChatNotebookSnapshotLanguage'; export class ChatEditingModifiedNotebookEntry extends AbstractChatEditingModifiedFileEntry { static NewModelCounter: number = 0; @@ -74,8 +66,12 @@ export class ChatEditingModifiedNotebookEntry extends AbstractChatEditingModifie */ override initialContent: string; /** - * Whether we're in the process of applying edits. + * 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. @@ -84,20 +80,19 @@ export class ChatEditingModifiedNotebookEntry extends AbstractChatEditingModifie private readonly _changesCount = observableValue(this, 0); override changesCount: IObservable = this._changesCount; - private readonly cellEntryMap = new Map(); - private readonly _entries = observableValue('cellEntries', []); - private modifiedToOriginalCellMap = new ResourceMap(); - private readonly _cellDiffInfo = observableValue('diffInfo', []); - private readonly _maxModifiedLineNumbers = observableValue('changedMaxLineNumber', []); + private readonly cellEntryMap = new ResourceMap(); + private modifiedToOriginalCell = new ResourceMap(); + private readonly _cellsDiffInfo = observableValue('diffInfo', []); - get cellDiffInfo(): IObservable { - return this._cellDiffInfo; + 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 will acceptAgentEdits are being called before `isLastEdit` is sent. + * 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(); @@ -106,37 +101,43 @@ export class ChatEditingModifiedNotebookEntry extends AbstractChatEditingModifie const notebookService = accessor.get(INotebookService); const resolver = accessor.get(INotebookEditorModelResolverService); const configurationServie = accessor.get(IConfigurationService); - const textModelService = accessor.get(ITextModelService); const resourceRef: IReference = await resolver.resolve(uri); const notebook = resourceRef.object.notebook; - const originalUri = ChatEditingNotebookFileSystemProvider.getSnapshotFileURI(telemetryInfo.requestId, notebook.uri.path); + 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); - const loadedFromSnapshot = !!initialContent; 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); } - initialContent = initialContent || createSnapshot(originalRef.object.notebook, options.serializer.options, configurationServie); - const modifiedCells = new ResourceMap(); - const originalCells = new ResourceMap(); - await Promise.all(resourceRef.object.notebook.cells.map(async cell => { - modifiedCells.set(cell.uri, disposables.add(await textModelService.createModelReference(cell.uri)).object.textEditorModel); - }).concat(originalRef.object.notebook.cells.map(async cell => { - originalCells.set(cell.uri, disposables.add(await textModelService.createModelReference(cell.uri)).object.textEditorModel); - }))); - - const instance = instantiationService.createInstance(ChatEditingModifiedNotebookEntry, resourceRef, originalRef, modifiedCells, originalCells, _multiDiffEntryDelegate, options.serializer.options, telemetryInfo, chatKind, initialContent, loadedFromSnapshot); + const instance = instantiationService.createInstance(ChatEditingModifiedNotebookEntry, resourceRef, originalRef, _multiDiffEntryDelegate, options.serializer.options, telemetryInfo, chatKind, initialContent); instance._register(disposables); return instance; }); } - public static canHandleSnapshot(initialContent: string | undefined): boolean { + public static canHandleSnapshotContent(initialContent: string | undefined): boolean { if (!initialContent) { return false; } @@ -150,17 +151,23 @@ export class ChatEditingModifiedNotebookEntry extends AbstractChatEditingModifie } } + 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 modifiedCellModels: ResourceMap, - private readonly originalCellModels: ResourceMap, private readonly _multiDiffEntryDelegate: { collapse: (transaction: ITransaction | undefined) => void }, private readonly transientOptions: TransientOptions | undefined, telemetryInfo: IModifiedEntryTelemetryInfo, kind: ChatEditKind, initialContent: string, - loadedFromSnapshot: boolean = false, @IConfigurationService private readonly configurationService: IConfigurationService, @IFilesConfigurationService fileConfigService: IFilesConfigurationService, @IChatService chatService: IChatService, @@ -168,121 +175,224 @@ export class ChatEditingModifiedNotebookEntry extends AbstractChatEditingModifie @IInstantiationService instantiationService: IInstantiationService, @ITextModelService private readonly textModelService: ITextModelService, @IModelService private readonly modelService: IModelService, + @IUndoRedoService undoRedoService: IUndoRedoService, @INotebookEditorWorkerService private readonly notebookEditorWorkerService: INotebookEditorWorkerService, - @INotebookLoggingService private readonly notebookLoggingService: INotebookLoggingService, + @INotebookLoggingService private readonly loggingService: INotebookLoggingService, ) { - super(modifiedResourceRef.object.notebook.uri, telemetryInfo, kind, configurationService, fileConfigService, chatService, fileService, instantiationService); - this._register(modifiedResourceRef); - this._register(originalResourceRef); - this.modifiedModel = modifiedResourceRef.object.notebook; - this.originalModel = originalResourceRef.object.notebook; + 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)); - this._maxModifiedLineNumbers.set(this.modifiedModel.cells.map(() => 0), undefined); - this.createEmptyDiffs(); - this.modifiedModel.cells.forEach((cell, index) => { - const originalModel = this.originalCellModels.get(this.originalModel.cells[index].uri)!; - this.modifiedToOriginalCellMap.set(cell.uri, originalModel); - this.getOrCreateModifiedTextFileEntryForCell(cell); + } + + 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); + } }); - if (loadedFromSnapshot) { - this.computeDiff(); - } + this._cellsDiffInfo.set(diffs, undefined); + this._changesCount.set(countChanges(diffs), undefined); } - private async computeDiff() { - const cellDiffInfo: CellDiffInfo[] = []; + 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) { - cellDiffInfo.push(...result.cellDiffInfo); - } else { - return; + cellsDiffInfo.push(...result.cellDiffInfo); } - } catch (e) { - this.notebookLoggingService.error('Notebook Chat', 'Error computing diff:\n' + e); - return; + } catch (ex) { + this.loggingService.error('Notebook Chat', 'Error computing diff:\n' + ex); + } finally { + this._isProcessingResponse.set(false, undefined); } - const diffs = await Promise.all(cellDiffInfo.map(async (diff, i) => { - switch (diff.type) { - case 'unchanged': - return this.createUnchangedCellDiffInfo(diff.originalCellIndex, diff.modifiedCellIndex); - case 'delete': - return this.createDeleteCellDiffInfo(diff.originalCellIndex); - case 'insert': - return this.createInsertedCellDiffInfo(diff.modifiedCellIndex); - default: { - const cell = this.modifiedModel.cells[diff.modifiedCellIndex]!; - const entry = this.cellEntryMap.get(cell); - const originalCell = this.originalModel.cells[diff.originalCellIndex]!; - const diff2: IDocumentDiff2 = { - ...(entry?.diffInfo.get() ?? nullDocumentDiff), - keep: noopKeep, - undo: noopUndo, - modifiedModel: this.modifiedCellModels.get(cell.uri)!, - originalModel: this.originalCellModels.get(originalCell.uri)!, - }; - return { - modifiedCellIndex: diff.modifiedCellIndex, - originalCellIndex: diff.originalCellIndex, - diff: diff2, - type: 'modified' - } satisfies ICellDiffInfo; - } - } - })); - this._cellDiffInfo.set(diffs, undefined); - this._changesCount.set(countChanges(diffs), undefined); - } - createEmptyDiffs() { - this._cellDiffInfo.set(this.modifiedModel.cells.map((_, i) => this.createUnchangedCellDiffInfo(i, i)), undefined); + this.initializeModelsFromDiffImpl(cellsDiffInfo); } + updateCellDiffInfo(cellsDiffInfo: ICellDiffInfo[], transcation: ITransaction | undefined) { + this._cellsDiffInfo.set(sortCellChanges(cellsDiffInfo), transcation); + this._changesCount.set(countChanges(cellsDiffInfo), transcation); - getDiffForUnchangedCell(cell: NotebookCellTextModel): IDocumentDiff2 { - return { - ...nullDocumentDiff, - keep: noopKeep, - undo: noopUndo, - originalModel: this.modifiedToOriginalCellMap.get(cell.uri)!, - modifiedModel: this.modifiedCellModels.get(cell.uri)!, - }; } mirrorNotebookEdits(e: NotebookTextModelChangedEvent) { if (this._isEditFromUs || Array.from(this.cellEntryMap.values()).some(entry => entry.isEditFromUs)) { - // TODO@DonJayamanne Apply this same edit to the original notebook. return; } - // TODO@DonJayamanne We need a way to undo this operation. - this._allEditsAreFromUs = this._allEditsAreFromUs || e.rawEvents.length > 0; - - const didResetToOriginalContent = createSnapshot(this.modifiedModel, this.transientOptions, this.configurationService) === this.initialContent; + // 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(); - switch (currentState) { - case WorkingSetEntryState.Modified: - if (didResetToOriginalContent) { - this._stateObs.set(WorkingSetEntryState.Rejected, undefined); + if (currentState === WorkingSetEntryState.Rejected) { + return; + } + if (currentState === WorkingSetEntryState.Modified && didResetToOriginalContent) { + this._stateObs.set(WorkingSetEntryState.Rejected, undefined); + this.updateCellDiffInfo([], undefined); + return; + } + + if (!e.rawEvents.length) { + return; + } + if (isTransientIPyNbExtensionEvent(this.modifiedModel.notebookType, e)) { + return; + } + + 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 async _doAccept(tx: ITransaction | undefined): Promise { - const outputSizeLimit = this.configurationService.getValue(NotebookSetting.outputBackupSizeLimit) * 1024; - const snapshot = this.modifiedModel.createSnapshot({ context: SnapshotContext.Backup, outputSizeLimit, transientOptions: this.transientOptions }); - this.originalModel.restoreSnapshot(snapshot, this.transientOptions); - this._changesCount.set(0, tx); - this.createEmptyDiffs(); - + this.updateCellDiffInfo([], tx); + const snapshot = createSnapshot(this.modifiedModel, this.transientOptions, this.configurationService); + restoreSnapshot(this.originalModel, snapshot); + this.initializeModelsFromDiff(); await this._collapse(tx); } protected override async _doReject(tx: ITransaction | undefined): Promise { - this._cellDiffInfo.set([], undefined); + this.updateCellDiffInfo([], tx); if (this.createdInRequestId === this._telemetryInfo.requestId) { await this._applyEdits(async () => { await this.modifiedResourceRef.object.revert({ soft: true }); @@ -291,19 +401,19 @@ export class ChatEditingModifiedNotebookEntry extends AbstractChatEditingModifie this._onDidDelete.fire(); } else { await this._applyEdits(async () => { - const outputSizeLimit = this.configurationService.getValue(NotebookSetting.outputBackupSizeLimit) * 1024; - const snapshot = this.originalModel.createSnapshot({ context: SnapshotContext.Backup, outputSizeLimit, transientOptions: this.transientOptions }); - this.modifiedModel.restoreSnapshot(snapshot, this.transientOptions); - if (this._allEditsAreFromUs && this._entries.get().every(entry => entry.allEditsAreFromUs)) { + 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); } - this.createEmptyDiffs(); } + private async _collapse(transaction: ITransaction | undefined): Promise { this._multiDiffEntryDelegate.collapse(transaction); } @@ -312,10 +422,10 @@ export class ChatEditingModifiedNotebookEntry extends AbstractChatEditingModifie const notebookEditor = getNotebookEditorFromEditorPane(editor); if (!notebookEditor && editor.getId() === NotebookTextDiffEditor.ID) { const diffEditor = (editor.getControl() as INotebookTextDiffEditor); - return this._instantiationService.createInstance(ChatEditingNotebookDiffEditorIntegration, diffEditor, this._cellDiffInfo); + return this._instantiationService.createInstance(ChatEditingNotebookDiffEditorIntegration, diffEditor, this._cellsDiffInfo); } assertType(notebookEditor); - return this._instantiationService.createInstance(ChatEditingNotebookEditorIntegration, this, notebookEditor, this.modifiedModel, this.originalModel, this._cellDiffInfo); + return this._instantiationService.createInstance(ChatEditingNotebookEditorIntegration, this, editor, this.modifiedModel, this.originalModel, this._cellsDiffInfo); } protected override _resetEditsState(tx: ITransaction): void { @@ -323,85 +433,138 @@ export class ChatEditingModifiedNotebookEntry extends AbstractChatEditingModifie 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; const cell = isCellUri && this.modifiedModel.cells.find(cell => isEqual(cell.uri, resource)); - const cellEntry = cell ? this.cellEntryMap.get(cell) : undefined; + 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); + const cellEntry = cell && this.cellEntryMap.get(cell.uri); cellEntry?.acceptAgentEdits([], true, responseModel); }); + this.editedCells.clear(); }; await this._applyEdits(async () => { - await Promise.all(edits.map(async edit => { + edits.map(edit => { if (TextEdit.isTextEdit(edit)) { - if (!this.editedCells.has(resource)) { - finishPreviousCells(); - this.editedCells.add(resource); + // 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); } - cellEntry?.acceptAgentEdits([edit], isLastEdits, responseModel); } else { - await this.acceptNotebookEdit(edit); + // 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 (isCellUri && isLastEdits) { - this.editedCells.delete(resource); - cellEntry?.acceptAgentEdits([], isLastEdits, responseModel); + 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)); + } + transaction((tx) => { if (!isLastEdits) { this._stateObs.set(WorkingSetEntryState.Modified, tx); this._isCurrentlyBeingModifiedByObs.set(responseModel, tx); - this._rewriteRatioObs.set(Math.min(1, this.calculateRewriteRadio()), tx); + const newRewriteRation = Math.max(this._rewriteRatioObs.get(), calculateNotebookRewriteRatio(this._cellsDiffInfo.get(), this.originalModel, this.modifiedModel)); + this._rewriteRatioObs.set(Math.min(1, newRewriteRation), tx); } else { finishPreviousCells(); this.editedCells.clear(); this._resetEditsState(tx); - // this._updateDiffInfoSeq(); this._rewriteRatioObs.set(1, tx); - // this._editDecorationClear.schedule(); } }); } - async acceptNotebookEdit(edit: ICellEditOperation): Promise { + 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); - // Ensure the models have been resolved for the new cells inserted. + this.disposeDeletedCellEntries(); + if (edit.editType !== CellEditType.Replace) { return; } if (edit.count === 0) { - const diff = sortCellChanges(this._cellDiffInfo.get()).slice(); // 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 = await Promise.all(edit.cells.map(async (c, i) => { - return this.createInsertedCellDiffInfo(edit.index + i); - })); - diff.splice(edit.index + 1, 0, ...diffInsert); - this._cellDiffInfo.set(sortCellChanges(diff), undefined); + 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._cellDiffInfo.get()).slice().map(d => { + 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); } @@ -411,66 +574,97 @@ export class ChatEditingModifiedNotebookEntry extends AbstractChatEditingModifie } return d; }); - this._cellDiffInfo.set(diff, undefined); - this._changesCount.set(countChanges(this._cellDiffInfo.get()), undefined); + this.updateCellDiffInfo(diff, undefined); } } - createUnchangedCellDiffInfo(originalCellIndex: number, modifiedCellIndex: number): ICellDiffInfo { - const cell = this.modifiedModel.cells[modifiedCellIndex]; - return { modifiedCellIndex, originalCellIndex, diff: this.getDiffForUnchangedCell(cell), type: 'unchanged' }; + 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; + } - async createInsertedCellDiffInfo(modifiedCellIndex: number): Promise { + 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])]; - const modifiedModel: ITextModel = cell.textModel ?? this._register((await this.textModelService.createModelReference(cell.uri))).object.textEditorModel; // 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.modifiedCellModels.set(cell.uri, modifiedModel); - this.modifiedToOriginalCellMap.set(cell.uri, originalModel); + this.modifiedToOriginalCell.set(cell.uri, originalModelUri); const keep = async () => { - await this._applyEdits(async () => this.keepPreviouslyInsertedCell(cell)); + this._applyEditsSync(() => this.keepPreviouslyInsertedCell(cell)); this.computeStateAfterAcceptingRejectingChanges(true); return true; }; const undo = async () => { - await this._applyEdits(async () => this.undoPreviouslyInsertedCell(cell)); + this._applyEditsSync(() => this.undoPreviouslyInsertedCell(cell)); this.computeStateAfterAcceptingRejectingChanges(false); return true; }; - this.getOrCreateModifiedTextFileEntryForCell(cell, keep, undo); + 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, - diff: { + keep, + undo, + modifiedModel: new ObservablePromise(this.resolveCellModel(cell.uri)), + originalModel: new ObservablePromise(Promise.resolve(originalModel)), + diff: observableValue('deletedCellDiff', { changes, identical: false, moves: [], quitEarly: false, - keep, - undo, - modifiedModel, - originalModel, - } + }) } satisfies ICellDiffInfo; } - private computeStateAfterAcceptingRejectingChanges(accepted: boolean) { - const currentSnapshot = createSnapshot(this.modifiedModel, this.transientOptions, this.configurationService); - const originalSnapshot = createSnapshot(this.originalModel, this.transientOptions, this.configurationService); - if (currentSnapshot === originalSnapshot) { - const state = accepted ? WorkingSetEntryState.Accepted : WorkingSetEntryState.Rejected; - this._stateObs.set(state, undefined); - } - } 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)); @@ -481,12 +675,12 @@ export class ChatEditingModifiedNotebookEntry extends AbstractChatEditingModifie 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 () => { - await this._applyEdits(async () => this.keepPreviouslyDeletedCell(this.originalModel.cells.indexOf(originalCell))); + this._applyEditsSync(() => this.keepPreviouslyDeletedCell(this.originalModel.cells.indexOf(originalCell))); this.computeStateAfterAcceptingRejectingChanges(true); return true; }; const undo = async () => { - await this._applyEdits(async () => this.undoPreviouslyDeletedCell(this.originalModel.cells.indexOf(originalCell), originalCell)); + this._applyEditsSync(() => this.undoPreviouslyDeletedCell(this.originalModel.cells.indexOf(originalCell), originalCell)); this.computeStateAfterAcceptingRejectingChanges(false); return true; }; @@ -496,127 +690,77 @@ export class ChatEditingModifiedNotebookEntry extends AbstractChatEditingModifie type: 'delete' as const, modifiedCellIndex: undefined, originalCellIndex, - diff: { + originalModel: new ObservablePromise(this.resolveCellModel(originalCell.uri)), + modifiedModel: new ObservablePromise(Promise.resolve(modifiedModel)), + keep, + undo, + diff: observableValue('cellDiff', { changes, identical: false, moves: [], quitEarly: false, - originalModel: originalCell.textModel!, - modifiedModel: modifiedModel, - keep, - undo, - } + }) } satisfies ICellDiffInfo; } + private undoPreviouslyInsertedCell(cell: NotebookCellTextModel) { const index = this.modifiedModel.cells.indexOf(cell); - const diff = sortCellChanges(this._cellDiffInfo.get()).slice().map(d => { - if (d.type === 'insert' && d.modifiedCellIndex === index) { - return d; - } - if (d.type !== 'delete' && d.modifiedCellIndex > index) { - return { - ...d, - modifiedCellIndex: d.modifiedCellIndex - 1, - }; - } - return d; - }).filter(d => !(d.type === 'insert' && d.modifiedCellIndex === index)); - const edit: ICellReplaceEdit = { cells: [], count: 1, editType: CellEditType.Replace, index, }; - this.modifiedModel.applyEdits([edit], true, undefined, () => undefined, undefined, true); - this._cellDiffInfo.set(diff, undefined); - this._changesCount.set(countChanges(this._cellDiffInfo.get()), undefined); + const diffs = adjustCellDiffForRevertingAnInsertedCell(index, + this._cellsDiffInfo.get(), + this.modifiedModel.applyEdits.bind(this.modifiedModel)); + this.disposeDeletedCellEntries(); + this.updateCellDiffInfo(diffs, undefined); } - private async keepPreviouslyInsertedCell(cell: NotebookCellTextModel) { + private keepPreviouslyInsertedCell(cell: NotebookCellTextModel) { const modifiedCellIndex = this.modifiedModel.cells.indexOf(cell); if (modifiedCellIndex === -1) { // Not possible. return; } - // Find where we should insert this cell in the original notebook. - let diff = sortCellChanges(this._cellDiffInfo.get()).slice(); - const entryIndex = diff.findIndex(d => d.type === 'insert' && d.modifiedCellIndex === modifiedCellIndex); - if (entryIndex === -1) { - // Not possible. - return; - } - diff = diff.slice(0, entryIndex); - const index = diff.reduce((prev, d) => Math.max(prev, d.type === 'insert' ? -1 : d.originalCellIndex), 0); const cellToInsert: ICellDto2 = { cellKind: cell.cellKind, language: cell.language, metadata: cell.metadata, outputs: cell.outputs, source: cell.getValue(), - mime: cell.mime + mime: cell.mime, + internalMetadata: { + cellId: cell.internalMetadata.cellId + } }; - const edit: ICellReplaceEdit = { cells: [cellToInsert], count: 0, editType: CellEditType.Replace, index, }; - this.originalModel.applyEdits([edit], true, undefined, () => undefined, undefined, true); - const originalCell = this.originalModel.cells[index]; - const originalModel = originalCell.textModel ?? this._register((await this.textModelService.createModelReference(originalCell.uri))).object.textEditorModel; - this.originalCellModels.set(originalCell.uri, originalModel); - this.modifiedToOriginalCellMap.set(cell.uri, originalCell.textModel!); - const unchangedCell: ICellDiffInfo = { - type: 'unchanged', + this.cellEntryMap.get(cell.uri)?.dispose(); + this.cellEntryMap.delete(cell.uri); + const cellDiffs = adjustCellDiffForKeepingAnInsertedCell( modifiedCellIndex, - originalCellIndex: index, - diff: this.getDiffForUnchangedCell(cell) - }; - diff.push(unchangedCell); - this._cellDiffInfo.set(sortCellChanges(diff), undefined); - this._changesCount.set(countChanges(this._cellDiffInfo.get()), undefined); + this._cellsDiffInfo.get().slice(), + cellToInsert, + this.originalModel.applyEdits.bind(this.originalModel), + this.createModifiedCellDiffInfo.bind(this) + ); + this.updateCellDiffInfo(cellDiffs, undefined); } - private async undoPreviouslyDeletedCell(deletedOriginalIndex: number, originalCell: NotebookCellTextModel) { - // Find where we should insert this cell. - const index = sortCellChanges(this._cellDiffInfo.get()).reverse().reduce((previous, curr) => { - if (curr.type === 'delete' || curr.type === 'insert') { - return previous; - } - if (curr.originalCellIndex <= deletedOriginalIndex) { - return previous; - } - if (curr.modifiedCellIndex < previous) { - return curr.modifiedCellIndex; - } - return previous; - }, this.modifiedModel.cells.length - 1); - - const diff = sortCellChanges(this._cellDiffInfo.get()).slice() - .map(d => { - if (d.type !== 'delete' && d.modifiedCellIndex >= index) { - return { - ...d, - modifiedCellIndex: d.modifiedCellIndex + 1, - }; - } - return d; - }).filter(d => !(d.type === 'delete' && d.originalCellIndex === deletedOriginalIndex)); - + 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 - }; - const edit: ICellReplaceEdit = { cells: [cellToInsert], count: 0, editType: CellEditType.Replace, index, }; - this.modifiedModel.applyEdits([edit], true, undefined, () => undefined, undefined, true); - const newCell = this.modifiedModel.cells[index]; - const modifiedModel = newCell.textModel ?? this._register((await this.textModelService.createModelReference(newCell.uri))).object.textEditorModel; - this.modifiedCellModels.set(newCell.uri, modifiedModel); - this.modifiedToOriginalCellMap.set(newCell.uri, originalCell.textModel!); - const unchangedCell: ICellDiffInfo = { - type: 'unchanged', - modifiedCellIndex: index, - originalCellIndex: deletedOriginalIndex, - diff: this.getDiffForUnchangedCell(newCell) + mime: originalCell.mime, + internalMetadata: { + cellId: originalCell.internalMetadata.cellId + } }; - diff.push(unchangedCell); - this._cellDiffInfo.set(sortCellChanges(diff), undefined); - this._changesCount.set(countChanges(this._cellDiffInfo.get()), undefined); + const cellDiffs = adjustCellDiffForRevertingADeletedCell( + deletedOriginalIndex, + this._cellsDiffInfo.get(), + cellToInsert, + this.modifiedModel.applyEdits.bind(this.modifiedModel), + this.createModifiedCellDiffInfo.bind(this) + ); + this.updateCellDiffInfo(cellDiffs, undefined); } @@ -624,7 +768,7 @@ export class ChatEditingModifiedNotebookEntry extends AbstractChatEditingModifie // 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._cellDiffInfo.get()).slice() + const diffs = sortCellChanges(this._cellsDiffInfo.get()) .filter(d => !(d.type === 'delete' && d.originalCellIndex === deletedOriginalIndex)) .map(diff => { if (diff.type !== 'insert' && diff.originalCellIndex > deletedOriginalIndex) { @@ -635,8 +779,7 @@ export class ChatEditingModifiedNotebookEntry extends AbstractChatEditingModifie } return diff; }); - this._cellDiffInfo.set(diffs, undefined); - this._changesCount.set(countChanges(this._cellDiffInfo.get()), undefined); + this.updateCellDiffInfo(diffs, undefined); } private async _applyEdits(operation: () => Promise) { @@ -649,33 +792,17 @@ export class ChatEditingModifiedNotebookEntry extends AbstractChatEditingModifie } } - calculateRewriteRadio() { - const cellChanges = this._cellDiffInfo.get(); - const totalNumberOfUpdatedLines = cellChanges.reduce((totalUpdatedLines, value) => { - const getUpadtedLineCount = () => { - if (value.type === 'unchanged') { - return 0; - } - if (value.type === 'delete') { - return this.originalModel.cells[value.originalCellIndex].textModel?.getLineCount() ?? 0; - } - if (value.type === 'insert') { - return this.modifiedModel.cells[value.modifiedCellIndex].textModel?.getLineCount() ?? 0; - } - return value.diff.changes.reduce((maxLineNumber, change) => { - return Math.max(maxLineNumber, change.modified.endLineNumberExclusive); - }, 0); - }; - - return totalUpdatedLines + getUpadtedLineCount(); - }, 0); - - const totalNumberOfLines = this.modifiedModel.cells.reduce((totalLines, cell) => totalLines + (cell.textModel?.getLineCount() ?? 0), 0); - return Math.min(1, totalNumberOfUpdatedLines / totalNumberOfLines); + 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 { - this.cellEntryMap.forEach(entry => entry.isFirstEditAfterStartOrSnapshot = true); return { resource: this.modifiedURI, languageId: 'notebook', @@ -691,66 +818,97 @@ export class ChatEditingModifiedNotebookEntry extends AbstractChatEditingModifie override equalsSnapshot(snapshot: ISnapshotEntry | undefined): boolean { return !!snapshot && - this.modifiedURI.toString() === snapshot.resource.toString() && + isEqual(this.modifiedURI, snapshot.resource) && this.state.get() === snapshot.state && - createSnapshot(this.originalModel, this.transientOptions, this.configurationService) === snapshot.original && - createSnapshot(this.modifiedModel, this.transientOptions, this.configurationService) === snapshot.current; + new SnapshotComparer(snapshot.original).isEqual(this.originalModel) && + new SnapshotComparer(snapshot.current).isEqual(this.modifiedModel); } override restoreFromSnapshot(snapshot: ISnapshotEntry): void { + this.updateCellDiffInfo([], undefined); this._stateObs.set(snapshot.state, undefined); restoreSnapshot(this.originalModel, snapshot.original); - restoreSnapshot(this.modifiedModel, snapshot.current); + this.restoreSnapshotInModifiedModel(snapshot.current); + this.initializeModelsFromDiff(); } override resetToInitialContent(): void { - restoreSnapshot(this.modifiedModel, this.initialContent); + this.updateCellDiffInfo([], undefined); + this.restoreSnapshotInModifiedModel(this.initialContent); + this.initializeModelsFromDiff(); } - getOrCreateModifiedTextFileEntryForCell(cell: NotebookCellTextModel, accept?: () => Promise, reject?: () => Promise): ChatEditingNotebookCellEntry | undefined { - let cellEntry = this.cellEntryMap.get(cell); + 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 originalCellModel = this.modifiedToOriginalCellMap.get(cell.uri); - const modifiedCellModel = this.modifiedCellModels.get(cell.uri); - if (!modifiedCellModel || !originalCellModel) { - return; - } - cellEntry = this._register(this._instantiationService.createInstance(ChatEditingNotebookCellEntry, cell, modifiedCellModel, originalCellModel, this._telemetryInfo, accept, reject)); - this.cellEntryMap.set(cell, cellEntry); - this._register(autorun(r => { - const cellDiff = cellEntry.diffInfo.read(r); - let diffs = this.cellDiffInfo.get().slice(); + 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); - const entry = diffs.find(entry => entry.modifiedCellIndex === index); + let entry = diffs.find(entry => entry.modifiedCellIndex === index); if (!entry) { // Not possible. return; } - entry.diff = { ...entry.diff, ...cellDiff }; - if (cellDiff.identical) { - entry.diff = { ...entry.diff, ...nullDocumentDiff }; + 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 (entry.type === 'unchanged' || entry.type === 'modified') { - entry.type = cellDiff.identical ? 'unchanged' : 'modified'; + if (!cellEntry.diffInfo.get().identical && entry.type === 'unchanged') { + entry = { + ...entry, + type: 'modified', + }; } - diffs = diffs.filter(entry => entry.modifiedCellIndex !== index).concat({ ...entry }); - const maxModifiedLineNumber = cellEntry.maxModifiedLineNumber.read(r); - const changeCount = countChanges(diffs); - const maxModifiedLineNumbers = this._maxModifiedLineNumbers.get().slice(); - maxModifiedLineNumbers[index] = maxModifiedLineNumber; + diffs.splice(entryIndex, 1, { ...entry }); transaction(tx => { - this._cellDiffInfo.set(sortCellChanges(diffs), tx); - this._changesCount.set(changeCount, tx); - this._maxModifiedLineNumbers.set(maxModifiedLineNumbers, tx); + this.updateCellDiffInfo(diffs, tx); }); })); - this._register(autorun(r => { + 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); @@ -759,367 +917,6 @@ export class ChatEditingModifiedNotebookEntry extends AbstractChatEditingModifie } })); - const entries = this.modifiedModel.cells.map(cell => this.cellEntryMap.get(cell)).filter(entry => !!entry); - this._entries.set(entries, undefined); - return cellEntry; } } - -const BufferMarker = 'ArrayBuffer-4f56482b-5a03-49ba-8356-210d3b0c1c3d'; -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); -} - -function restoreSnapshot(notebook: NotebookTextModel, snapshot: string): void { - try { - const { transientOptions, data } = deserializeSnapshot(snapshot); - notebook.restoreSnapshot(data, transientOptions); - } - catch (ex) { - console.error('Error restoring Notebook snapshot', ex); - } -} - -export function serializeSnapshot(data: NotebookData, transientOptions: TransientOptions | undefined): string { - // No need to store the internal metadata, as this can contain unique information such as cell ids. - // Also its not something that can be persisted, hence no need to try to restore that either. - data.cells.forEach(cell => cell.internalMetadata = undefined); - return JSON.stringify([ - JSON.stringify(transientOptions) - , JSON.stringify(data, (_key, value) => { - if (value instanceof VSBuffer) { - return { - type: BufferMarker, - data: encodeBase64(value) - }; - } - return value; - }) - ]); -} - -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 }; -} - - -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 _isFirstEditAfterStartOrSnapshot: boolean = true; - public set isFirstEditAfterStartOrSnapshot(value: boolean) { - this._isFirstEditAfterStartOrSnapshot = value; - } - 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 readonly changesCount: IObservable; - public readonly diffInfo: IObservable; - 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; - - constructor( - public readonly cell: NotebookCellTextModel, - private readonly modifiedModel: ITextModel, - private readonly originalModel: ITextModel, - private readonly _telemetryInfo: IModifiedEntryTelemetryInfo, - acceptChange: (() => Promise) | undefined, - undoChange: (() => Promise) | undefined, - @IConfigurationService configService: IConfigurationService, - @IChatService private readonly _chatService: IChatService, - @IEditorWorkerService private readonly _editorWorkerService: IEditorWorkerService, - @IUndoRedoService private readonly _undoRedoService: IUndoRedoService - - ) { - super(); - this.diffInfo = this._diffInfo.map(value => { - return { - ...value, - originalModel: this.originalModel, - modifiedModel: this.modifiedModel, - keep: changes => acceptChange ? acceptChange() : this._acceptHunk(changes), - undo: changes => undoChange ? undoChange() : this._rejectHunk(changes) - } satisfies IDocumentDiff2; - }); - this.changesCount = this._diffInfo.map(diff => diff.changes.length); - this._register(this.modifiedModel.onDidChangeContent(e => { - if (this.disposed) { - return; - } - 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(); - } - } - - acceptAgentEdits(textEdits: TextEdit[], isLastEdits: boolean, responseModel: IChatResponseModel): void { - - // 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.modifiedModel, null)); - } - - 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); - } - - 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; - } - - 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/chatEditingNotebookEditorIntegration.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingNotebookEditorIntegration.ts deleted file mode 100644 index f702df873c88..000000000000 --- a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingNotebookEditorIntegration.ts +++ /dev/null @@ -1,578 +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 } 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 { 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 { 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 { INotebookEditor } from '../../../notebook/browser/notebookBrowser.js'; -import { NotebookCellTextModel } from '../../../notebook/common/model/notebookCellTextModel.js'; -import { NotebookTextModel } from '../../../notebook/common/model/notebookTextModel.js'; -import { ChatAgentLocation, IChatAgentService } from '../../common/chatAgents.js'; -import { IModifiedFileEntry, IModifiedFileEntryChangeHunk, IModifiedFileEntryEditorIntegration } from '../../common/chatEditingService.js'; -import { ChatEditingCodeEditorIntegration, IDocumentDiff2 } from './chatEditingCodeEditorIntegration.js'; - -/** - * 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'; - diff: IDocumentDiff2; // Null diff Change (property to be consistent with others, also we have a list of all line numbers) -} | { - originalCellIndex: number; - modifiedCellIndex: number; - type: 'modified'; - diff: IDocumentDiff2; // List of the changes. -} | -{ - modifiedCellIndex: undefined; - originalCellIndex: number; - type: 'delete'; - diff: IDocumentDiff2; // List of all the lines deleted. -} | -{ - modifiedCellIndex: number; - originalCellIndex: undefined; - type: 'insert'; - diff: IDocumentDiff2; // List of all the new lines. -}; - - -export class ChatEditingNotebookEditorIntegration extends Disposable implements IModifiedFileEntryEditorIntegration { - private readonly _currentIndex = observableValue(this, -1); - readonly currentIndex: IObservable = this._currentIndex; - - // TODO@DonJayamanne For now we're going to ignore being able to focus on a deleted cell. - private readonly _currentCell = observableValue(this, undefined); - readonly currentCell: IObservable = this._currentCell; - - 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: IModifiedFileEntry, - 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, - ) { - super(); - - const onDidChangeVisibleRanges = debouncedObservable(observableFromEvent(notebookEditor.onDidChangeVisibleRanges, () => notebookEditor.visibleRanges), 50); - const notebookEdotirViewModelAttached = observableFromEvent(notebookEditor.onDidAttachViewModel, () => notebookEditor.getViewModel()); - - - // INIT current index when: enabled, not streaming anymore, once per request, and when having changes - let lastModifyingRequestId: string | undefined; - this._store.add(autorun(r => { - - if (!_entry.isCurrentlyBeingModifiedBy.read(r) - && lastModifyingRequestId !== _entry.lastModifyingRequestId - && cellChanges.read(r).some(c => c.type !== 'unchanged' && c.type !== 'delete' && !c.diff.identical && !c.diff.identical) - ) { - lastModifyingRequestId = _entry.lastModifyingRequestId; - const firstModifiedCell = cellChanges.read(r). - filter(c => c.type !== 'unchanged' && c.type !== 'delete'). - filter(c => !c.diff.identical). - reduce((prev, curr) => curr.modifiedCellIndex < prev ? curr.modifiedCellIndex : prev, Number.MAX_SAFE_INTEGER); - if (typeof firstModifiedCell !== 'number' || firstModifiedCell === Number.MAX_SAFE_INTEGER) { - return; - } - const activeCell = notebookEditor.getActiveCell(); - const index = activeCell ? notebookModel.cells.findIndex(c => c.handle === activeCell.handle) : firstModifiedCell; - this._currentCell.set(notebookModel.cells[index], undefined); - } - })); - - this._register(autorun(r => { - const sortedCellChanges = sortCellChanges(cellChanges.read(r)); - - const changes = sortedCellChanges.filter(c => c.type !== 'unchanged' && c.type !== 'delete' && !c.diff.identical); - onDidChangeVisibleRanges.read(r); - if (!changes.length) { - this.cellEditorIntegrations.forEach(({ diff }) => { - diff.set({ ...diff.get(), ...nullDocumentDiff }, undefined); - }); - return; - } - - const validCells = new Set(); - changes.forEach((diff) => { - if (diff.modifiedCellIndex === undefined) { - return; - } - const cell = notebookModel.cells[diff.modifiedCellIndex]; - const editor = notebookEditor.codeEditors.find(([vm,]) => vm.handle === notebookModel.cells[diff.modifiedCellIndex].handle)?.[1]; - if (!editor || !cell) { - return; - } - validCells.add(cell); - if (this.cellEditorIntegrations.has(cell)) { - this.cellEditorIntegrations.get(cell)!.diff.set(diff.diff, undefined); - } else { - const diff2 = observableValue(`diff${cell.handle}`, diff.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); - } - }); - - // set initial index - this._currentIndex.set(0, undefined); - this._revealChange(sortedCellChanges[0]); - - this._register(autorun(r => { - const currentChange = this.currentChange.read(r); - if (currentChange) { - const change = currentChange.change; - const indexInChange = currentChange.index; - const diffChangeIndex = sortCellChanges(this.cellChanges.get().filter(c => c.type !== 'unchanged' && !c.diff.identical)).findIndex(c => c === change); - - if (diffChangeIndex !== -1) { - this._currentIndex.set(diffChangeIndex + indexInChange, undefined); - } - } else { - this._currentIndex.set(-1, 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)?.diff; - if (entry) { - return entry.keep(entry.changes[0]); - } - return Promise.resolve(true); - }, - reject() { - const entry = cellChanges.get().find(c => c.type === 'delete' && c.originalCellIndex === deletedCellIndex)?.diff; - if (entry) { - return entry.undo(entry.changes[0]); - } - return Promise.resolve(true); - }, - } satisfies IModifiedFileEntryChangeHunk; - } - })); - - return { - insertedCellDecorator, - deletedCellDecorator - }; - }); - - this._register(autorun(r => { - // We can have inserted cells that have been accepted, in those cases we do not wany any decorators on them. - const changes = cellChanges.read(r).filter(c => c.type === 'insert' ? !c.diff.identical : true); - const decorators = this.insertDeleteDecorators.read(r); - if (decorators) { - decorators.insertedCellDecorator.apply(changes); - decorators.deletedCellDecorator.apply(changes, originalModel); - } - })); - } - - getCurrentCell() { - const activeCell = this.notebookModel.cells.find(c => c.handle === this.notebookEditor.getActiveCell()?.handle) || this._currentCell.get(); - if (!activeCell) { - return undefined; - } - const index = this.notebookModel.cells.findIndex(c => c.handle === activeCell.handle); - const integration = this.cellEditorIntegrations.get(activeCell)?.integration; - return integration ? { integration, index: index, handle: activeCell.handle, cell: activeCell } : undefined; - } - - selectCell(cell: NotebookCellTextModel) { - const integration = this.cellEditorIntegrations.get(cell)?.integration; - if (integration) { - this._currentCell.set(cell, undefined); - const cellViewModel = this.notebookEditor.getViewModel()?.viewCells.find(c => c.handle === cell.handle); - if (cellViewModel) { - this.notebookEditor.focusNotebookCell(cellViewModel, 'editor'); - } - } - } - getNextCell(nextOrPrevious: boolean) { - const current = this.getCurrentCell(); - if (!current) { - // const changes = this.cellChanges.get().filter(c => c.type === 'modified' || c.type !== 'delete'); - // if (!changes.length) { - // return undefined; - // } - // return this.getIntegrationForCell(changes[0].modifiedCellIndex); - return; - } - const changes = this.cellChanges.get().filter(c => c.type === 'modified' || c.type === 'insert'); - const nextIndex = changes.reduce((prev, curr) => { - if (nextOrPrevious) { - if (typeof curr.modifiedCellIndex !== 'number' || curr.modifiedCellIndex <= current.index) { - return prev; - } - return Math.min(prev, curr.modifiedCellIndex); - } else { - if (typeof curr.modifiedCellIndex !== 'number' || curr.modifiedCellIndex >= current.index) { - return prev; - } - return Math.max(prev, curr.modifiedCellIndex); - } - }, nextOrPrevious ? Number.MAX_SAFE_INTEGER : -1); - if (nextIndex === -1 || nextIndex === Number.MAX_SAFE_INTEGER) { - return undefined; - } - return this.getCell(nextIndex); - } - - getCell(modifiedCellIndex: number) { - const cell = this.notebookModel.cells[modifiedCellIndex]; - const integration = this.cellEditorIntegrations.get(cell)?.integration; - return integration ? { integration, index: modifiedCellIndex, handle: cell.handle, cell } : undefined; - } - - 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._revealChange(change, firstOrLast); - } - - private _revealChange(change: ICellDiffInfo, firstOrLast?: boolean) { - switch (change.type) { - case 'insert': - case 'modified': - { - const cell = this.getCell(change.modifiedCellIndex); - if (!cell) { - return false; - } - - cell.integration.reveal(firstOrLast ?? true); - this._currentChange.set({ change: change, index: cell.integration.currentIndex.get() }, undefined); - - return true; - } - case 'delete': - // reveal the deleted cell decorator - this._currentCell.set(undefined, undefined); - this.insertDeleteDecorators.get()?.deletedCellDecorator.reveal(change.originalCellIndex); - this._currentChange.set({ change: change, index: 0 }, undefined); - return true; - default: - break; - } - - return false; - } - - 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) { - this._currentCell.set(undefined, undefined); - return this._revealChange(firstChange); - } - - return false; - } - - // go to next - // first check if we are at the end of the current change - switch (currentChange.change.type) { - case 'modified': - { - const currentChangeInfo = this.getCell(currentChange.change.modifiedCellIndex); - if (!currentChangeInfo) { - return false; - } - - if (currentChangeInfo.integration.next(false)) { - this._currentChange.set({ change: currentChange.change, index: currentChangeInfo.integration.currentIndex.get() }, undefined); - return true; - } else { - const nextChange = changes[changes.indexOf(currentChange.change) + 1]; - if (nextChange) { - return this._revealChange(nextChange, true); - } - } - } - break; - case 'insert': - case 'delete': - { - // go to next change directly - const nextChange = changes[changes.indexOf(currentChange.change) + 1]; - if (nextChange) { - return this._revealChange(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) { - this._currentCell.set(undefined, undefined); - return this._revealChange(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': - { - const currentChangeInfo = this.getCell(currentChange.change.modifiedCellIndex); - if (!currentChangeInfo) { - return false; - } - - if (currentChangeInfo.integration.previous(false)) { - this._currentChange.set({ change: currentChange.change, index: currentChangeInfo.integration.currentIndex.get() }, undefined); - return true; - } else { - const prevChange = changes[changes.indexOf(currentChange.change) - 1]; - if (prevChange) { - return this._revealChange(prevChange, false); - } - } - } - break; - case 'insert': - case 'delete': - { - // go to previous change directly - const prevChange = changes[changes.indexOf(currentChange.change) - 1]; - if (prevChange) { - return this._revealChange(prevChange, false); - } - } - break; - default: - break; - } - - if (wrap) { - const lastChange = changes[changes.length - 1]; - if (lastChange) { - return this._revealChange(lastChange, false); - } - } - - return false; - } - - enableAccessibleDiffView(): void { - this.getCurrentCell()?.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.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.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.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 { - // - } -} - -export function countChanges(changes: ICellDiffInfo[]): number { - return changes.reduce((count, diff) => { - // When we accept some of the cell insert/delete the items might still be in the list. - if (diff.diff.identical) { - return count; - } - switch (diff.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.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') || (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/chatEditing/chatEditingServiceImpl.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingServiceImpl.ts index bbce908e6415..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'; @@ -34,10 +33,11 @@ import { IExtensionService } from '../../../../services/extensions/common/extens import { ILifecycleService } from '../../../../services/lifecycle/common/lifecycle.js'; import { IMultiDiffSourceResolver, IMultiDiffSourceResolverService, IResolvedMultiDiffSource, MultiDiffEditorItem } from '../../../multiDiffEditor/browser/multiDiffSourceResolverService.js'; import { CellUri } from '../../../notebook/common/notebookCommon.js'; -import { ChatAgentLocation, IChatAgentService } from '../../common/chatAgents.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, 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(CellUri.parse(part.uri)?.notebook ?? part.uri, responseModel, undoStop) }; - } - + entry = { seen: 0, streaming: session.startStreamingEdits(CellUri.parse(part.uri)?.notebook ?? part.uri, responseModel, undoStop) }; editsSeen[i] = entry; } diff --git a/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingSession.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/chatEditingSession.ts index a106b3600fda..da9bf5d722da 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'; @@ -50,6 +49,8 @@ import { ChatEditingModifiedDocumentEntry } from './chatEditingModifiedDocumentE 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'; @@ -77,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; @@ -140,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; @@ -351,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, @@ -404,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 => { @@ -425,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(); @@ -469,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)?.stop.entries; - if (!entries) { - return null; - } + 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; } @@ -556,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); - } } } @@ -569,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`); @@ -711,7 +682,7 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio startPromise.complete(); return completePromise.p; - }).then(() => this._onStreamingEditDequeued()); + }); let didComplete = false; @@ -747,7 +718,7 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio sequencer.queue(async () => { if (!this.isDisposed) { await this._acceptEdits(resource, [], true, responseModel); - this._resolve(responseModel.requestId, inUndoStop, resource); + await this._resolve(responseModel.requestId, inUndoStop, resource); completePromise.complete(); } }); @@ -755,74 +726,6 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio }; } - private _onStreamingEditDequeued() { - const state = this._state.get(); - if (state === ChatEditingSessionState.Disposed) { - return; - } - - const isStreamingEdits = !Iterable.isEmpty(this._streamingEditLocks.keys()); - const targetState = isStreamingEdits ? ChatEditingSessionState.StreamingEdits : ChatEditingSessionState.Idle; - if (state !== targetState) { - this._state.set(targetState, undefined); - } - } - - 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); @@ -976,15 +879,20 @@ export class ChatEditingSession extends Disposable implements IChatEditingSessio }; } - private _resolve(requestId: string, undoStop: string | undefined, resource: URI): void { - transaction((tx) => { + private async _resolve(requestId: string, undoStop: string | undefined, resource: URI): Promise { + await asyncTransaction(async (tx) => { + const hasOtherTasks = Iterable.some(this._streamingEditLocks.keys(), k => k !== resource.toString()); + if (!hasOtherTasks) { + this._state.set(ChatEditingSessionState.Idle, tx); + } + const entry = this._getEntry(resource); if (!entry) { return; } this.ensureEditInUndoStopMatches(requestId, undoStop, entry, /* next= */ true, tx); - entry.acceptStreamingEditsEnd(tx); + return entry.acceptStreamingEditsEnd(tx); }); this._onDidChange.fire(ChatEditingSessionChangeType.Other); @@ -1049,11 +957,11 @@ 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 notebookUri = CellUri.parse(resource)?.notebook || resource; // If a notebook isn't open, then use the old synchronization approach. - if (this._notebookService.hasSupportedNotebooks(notebookUri) && (this._notebookService.getNotebookTextModel(notebookUri) || ChatEditingModifiedNotebookEntry.canHandleSnapshot(initialContent))) { - return ChatEditingModifiedNotebookEntry.create(notebookUri, multiDiffEntryDelegate, telemetryInfo, chatKind, initialContent, this._instantiationService); + 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); @@ -1065,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); + } } } @@ -1158,7 +1070,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; } @@ -1357,7 +1269,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/notebook/browser/contrib/chatEdit/chatEditingNotebookFileSystemProvider.ts b/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingNotebookFileSystemProvider.ts similarity index 64% rename from src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/chatEditingNotebookFileSystemProvider.ts rename to src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingNotebookFileSystemProvider.ts index 0b3d8cd343d0..b79dd102d43b 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/chatEditingNotebookFileSystemProvider.ts +++ b/src/vs/workbench/contrib/chat/browser/chatEditing/notebook/chatEditingNotebookFileSystemProvider.ts @@ -10,46 +10,32 @@ import { Disposable, IDisposable } from '../../../../../../base/common/lifecycle import { ResourceMap } from '../../../../../../base/common/map.js'; import { ReadableStreamEvents } from '../../../../../../base/common/stream.js'; import { URI } from '../../../../../../base/common/uri.js'; -import { IFileService, IFileSystemProvider, FileSystemProviderCapabilities, IFileChange, IWatchOptions, IStat, IFileDeleteOptions, IFileOverwriteOptions, IFileWriteOptions, IFileReadStreamOptions, IFileOpenOptions, FileType } from '../../../../../../platform/files/common/files.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) { + @IFileService private readonly fileService: IFileService, + @IInstantiationService instantiationService: IInstantiationService, + ) { super(); - this._register(this.fileService.registerProvider(ChatEditingNotebookFileSystemProvider.scheme, new ChatEditingNotebookFileSystemProvider())); + 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 { - public static readonly scheme = 'chat-editing-notebook-model'; private static registeredFiles = new ResourceMap(); - - public static getEmptyFileURI(): URI { - return URI.from({ - scheme: ChatEditingNotebookFileSystemProvider.scheme, - query: JSON.stringify({ kind: 'empty' }), - }); - } - - public static getFileURI(documentId: string, path: string): URI { - return URI.from({ - scheme: ChatEditingNotebookFileSystemProvider.scheme, - path, - query: JSON.stringify({ kind: 'doc' }), - }); - } - public static getSnapshotFileURI(requestId: string | undefined, path: string): URI { - return URI.from({ - scheme: ChatEditingNotebookFileSystemProvider.scheme, - path, - query: JSON.stringify({ requestId: requestId ?? '' }), - }); - } - public readonly capabilities: FileSystemProviderCapabilities = FileSystemProviderCapabilities.Readonly | FileSystemProviderCapabilities.FileAtomicRead | FileSystemProviderCapabilities.FileReadWrite; public static registerFile(resource: URI, buffer: VSBuffer): IDisposable { ChatEditingNotebookFileSystemProvider.registeredFiles.set(resource, buffer); @@ -62,6 +48,9 @@ export class ChatEditingNotebookFileSystemProvider implements IFileSystemProvide }; } + 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 { @@ -92,11 +81,27 @@ export class ChatEditingNotebookFileSystemProvider implements IFileSystemProvide } async readFile(resource: URI): Promise { const buffer = ChatEditingNotebookFileSystemProvider.registeredFiles.get(resource); - if (!buffer) { - throw new Error('File not found'); + 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'); } - return buffer.buffer; + 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.'); } 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 0e19db104f3d..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; @@ -156,18 +140,6 @@ export class InlineAnchorWidget extends Disposable { }) .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 94405a7465c4..3d17180ac1d2 100644 --- a/src/vs/workbench/contrib/chat/browser/chatInputPart.ts +++ b/src/vs/workbench/contrib/chat/browser/chatInputPart.ts @@ -12,19 +12,15 @@ import { ActionViewItem, IActionViewItemOptions } from '../../../../base/browser 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'; @@ -33,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'; @@ -57,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'; @@ -68,39 +60,42 @@ 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 { IChatEditingSession } from '../common/chatEditingService.js'; +import { ChatEntitlement, IChatEntitlementService } from '../common/chatEntitlementService.js'; import { IChatRequestVariableEntry, isImageVariableEntry, isLinkVariableEntry, isPasteVariableEntry } from '../common/chatModel.js'; -import { IChatFollowup } from '../common/chatService.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 { PromptAttachmentsCollectionWidget } from './attachments/promptAttachments/promptAttachmentsCollectionWidget.js'; import { IChatWidget } from './chat.js'; import { ChatAttachmentModel } from './chatAttachmentModel.js'; import { toChatVariable } from './chatAttachmentModel/chatPromptAttachmentsCollection.js'; -import { hookUpResourceAttachmentDragAndContextMenu, hookUpSymbolAttachmentDragAndContextMenu } from './chatContentParts/chatAttachmentsContentPart.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.$; @@ -124,11 +119,11 @@ interface IChatInputPartOptions { editorOverflowWidgetsDomNode?: HTMLElement; renderWorkingSet?: boolean; enableImplicitContext?: boolean; + supportsChangingModes?: boolean; } export interface IWorkingSetEntry { uri: URI; - isMarkedReadonly?: boolean; } export class ChatInputPart extends Disposable implements IHistoryNavigationWidget { @@ -158,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) { @@ -184,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)) { @@ -198,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 @@ -224,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()); @@ -268,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; } @@ -281,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()); @@ -293,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; @@ -340,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, @@ -348,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; @@ -381,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))); @@ -420,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); @@ -429,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; @@ -477,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 { @@ -524,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 { @@ -734,8 +780,8 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge dom.h('.chat-input-container@inputContainer', [ 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('.chat-attached-context@attachedContextContainer'), ]), dom.h('.chat-editor-container@editorContainer'), dom.h('.chat-input-toolbars@inputToolbars'), @@ -756,14 +802,20 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge const toolbarsContainer = elements.inputToolbars; const attachmentToolbarContainer = elements.attachmentToolbar; this.chatEditingSessionWidgetContainer = elements.chatEditingSessionWidgetContainer; - this.renderAttachedContext(); 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); @@ -788,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, @@ -801,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); @@ -839,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 }, @@ -866,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(); } @@ -875,7 +931,9 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge const itemDelegate: ModelPickerDelegate = { onDidChangeModel: this._onDidChangeCurrentLanguageModel.event, setModel: (model: ILanguageModelChatMetadataAndIdentifier) => { - this.setCurrentLanguageModelByUser(model); + // 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() @@ -883,7 +941,11 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge 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; @@ -956,34 +1018,32 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge this.renderAttachedContext(); })); - if (this.options.renderWorkingSet) { - const attachmentToolbar = 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, - getKeyBinding: () => undefined, - actionViewItemProvider: (action, options) => { - if (action.id === 'workbench.action.chat.editing.attachFiles') { - const viewItem = this.instantiationService.createInstance(AddFilesButton, undefined, action, options); - return viewItem; - } - return undefined; + 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; } - })); - attachmentToolbar.context = { widget, placeholder: localize('chatAttachFiles', 'Search for files and context to add to your request') }; - this._register(attachmentToolbar.onDidChangeMenuItems(() => { - if (this.cachedDimensions) { - this.layout(this.cachedDimensions.height, this.cachedDimensions.width); + if (action.id === AttachToolsAction.id) { + return this.selectedToolsModel.toolsActionItemViewItemProvider(action, options); } - })); - } else { - dom.hide(attachmentToolbarContainer); - } + return undefined; + } + })); + 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); + } + })); } - private async renderAttachedContext() { + private renderAttachedContext() { const container = this.attachedContextContainer; const oldHeight = container.offsetHeight; const store = new DisposableStore(); @@ -993,7 +1053,9 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge const hoverDelegate = store.add(createInstantHoverDelegate()); const attachments = [...this.attachmentModel.attachments.entries()]; - dom.setVisibility(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; } @@ -1006,160 +1068,27 @@ export class ChatInputPart extends Disposable implements IHistoryNavigationWidge this.promptInstructionsAttached.set(!this.instructionAttachmentsPart.empty); 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)) { - ariaLabel = localize('chat.attachment.link', "Attached link, {0}", attachment.name); - label.setResource({ resource: attachment.value, name: attachment.name }, { icon: Codicon.link, title: attachment.value.toString() }); - this.attachButtonAndDisposables(widget, index, attachment, hoverDelegate); + 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) { @@ -1167,119 +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); - - 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; - } + private handleAttachmentDeletion(e: globalThis.Event, index: number, attachment: IChatRequestVariableEntry) { + this._attachmentModel.delete(attachment.id); - // 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; - } + // 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(); - } - - 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); - - hoverImage.onload = () => { - URL.revokeObjectURL(url); - }; + if (this._attachmentModel.size === 0) { + this.focus(); + } - 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); - } - }; + this._onDidChangeContext.fire({ removed: [attachment] }); } - async renderChatEditingSessionState(chatEditingSession: IChatEditingSession | null, chatWidget?: IChatWidget) { + async renderChatEditingSessionState(chatEditingSession: IChatEditingSession | null) { dom.setVisibility(Boolean(chatEditingSession), this.chatEditingSessionWidgetContainer); - if (chatEditingSession && this.configurationService.getValue('chat.renderRelatedFiles')) { - this.renderChatRelatedFiles(chatEditingSession, this.relatedFilesContainer); - } - const seenEntries = new ResourceSet(); const entries: IChatCollapsibleListItem[] = chatEditingSession?.entries.get().map((entry) => { seenEntries.add(entry.modifiedURI); @@ -1299,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); } } @@ -1326,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; @@ -1410,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, { @@ -1443,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(() => { @@ -1526,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.attachmentsContainer.offsetHeight + 6, + attachmentsHeight: this.attachmentsContainer.offsetHeight + (this.attachmentsContainer.checkVisibility() ? 6 : 0), editorBorder: 2, inputPartHorizontalPaddingInside: 12, toolbarsWidth: this.options.renderStyle === 'compact' ? executeToolbarWidth + executeToolbarPadding + inputToolbarWidth + inputToolbarPadding : 0, @@ -1622,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 = { @@ -1644,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!); @@ -1668,58 +1501,82 @@ 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; + } + }); - super(action, agentStateActions, contextMenuService, undefined, keybindingService, contextKeyService); - this.agentStateActions = agentStateActions; + const actionProvider: IActionProvider = { + getActions: () => { + const agentStateActions = [ + makeAction(ChatMode.Agent), + makeAction(ChatMode.Edit), + ]; + if (chatService.unifiedViewEnabled) { + agentStateActions.unshift(makeAction(ChatMode.Chat)); + } + + return 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'); } } diff --git a/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts b/src/vs/workbench/contrib/chat/browser/chatListRenderer.ts index 8d1682e18c5f..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, IChatUndoStop } 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'; @@ -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; @@ -142,7 +143,7 @@ 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) { @@ -240,10 +238,13 @@ 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); } @@ -741,57 +741,37 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer 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 { - 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; @@ -812,10 +792,14 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer part.kind === 'toolInvocation' && !part.isComplete))) { @@ -825,9 +809,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer): IWordCountResult | undefined { const rate = this.getProgressiveRenderRate(element); const numWordsToRender = renderData.lastRenderTime === 0 ? 1 : @@ -872,7 +854,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer content.kind === other.kind); } private renderUndoStop(content: IChatUndoStop) { - return this.renderNoContent(other => other.kind === 'undoStop' && other.id === content.id); + return this.renderNoContent(other => other.kind === content.kind && other.id === content.id); } private renderNoContent(equals: (otherContent: IChatRendererContent) => boolean): IChatContentPart { @@ -936,7 +917,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer { this.updateItemHeight(templateData); })); @@ -957,66 +938,40 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer { - const codeBlocksByResponseId = this.codeBlocksByResponseId.get(element.id); - if (codeBlocksByResponseId) { - // Only delete if this is my code block - part.codeblocks?.forEach((info, i) => { - const codeblock = codeBlocksByResponseId[codeBlockStartIndex + i]; - if (codeblock?.ownerMarkdownPartId === part.codeblocksPartId) { - delete codeBlocksByResponseId[codeBlockStartIndex + i]; - } - }); - } - })); - - part.codeblocks?.forEach((info, i) => { - codeBlocksByResponseId[codeBlockStartIndex + i] = info; - info.uriPromise.then(uri => { - if (!uri) { - return; - } - - this.codeBlocksByEditorUri.set(uri, info); - part.addDisposable!(toDisposable(() => { - const codeblock = this.codeBlocksByEditorUri.get(uri); - if (codeblock?.ownerMarkdownPartId === part.codeblocksPartId) { - this.codeBlocksByEditorUri.delete(uri); - } - })); - }); - }); - - } - - private renderToolInvocation(toolInvocation: IChatToolInvocation | IChatToolInvocationSerialized, context: IChatContentPartRenderContext, templateData: IChatListItemTemplate): IChatContentPart | undefined { - const codeBlockStartIndex = this.getCodeBlockStartIndex(context); - const part = this.instantiationService.createInstance(ChatToolInvocationPart, toolInvocation, context, this.renderer, this._contentReferencesListPool, this._editorPool, () => this._currentLayoutWidth, this._toolInvocationCodeBlockCollection, codeBlockStartIndex); - part.addDisposable(part.onDidChangeHeight(() => { - this.updateItemHeight(templateData); - })); - this.handleRenderedCodeblocks(context.element, part, codeBlockStartIndex); - return part; - } - - private renderProgressTask(task: IChatTask, templateData: IChatListItemTemplate, context: IChatContentPartRenderContext): IChatContentPart | undefined { - if (!isResponseVM(context.element)) { - return; + 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 taskPart = this.instantiationService.createInstance(ChatTaskContentPart, task, this._contentReferencesListPool, this.renderer, context); - taskPart.addDisposable(taskPart.onDidChangeHeight(() => { - this.updateItemHeight(templateData); - })); - return taskPart; + 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); @@ -1043,7 +998,7 @@ export class ChatListItemRenderer extends Disposable implements ITreeRenderer { @@ -1102,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/chatQuick.ts b/src/vs/workbench/contrib/chat/browser/chatQuick.ts index a4af4355058f..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, 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; 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 0fe3d34257fa..c7701df9dc05 100644 --- a/src/vs/workbench/contrib/chat/browser/chatSetup.ts +++ b/src/vs/workbench/contrib/chat/browser/chatSetup.ts @@ -7,70 +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, CHAT_SETUP_ACTION_ID, CHAT_SETUP_ACTION_LABEL } 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 '../common/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 { ChatEntitlement, IChatEntitlements, IChatEntitlementsService } from '../common/chatEntitlementsService.js'; -import { IStatusbarService } from '../../../services/statusbar/browser/statusbar.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 ?? '', @@ -81,53 +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 ?? '', }; -//#region Service +//#region Contribution + +class SetupChatAgentImplementation extends Disposable implements IChatAgentImplementation { -export class ChatEntitlementsService extends Disposable implements IChatEntitlementsService { + static register(instantiationService: IInstantiationService, location: ChatAgentLocation, isToolsAgent: boolean, context: ChatEntitlementContext, controller: Lazy): IDisposable { + return instantiationService.invokeFunction(accessor => { + const chatAgentService = accessor.get(IChatAgentService); - declare _serviceBrand: undefined; + // 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 + }; - readonly context: ChatSetupContext | undefined; - readonly requests: ChatSetupRequests | undefined; + 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( - @IInstantiationService instantiationService: IInstantiationService, - @IProductService productService: IProductService, - @IWorkbenchEnvironmentService environmentService: IWorkbenchEnvironmentService + 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(); + } - if ( - !productService.defaultChatAgent || // needs product config - (isWeb && !environmentService.remoteAuthority) // only enabled locally or a remote backend - ) { - return; - } + 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 - this.context = this._register(instantiationService.createInstance(ChatSetupContext)); - this.requests = this._register(instantiationService.createInstance(ChatSetupRequests, this.context)); + return this.doInvoke(request, progress, chatService, languageModelsService); + }); } - async resolve(token: CancellationToken): Promise { - return this.requests?.forceResolveEntitlement(undefined, token); + 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 {}; } } -//#endregion +enum ChatSetupDialogResult { + Canceled = 0, + DefaultSetup = 1, + SetupWithoutEnterpriseProvider = 2, + SetupWithEnterpriseProvider = 3 +} -//#region Contribution +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; + } + + 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 { @@ -138,26 +377,48 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr @IInstantiationService private readonly instantiationService: IInstantiationService, @ICommandService private readonly commandService: ICommandService, @ITelemetryService private readonly telemetryService: ITelemetryService, - @IWorkbenchAssignmentService private readonly experimentService: IWorkbenchAssignmentService, - @IChatEntitlementsService chatEntitlementsService: ChatEntitlementsService, + @IChatEntitlementService chatEntitlementService: ChatEntitlementService, + @IConfigurationService private readonly configurationService: IConfigurationService, ) { super(); - const context = chatEntitlementsService.context; - const requests = chatEntitlementsService.requests; + const context = chatEntitlementService.context?.value; + const requests = chatEntitlementService.requests?.value; if (!context || !requests) { return; // disabled } 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, @@ -166,12 +427,16 @@ 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() { @@ -196,14 +461,60 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr 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); + } + } + } + } } } @@ -218,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', @@ -270,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, @@ -285,13 +596,13 @@ 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)); @@ -335,443 +646,6 @@ export class ChatSetupContribution extends Disposable implements IWorkbenchContr } })); } - - 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 monthly_quotas?: { - readonly chat: number; - readonly completions: number; - }; - readonly limited_user_reset_date: string; -} - -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; - } - - 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: { - chatTotal: entitlementsResponse.monthly_quotas?.chat, - completionsTotal: entitlementsResponse.monthly_quotas?.completions, - chatRemaining: entitlementsResponse.limited_user_quotas?.chat, - completionsRemaining: 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.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 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; - } - - 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)) - } - ] - }); - } - - override dispose(): void { - this.pendingResolveCts.dispose(true); - - super.dispose(); - } } //#endregion @@ -784,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 { @@ -808,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, @@ -826,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(); @@ -846,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, { @@ -854,8 +730,8 @@ class ChatSetupController extends Disposable { }); try { - await this.progressService.withProgress({ - location: ProgressLocation.Window, + return await this.progressService.withProgress({ + location: options?.notificationProgress ? ProgressLocation.Notification : ProgressLocation.Window, command: CHAT_SETUP_ACTION_ID, title, }, () => this.doSetup(options?.forceSignIn ?? false, watch)); @@ -864,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; @@ -878,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; @@ -890,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; @@ -910,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 entitlements: IChatEntitlements | 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); - - entitlements = await this.requests.forceResolveEntitlement(session); + ({ session, entitlements } = await this.requests.signIn()); } catch (e) { this.logService.error(`[chat setup] signIn: error ${e}`); } @@ -931,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") }); @@ -944,9 +819,10 @@ class ChatSetupController extends Disposable { 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); @@ -964,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); @@ -993,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 { @@ -1027,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' @@ -1174,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; @@ -1252,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; } } @@ -1389,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 index f4e9d75cc1c5..6d1030537c48 100644 --- a/src/vs/workbench/contrib/chat/browser/chatStatus.ts +++ b/src/vs/workbench/contrib/chat/browser/chatStatus.ts @@ -6,48 +6,103 @@ import './media/chatStatus.css'; import { safeIntl } from '../../../../base/common/date.js'; import { Disposable, DisposableStore, MutableDisposable } from '../../../../base/common/lifecycle.js'; -import { language, OS } from '../../../../base/common/platform.js'; +import { language } from '../../../../base/common/platform.js'; import { localize } from '../../../../nls.js'; -import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; -import { IKeybindingService } from '../../../../platform/keybinding/common/keybinding.js'; -import { IProductService } from '../../../../platform/product/common/productService.js'; import { IWorkbenchContribution } from '../../../common/contributions.js'; -import { IWorkbenchAssignmentService } from '../../../services/assignment/common/assignmentService.js'; -import { IStatusbarEntry, IStatusbarEntryAccessor, IStatusbarService, ShowTooltipCommand, StatusbarAlignment, TooltipContent } from '../../../services/statusbar/browser/statusbar.js'; -import { ChatContextKeys } from '../common/chatContextKeys.js'; -import { IChatQuotasService } from '../common/chatQuotasService.js'; -import { quotaToButtonMessage, OPEN_CHAT_QUOTA_EXCEEDED_DIALOG, CHAT_SETUP_ACTION_LABEL, TOGGLE_CHAT_ACTION_ID, CHAT_OPEN_ACTION_ID } from './actions/chatActions.js'; -import { $, addDisposableListener, append, EventType } from '../../../../base/browser/dom.js'; -import { IChatEntitlementsService } from '../common/chatEntitlementsService.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 { KeybindingLabel } from '../../../../base/browser/ui/keybindingLabel/keybindingLabel.js'; -import { defaultCheckboxStyles, defaultKeybindingLabelStyles } from '../../../../platform/theme/browser/defaultStyles.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 { Command } from '../../../../editor/common/languages.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 readonly treatment = this.assignmentService.getTreatment('config.chat.experimental.statusIndicator.enabled'); //TODO@bpasero remove this experiment eventually + private static readonly SETTING = 'chat.experimental.statusIndicator.enabled'; private entry: IStatusbarEntryAccessor | undefined = undefined; - private readonly entryDisposables = this._register(new MutableDisposable()); - private dateFormatter = safeIntl.DateTimeFormat(language, { year: 'numeric', month: 'long', day: 'numeric' }); + private dashboard = new Lazy(() => this.instantiationService.createInstance(ChatStatusDashboard)); constructor( @IStatusbarService private readonly statusbarService: IStatusbarService, - @IChatQuotasService private readonly chatQuotasService: IChatQuotasService, - @IChatEntitlementsService private readonly chatEntitlementsService: IChatEntitlementsService, - @IContextKeyService private readonly contextKeyService: IContextKeyService, - @IWorkbenchAssignmentService private readonly assignmentService: IWorkbenchAssignmentService, - @IProductService private readonly productService: IProductService, - @IKeybindingService private readonly keybindingService: IKeybindingService, + @IChatEntitlementService private readonly chatEntitlementService: ChatEntitlementService, @IConfigurationService private readonly configurationService: IConfigurationService, - @ICommandService private readonly commandService: ICommandService + @IInstantiationService private readonly instantiationService: IInstantiationService ) { super(); @@ -56,150 +111,212 @@ export class ChatStatusBarEntry extends Disposable implements IWorkbenchContribu } private async create(): Promise { - let enabled = false; - if (this.productService.quality === 'stable') { - enabled = (await this.treatment) === true; - } else { - enabled = true; - } + const hidden = this.chatEntitlementService.sentiment === ChatSentiment.Disabled; + const disabled = this.configurationService.getValue(ChatStatusBarEntry.SETTING) === false; - if (!enabled) { - return; - } + 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 }); - this.entry = this._register(this.statusbarService.addEntry(this.getEntryProps(), ChatStatusBarEntry.ID, StatusbarAlignment.RIGHT, Number.NEGATIVE_INFINITY /* the end of the right hand side */)); + // 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 { - const contextKeysSet = new Set([ - ChatContextKeys.Setup.limited.key, - ChatContextKeys.Setup.installed.key, - ChatContextKeys.Setup.canSignUp.key, - ChatContextKeys.Setup.signedOut.key - ]); - this._register(this.contextKeyService.onDidChangeContext(e => { - if (!this.entry) { - return; - } - - if (e.affectsSome(contextKeysSet)) { - this.entry.update(this.getEntryProps()); + this._register(this.configurationService.onDidChangeConfiguration(e => { + if (e.affectsConfiguration(ChatStatusBarEntry.SETTING)) { + this.create(); } })); - this._register(this.chatQuotasService.onDidChangeQuotaExceeded(() => this.entry?.update(this.getEntryProps()))); + 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 { - const disposables = new DisposableStore(); - this.entryDisposables.value = disposables; - let text = '$(copilot)'; let ariaLabel = localize('chatStatus', "Copilot Status"); - let command: string | Command = TOGGLE_CHAT_ACTION_ID; - let tooltip: TooltipContent = localize('openChat', "Open Chat ({0})", this.keybindingService.lookupKeybinding(command)?.getLabel() ?? ''); - - // Quota Exceeded - const { chatQuotaExceeded, completionsQuotaExceeded } = this.chatQuotasService.quotas; - 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"); + 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'; } - text = `$(copilot-warning) ${quotaWarning}`; - ariaLabel = quotaWarning; - command = OPEN_CHAT_QUOTA_EXCEEDED_DIALOG; - tooltip = quotaToButtonMessage({ chatQuotaExceeded, completionsQuotaExceeded }); - } + // 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"); + } - // Copilot Not Installed - else if ( - this.contextKeyService.getContextKeyValue(ChatContextKeys.Setup.installed.key) === false || - this.contextKeyService.getContextKeyValue(ChatContextKeys.Setup.canSignUp.key) === true - ) { - tooltip = CHAT_SETUP_ACTION_LABEL.value; + text = `$(copilot-warning) ${quotaWarning}`; + ariaLabel = quotaWarning; + kind = 'prominent'; + } } - // Signed out - else if (this.contextKeyService.getContextKeyValue(ChatContextKeys.Setup.signedOut.key) === true) { - text = '$(copilot-not-connected)'; - ariaLabel = localize('signInToUseCopilot', "Sign in to Use Copilot..."); - tooltip = localize('signInToUseCopilot', "Sign in to Use Copilot..."); - } + return { + name: localize('chatStatus', "Copilot Status"), + text, + ariaLabel, + command: ShowTooltipCommand, + showInAllWindows: true, + kind, + tooltip: { element: token => this.dashboard.value.show(token) } + }; + } - // Copilot Limited User - else if (this.contextKeyService.getContextKeyValue(ChatContextKeys.Setup.limited.key) === true) { - tooltip = () => { - const container = $('div.chat-status-bar-entry-tooltip'); + override dispose(): void { + super.dispose(); - // Quota Indicator - const { chatTotal, chatRemaining, completionsTotal, completionsRemaining, quotaResetDate } = this.chatQuotasService.quotas; + this.entry?.dispose(); + this.entry = undefined; + } +} - container.appendChild($('div', undefined, localize('limitTitle', "You are currently using Copilot Free:"))); +function isNewUser(chatEntitlementService: IChatEntitlementService): boolean { + return chatEntitlementService.sentiment !== ChatSentiment.Installed || // copilot not installed + chatEntitlementService.entitlement === ChatEntitlement.Available; // not yet signed up to copilot +} - const chatQuotaIndicator = this.createQuotaIndicator(container, chatTotal, chatRemaining, localize('chatsLabel', "Chats Used")); - const completionsQuotaIndicator = this.createQuotaIndicator(container, completionsTotal, completionsRemaining, localize('completionsLabel', "Completions Used")); +function canUseCopilot(chatEntitlementService: IChatEntitlementService): boolean { + const newUser = isNewUser(chatEntitlementService); + const signedOut = chatEntitlementService.entitlement === ChatEntitlement.Unknown; + const allQuotaReached = chatEntitlementService.quotas.chatQuotaExceeded && chatEntitlementService.quotas.completionsQuotaExceeded; - this.chatEntitlementsService.resolve(CancellationToken.None).then(() => { - const { chatTotal, chatRemaining, completionsTotal, completionsRemaining } = this.chatQuotasService.quotas; + return !newUser && !signedOut && !allQuotaReached; +} - chatQuotaIndicator(chatTotal, chatRemaining); - completionsQuotaIndicator(completionsTotal, completionsRemaining); - }); +interface ISettingsAccessor { + readSetting: () => boolean; + writeSetting: (value: boolean) => Promise; +} - container.appendChild($('div', undefined, localize('limitQuota', "Usage will reset on {0}.", this.dateFormatter.format(quotaResetDate)))); +class ChatStatusDashboard extends Disposable { - // Settings - container.appendChild(document.createElement('hr')); - this.createSettings(container, disposables); + private readonly element = $('div.chat-status-bar-entry-tooltip'); - // Shortcuts - container.appendChild(document.createElement('hr')); - this.createShortcuts(container, disposables); + 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)); + } - return container; - }; - command = ShowTooltipCommand; + 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); + })(); } - // Any other User - else { - tooltip = () => { - const container = $('div.chat-status-bar-entry-tooltip'); + // Settings + { + addSeparator(localize('settingsTitle', "Settings")); - // Settings - this.createSettings(container, disposables); + 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); - // Shortcuts - container.appendChild($('hr')); - this.createShortcuts(container, disposables); + 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."))); - return container; - }; - command = ShowTooltipCommand; + 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 { - name: localize('chatStatus', "Copilot Status"), - text, - ariaLabel, - command, - showInAllWindows: true, - kind: 'copilot', - tooltip - }; + 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'); - container.appendChild($('div.quota-indicator', undefined, + const quotaIndicator = container.appendChild($('div.quota-indicator', undefined, $('div.quota-label', undefined, $('span', undefined, label), quotaText @@ -210,9 +327,23 @@ export class ChatStatusBarEntry extends Disposable implements IWorkbenchContribu )); const update = (total: number | undefined, remaining: number | undefined) => { + quotaIndicator.classList.remove('error'); + quotaIndicator.classList.remove('warning'); + if (typeof total === 'number' && typeof remaining === 'number') { - quotaText.textContent = localize('quotaDisplay', "{0} / {1}", total - remaining, total); - quotaBit.style.width = `${((total - remaining) / total) * 100}%`; + 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'); + } } }; @@ -221,64 +352,131 @@ export class ChatStatusBarEntry extends Disposable implements IWorkbenchContribu return update; } - private createShortcuts(container: HTMLElement, disposables: DisposableStore): HTMLElement { - const shortcuts = container.appendChild($('div.shortcuts')); + private createSettings(container: HTMLElement, disposables: DisposableStore): HTMLElement { + const modeId = this.editorService.activeTextEditorLanguageId; + const settings = container.appendChild($('div.settings')); - const openChat = { text: localize('shortcuts.chat', "Chat"), id: CHAT_OPEN_ACTION_ID }; - const openCopilotEdits = { text: localize('shortcuts.copilotEdits', "Copilot Edits"), id: 'workbench.action.chat.openEditSession' }; - const inlineChat = { text: localize('shortcuts.inlineChat', "Inline Chat"), id: 'inlineChat.start' }; + // --- Code Completions + { + const globalSetting = append(settings, $('div.setting')); + this.createCodeCompletionsSetting(globalSetting, localize('settings.codeCompletions', "Code Completions (all files)"), '*', disposables); - for (const entry of [openChat, openCopilotEdits, inlineChat]) { - const keys = this.keybindingService.lookupKeybinding(entry.id); - if (!keys) { - continue; + 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); + } - const shortcut = append(shortcuts, $('div.shortcut')); + 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); - const shortcutLabel = append(shortcut, $('span.shortcut-label', undefined, entry.text)); + checkbox.checked = !checkbox.checked; + accessor.writeSetting(checkbox.checked); + checkbox.focus(); + } + })); + }); - const shortcutKey = disposables.add(new KeybindingLabel(shortcut, OS, { ...defaultKeybindingLabelStyles })); - shortcutKey.set(keys); + disposables.add(checkbox.onChange(() => { + accessor.writeSetting(checkbox.checked); + })); - for (const element of [shortcutLabel, shortcutKey.element]) { - disposables.add(addDisposableListener(element, EventType.CLICK, e => { - this.commandService.executeCommand(entry.id); - })); + 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 shortcuts; + return checkbox; } - private createSettings(container: HTMLElement, disposables: DisposableStore): HTMLElement { - const settings = container.appendChild($('div.settings')); + private createCodeCompletionsSetting(container: HTMLElement, label: string, modeId: string | undefined, disposables: DisposableStore): void { + this.createSetting(container, defaultChat.completionsEnablementSetting, label, this.getCompletionsSettingAccessor(modeId), disposables); + } - const toggleCompletions = { text: localize('settings.toggleCompletions', "Code Completions"), id: 'editor.inlineSuggest.enabled' }; - const toggleNextEditSuggestions = { text: localize('settings.toggleNextEditSuggestions', "Next Edit Suggestions (Preview)"), id: 'github.copilot.nextEditSuggestions.enabled' }; + private getCompletionsSettingAccessor(modeId = '*'): ISettingsAccessor { + const settingId = defaultChat.completionsEnablementSetting; - for (const entry of [toggleCompletions, toggleNextEditSuggestions]) { - const checked = Boolean(this.configurationService.getValue(entry.id)); + return { + readSetting: () => { + const result = this.configurationService.getValue>(settingId); + if (!isObject(result)) { + return false; + } - const setting = append(settings, $('div.setting')); + if (typeof result[modeId] !== 'undefined') { + return Boolean(result[modeId]); // go with setting if explicitly defined + } - const checkbox = disposables.add(new Checkbox(entry.text, checked, defaultCheckboxStyles)); - setting.appendChild(checkbox.domNode); + return Boolean(result['*']); // fallback to global setting otherwise + }, + writeSetting: (value: boolean) => { + let result = this.configurationService.getValue>(settingId); + if (!isObject(result)) { + result = Object.create(null); + } - const settingLabel = append(setting, $('span.setting-label', undefined, entry.text)); - disposables.add(addDisposableListener(settingLabel, EventType.CLICK, e => { - if (checkbox?.enabled && (e.target as HTMLElement).tagName !== 'A') { - checkbox.checked = !checkbox.checked; - this.configurationService.updateValue(entry.id, checkbox.checked); - checkbox.focus(); + 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 }); } - })); - disposables.add(checkbox.onChange(() => { - this.configurationService.updateValue(entry.id, checkbox.checked); - })); + 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(); } - return settings; + 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 6e9563085f7a..3cf5196fdd41 100644 --- a/src/vs/workbench/contrib/chat/browser/chatViewPane.ts +++ b/src/vs/workbench/contrib/chat/browser/chatViewPane.ts @@ -25,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'; @@ -76,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` @@ -85,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(); @@ -144,15 +145,17 @@ export class ChatViewPane extends ViewPane implements IViewWelcomeDelegate { return !!shouldShow; } - private getSessionId() { - let sessionId: string | undefined; + private getTransferredOrPersistedSessionInfo(): { sessionId?: string; inputValue?: string; mode?: ChatMode } { if (this.chatService.transferredSessionData?.location === this.chatOptions.location) { - sessionId = this.chatService.transferredSessionData.sessionId; - this.viewState.inputValue = this.chatService.transferredSessionData.inputValue; + 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 { @@ -171,21 +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, - enableImplicitContext: this.chatOptions.location === ChatAgentLocation.Panel || this.chatOptions.location === ChatAgentLocation.EditingSession, - 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, @@ -201,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') { @@ -210,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 12fba91fa444..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'; @@ -32,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'; @@ -150,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; @@ -158,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; @@ -201,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; @@ -218,6 +222,11 @@ 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, @@ -238,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(); @@ -255,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); @@ -345,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; @@ -471,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`)); @@ -480,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, { @@ -540,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(); } @@ -616,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); } @@ -629,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) { @@ -656,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); @@ -697,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 @@ -838,7 +857,8 @@ export class ChatWidget extends Disposable implements IChatWidget { menus: { executeToolbar: MenuId.ChatExecute, ...this.viewOptions.menus }, editorOverflowWidgetsDomNode: this.viewOptions.editorOverflowWidgetsDomNode, enableImplicitContext: this.viewOptions.enableImplicitContext, - renderWorkingSet: this.viewOptions.enableWorkingSet === 'explicit' + renderWorkingSet: this.viewOptions.enableWorkingSet === 'explicit', + supportsChangingModes: this.viewOptions.supportsChangingModes, }, this.styles, () => this.collectInputState() @@ -861,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; @@ -919,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 { @@ -952,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) { @@ -1083,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; } @@ -1108,35 +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)); - } - } - - // 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(); @@ -1144,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); } @@ -1164,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) { @@ -1242,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); @@ -1262,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(); } @@ -1373,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/contrib/chatDynamicVariables.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatDynamicVariables.ts index 0bf919d40f03..67848145eb8f 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatDynamicVariables.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatDynamicVariables.ts @@ -3,7 +3,8 @@ * 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'; @@ -35,7 +36,7 @@ import { IWorkspaceContextService } from '../../../../../platform/workspace/comm import { getExcludes, IFileQuery, ISearchComplete, ISearchConfiguration, ISearchService, QueryType } from '../../../../services/search/common/search.js'; import { ISymbolQuickPickItem } from '../../../search/browser/symbolsQuickAccess.js'; import { IDiagnosticVariableEntryFilterData } from '../../common/chatModel.js'; -import { IChatRequestVariableValue, IDynamicVariable } from '../../common/chatVariables.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'; @@ -348,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 @@ -392,6 +394,7 @@ export async function createFolderQuickPick(accessor: ServicesAccessor): Promise searchFolders( workspace, value, + true, undefined, undefined, configurationService, @@ -419,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), @@ -449,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: [{ @@ -468,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; @@ -479,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); @@ -505,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; } @@ -649,56 +683,132 @@ export class AddDynamicVariableAction extends Action2 { } registerAction2(AddDynamicVariableAction); -export async function createMarkersQuickPick(accessor: ServicesAccessor): Promise { - const markers = accessor.get(IMarkerService).read(); +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); - markers.sort((a, b) => uriIdentityService.extUri.compare(a.resource, b.resource) || b.severity - a.severity); + 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)[] = []; - for (const marker of markers) { - if (!uriIdentityService.extUri.isEqual(marker.resource, (items.at(-1) as MarkerPickItem)?.resource)) { - items.push({ type: 'separator', label: labelService.getUriLabel(marker.resource, { relative: true }) }); - } - 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: { filterUri: marker.resource, filterRange: { startLineNumber: marker.startLineNumber, endLineNumber: marker.endLineNumber, startColumn: marker.startColumn, endColumn: marker.endColumn } } - }); + 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 (items.length === 2) { // single error in a URI - return (items[1] as MarkerPickItem).entry; + if (pickCount < 2) { // single error in a URI + return items.find((i): i is MarkerPickItem => i.type === 'item')?.entry; } - if (items.length > 2) { - if (severities.has(MarkerSeverity.Error)) { - items.unshift({ type: 'item', label: localize('markers.panel.allErrors', 'All Errors'), entry: { filterSeverity: MarkerSeverity.Error } }); - } - if (severities.has(MarkerSeverity.Warning)) { - items.unshift({ type: 'item', label: localize('markers.panel.allWarnings', 'All Warnings'), entry: { filterSeverity: MarkerSeverity.Warning } }); - } - if (severities.has(MarkerSeverity.Info)) { - items.unshift({ type: 'item', label: localize('markers.panel.allInfos', 'All Infos'), entry: { filterSeverity: MarkerSeverity.Info } }); - } + 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 quickPick = quickInputService.createQuickPick({ useSeparators: true }); + 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 quickInputService.pick(items, { canPickMany: false }).then(v => v?.entry); + 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 f4673ffc8580..afcd62f22183 100644 --- a/src/vs/workbench/contrib/chat/browser/contrib/chatImplicitContext.ts +++ b/src/vs/workbench/contrib/chat/browser/contrib/chatImplicitContext.ts @@ -6,6 +6,7 @@ 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'; @@ -16,10 +17,11 @@ import { IConfigurationService } from '../../../../../platform/configuration/com 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'; @@ -44,7 +46,7 @@ export class ChatImplicitContextContribution extends Disposable implements IWork const activeEditorDisposables = this._register(new DisposableStore()); this._register(Event.runAndSubscribe( - editorService.onDidVisibleEditorsChange, + editorService.onDidActiveEditorChange, (() => { activeEditorDisposables.clear(); const codeEditor = this.findActiveCodeEditor(); @@ -58,6 +60,17 @@ 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(autorun((reader) => { @@ -79,12 +92,19 @@ export class ChatImplicitContextContribution extends Disposable implements IWork 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; } @@ -107,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(); @@ -134,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; diff --git a/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts b/src/vs/workbench/contrib/chat/browser/contrib/chatInputCompletions.ts index 0f4cd25c10dc..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 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 e336a86b0f20..b5f426737fd6 100644 --- a/src/vs/workbench/contrib/chat/browser/imageUtils.ts +++ b/src/vs/workbench/contrib/chat/browser/imageUtils.ts @@ -80,6 +80,17 @@ export function convertStringToUInt8Array(data: string): Uint8Array { 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) && (() => { diff --git a/src/vs/workbench/contrib/chat/browser/languageModelToolsService.ts b/src/vs/workbench/contrib/chat/browser/languageModelToolsService.ts index ab12b8c94625..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(); }); @@ -267,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 a542bb5c97b0..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; @@ -435,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; } @@ -569,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; @@ -692,7 +692,6 @@ have to be updated for changes to the rules above, or to support more deeply nes gap: 4px; margin-top: 6px; flex-wrap: wrap; - overflow: hidden; } .chat-related-files { @@ -700,6 +699,7 @@ have to be updated for changes to the rules above, or to support more deeply nes flex-wrap: wrap; align-items: center; gap: 4px; + max-width: 100%; } .chat-related-files .monaco-button-dropdown .monaco-text-button { @@ -724,8 +724,8 @@ have to be updated for changes to the rules above, or to support more deeply nes 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 { @@ -733,9 +733,11 @@ have to be updated for changes to the rules above, or to support more deeply nes flex-direction: column; color: var(--vscode-descriptionForeground); padding-top: 3px; + 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: 20px; + width: 17px; outline-offset: -2px !important; } @@ -861,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; @@ -890,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; @@ -898,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; } @@ -1041,14 +1058,6 @@ have to be updated for changes to the rules above, or to support more deeply nes border-style: dashed; opacity: 0.75; } -.chat-attached-context .chat-prompt-attachment .monaco-button { - border-left: 1px solid var(--vscode-chat-requestBorder, var(--vscode-input-background, transparent)); - margin-left: 3px; -} -.chat-attached-context .chat-prompt-attachment.error .monaco-button, -.chat-attached-context .chat-prompt-attachment.warning .monaco-button { - border-left-color: currentColor; -} .chat-attached-context .chat-prompt-attachment:focus .monaco-button { border-color: var(--vscode-focusBorder); } @@ -1120,12 +1129,20 @@ 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; - gap: 0px; 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; @@ -1143,8 +1160,53 @@ have to be updated for changes to the rules above, or to support more deeply nes .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; } @@ -1177,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, @@ -1197,6 +1262,7 @@ have to be updated for changes to the rules above, or to support more deeply nes flex-wrap: wrap; cursor: default; gap: 4px; + max-width: 100%; } .interactive-session .interactive-input-part.compact .chat-attached-context { @@ -1248,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; @@ -1265,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 { @@ -1659,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 { @@ -1723,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 index 331871acc2f0..9356b690797b 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chatEditingEditorOverlay.css +++ b/src/vs/workbench/contrib/chat/browser/media/chatEditingEditorOverlay.css @@ -98,12 +98,16 @@ color: var(--vscode-button-foreground); } -.chat-editor-overlay-widget .monaco-action-bar .action-item.disabled > .action-label.codicon::before, -.chat-editor-overlay-widget .monaco-action-bar .action-item.disabled > .action-label.codicon, -.chat-editor-overlay-widget .monaco-action-bar .action-item.disabled > .action-label, -.chat-editor-overlay-widget .monaco-action-bar .action-item.disabled > .action-label:hover { - color: var(--vscode-button-foreground); - opacity: 0.7; +.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; + } } 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 index 911602f593f2..82f35aebb209 100644 --- a/src/vs/workbench/contrib/chat/browser/media/chatStatus.css +++ b/src/vs/workbench/contrib/chat/browser/media/chatStatus.css @@ -5,71 +5,109 @@ /* Overall */ +.chat-status-bar-entry-tooltip { + margin-top: 4px; + margin-bottom: 4px; +} + .chat-status-bar-entry-tooltip hr { margin-top: 8px; margin-bottom: 8px; } -/* Settings */ - -.chat-status-bar-entry-tooltip .settings { - display: flex; - flex-direction: column; - gap: 5px; +.chat-status-bar-entry-tooltip div.header { + color: var(--vscode-descriptionForeground); + margin-bottom: 4px; + font-weight: 600; } -.chat-status-bar-entry-tooltip .settings .setting .codicon { - font-size: 16px; +.chat-status-bar-entry-tooltip div.description { + color: var(--vscode-descriptionForeground); } -.chat-status-bar-entry-tooltip .settings .setting .setting-label { - cursor: pointer; +.chat-status-bar-entry-tooltip .monaco-button { + margin-top: 5px; + margin-bottom: 5px; } -/* Shortcuts */ +/* Setup for New User */ -.chat-status-bar-entry-tooltip .shortcuts { +.chat-status-bar-entry-tooltip .setup .chat-feature-container { display: flex; - flex-direction: column; + align-items: center; gap: 5px; -} - -.chat-status-bar-entry-tooltip .shortcuts .shortcut { - display: flex; - gap: 10px; -} - -.chat-status-bar-entry-tooltip .shortcuts .shortcut .shortcut-label { - flex: 1; - cursor: pointer; -} - -.chat-status-bar-entry-tooltip .shortcuts .shortcut .monaco-keybinding { - cursor: pointer; + padding: 4px; } /* Quota Indicator */ .chat-status-bar-entry-tooltip .quota-indicator { - margin-top: 8px; - margin-bottom: 8px; + 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: 8px; - background-color: var(--vscode-activityBarBadge-foreground); - border-color: var(--vscode-activityBarBadge-background); - border-radius: 6px; + 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-activityBarBadge-background); - border-radius: 6px; + 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/promptSyntax/contributions/createPromptCommand/createPromptCommand.ts b/src/vs/workbench/contrib/chat/browser/promptSyntax/contributions/createPromptCommand/createPromptCommand.ts index 28e4f6b52a6b..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,13 +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. @@ -30,9 +33,9 @@ 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. @@ -40,9 +43,9 @@ const GLOBAL_COMMAND_ID = `${BASE_COMMAND_ID}.global`; 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 Global Prompt"); +const USER_COMMAND_TITLE = localize('commands.prompts.create.title.user', "Create User Prompt"); /** * The command implementation. @@ -95,7 +98,7 @@ 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); }; @@ -108,35 +111,39 @@ 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 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 68f93da4348f..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; 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 d449726d917d..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 @@ -9,8 +9,8 @@ import { assert } from '../../../../../../../../base/common/assert.js'; import { VSBuffer } from '../../../../../../../../base/common/buffer.js'; import { dirname } from '../../../../../../../../base/common/resources.js'; import { IFileService } from '../../../../../../../../platform/files/common/files.js'; -import { isPromptFile, PROMPT_FILE_EXTENSION } from '../../../../../../../../platform/prompts/common/constants.js'; import { ICommandService } from '../../../../../../../../platform/commands/common/commands.js'; +import { isPromptFile, PROMPT_FILE_EXTENSION } from '../../../../../../../../platform/prompts/common/constants.js'; /** * Options for the {@link createPromptFile} utility. 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 776433e6625e..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 { 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'; /** * Command ID of the "Use Prompt" command. */ -const COMMAND_ID = 'workbench.command.prompts.use'; +export const COMMAND_ID = 'workbench.command.prompts.use'; /** * 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 COMMAND_KEY_BINDING = KeyMod.Alt | KeyMod.Shift | KeyCode.KeyE; +const COMMAND_KEY_BINDING = KeyMod.CtrlCmd | KeyCode.Slash | KeyMod.Alt; /** * Implementation of the "Use Prompt" command. The command works in the following way. @@ -49,36 +55,17 @@ 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: COMMAND_ID, - weight: KeybindingWeight.WorkbenchContrib, - primary: COMMAND_KEY_BINDING, - handler: command, -}); - -/** - * Register the "Use Prompt" command in the `command palette`. - */ -appendToCommandPalette( - { - id: COMMAND_ID, - title: localize('commands.prompts.use.title', "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 48431090b77c..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,7 +67,6 @@ export interface IChatRelatedFilesProvider { export interface WorkingSetDisplayMetadata { state: WorkingSetEntryState; description?: string; - isMarkedReadonly?: boolean; } export interface IStreamingEdits { @@ -88,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; @@ -157,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 { @@ -262,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/chatEntitlementsService.ts b/src/vs/workbench/contrib/chat/common/chatEntitlementsService.ts deleted file mode 100644 index c0f6f1ce8470..000000000000 --- a/src/vs/workbench/contrib/chat/common/chatEntitlementsService.ts +++ /dev/null @@ -1,46 +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 { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; - -export const IChatEntitlementsService = createDecorator('chatEntitlementsService'); - -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 interface IChatEntitlements { - readonly entitlement: ChatEntitlement; - readonly quotas?: IQuotas; -} - -export interface IQuotas { - readonly chatTotal?: number; - readonly completionsTotal?: number; - - readonly chatRemaining?: number; - readonly completionsRemaining?: number; - - readonly resetDate?: string; -} - -export interface IChatEntitlementsService { - - _serviceBrand: undefined; - - resolve(token: CancellationToken): Promise; -} diff --git a/src/vs/workbench/contrib/chat/common/chatModel.ts b/src/vs/workbench/contrib/chat/common/chatModel.ts index a62958b43673..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,19 +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 { MarkerSeverity } from '../../../../platform/markers/common/markers.js'; +import { IMarker, MarkerSeverity } from '../../../../platform/markers/common/markers.js'; import { CellUri, ICellEditOperation } from '../../notebook/common/notebookCommon.js'; -import { ChatAgentLocation, IChatAgentCommand, IChatAgentData, IChatAgentResult, IChatAgentService, IChatWelcomeMessageContent, reviveSerializedAgent } from './chatAgents.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; @@ -91,30 +92,66 @@ 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.filterSeverity, data.filterRange?.startLineNumber].join(':'); + return [data.filterUri, data.owner, data.filterSeverity, data.filterRange?.startLineNumber].join(':'); } export function label(data: IDiagnosticVariableEntryFilterData) { - let labelStr: string; - if (data.filterSeverity) { - const sev = data.filterRange ? MarkerSeverity.toString(data.filterSeverity) : MarkerSeverity.toStringPlural(data.filterSeverity); - labelStr = data.filterUri - ? localize('chat.attachment.problems.severity', "{0} in {1}", sev, basename(data.filterUri)) - : localize('chat.attachment.problems.severity2', "All {0}", sev); - } else { - labelStr = data.filterUri - ? localize('chat.attachment.problems.severity3', "Problems in {0}", basename(data.filterUri)) - : localize('chat.attachment.problems.severity4', "All Problems"); + 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; @@ -911,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; @@ -1078,6 +1114,7 @@ export type IChatChangeEvent = | IChatSetAgentEvent | IChatMoveEvent | IChatSetHiddenEvent + | IChatCompletedRequestEvent ; export interface IChatAddRequestEvent { @@ -1090,6 +1127,11 @@ export interface IChatChangedRequestEvent { request: IChatRequestModel; } +export interface IChatCompletedRequestEvent { + kind: 'completedRequest'; + request: IChatRequestModel; +} + export interface IChatAddResponseEvent { kind: 'addResponse'; response: IChatResponseModel; @@ -1165,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; @@ -1391,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(); @@ -1549,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/chatQuotasService.ts b/src/vs/workbench/contrib/chat/common/chatQuotasService.ts deleted file mode 100644 index e79bcf34a58c..000000000000 --- a/src/vs/workbench/contrib/chat/common/chatQuotasService.ts +++ /dev/null @@ -1,136 +0,0 @@ -/*--------------------------------------------------------------------------------------------- - * Copyright (c) Microsoft Corporation. All rights reserved. - * Licensed under the MIT License. See License.txt in the project root for license information. - *--------------------------------------------------------------------------------------------*/ - -import { Emitter, Event } from '../../../../base/common/event.js'; -import { Disposable } from '../../../../base/common/lifecycle.js'; -import { IContextKeyService } from '../../../../platform/contextkey/common/contextkey.js'; -import { createDecorator } from '../../../../platform/instantiation/common/instantiation.js'; -import { ChatContextKeys } from './chatContextKeys.js'; - -export const IChatQuotasService = createDecorator('chatQuotasService'); - -export interface IChatQuotasService { - - _serviceBrand: undefined; - - readonly onDidChangeQuotaExceeded: Event; - readonly onDidChangeQuotaRemaining: Event; - - readonly quotas: IChatQuotas; - - acceptQuotas(quotas: IChatQuotas): void; - clearQuotas(): void; -} - -export interface IChatQuotas { - chatQuotaExceeded: boolean; - completionsQuotaExceeded: boolean; - quotaResetDate: Date | undefined; - - chatTotal?: number; - completionsTotal?: number; - - chatRemaining?: number; - completionsRemaining?: number; -} - -export class ChatQuotasService extends Disposable implements IChatQuotasService { - - declare _serviceBrand: undefined; - - 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(): 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(); - } - - 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._onDidChangeQuotaExceeded.fire(); - } - })); - } - - acceptQuotas(quotas: IChatQuotas): void { - const oldQuota = this._quotas; - this._quotas = this.massageQuotas(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(); - } - } - - private massageQuotas(quotas: IChatQuotas): IChatQuotas { - return { - ...quotas, - chatTotal: typeof quotas.chatTotal === 'number' ? Math.floor(quotas.chatTotal / 10) : undefined, - chatRemaining: typeof quotas.chatRemaining === 'number' ? Math.floor(Math.max(0, quotas.chatRemaining) / 10) : undefined, - completionsRemaining: typeof quotas.completionsRemaining === 'number' ? Math.max(0, quotas.completionsRemaining) : undefined - }; - } - - 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); - } -} 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 9a6a32cb43e6..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 { @@ -404,7 +412,7 @@ export interface IChatTransferredSessionData { sessionId: string; inputValue: string; location: ChatAgentLocation; - toolsAgentModeEnabled: boolean; + mode: ChatMode; } export interface IChatSendRequestResponseState { @@ -437,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; @@ -498,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 f519d27d9323..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'; @@ -45,7 +47,7 @@ interface IChatTransfer { chat: ISerializableChatData; inputValue: string; location: ChatAgentLocation; - toolsAgentModeEnabled: boolean; + mode: ChatMode; } const SESSION_TRANSFER_EXPIRATION_IN_MILLISECONDS = 1000 * 60; @@ -134,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, @@ -171,7 +178,7 @@ export class ChatService extends Disposable implements IChatService { sessionId: transferredChat.sessionId, inputValue: transferredData.inputValue, location: transferredData.location, - toolsAgentModeEnabled: transferredData.toolsAgentModeEnabled, + mode: transferredData.mode, }; } @@ -197,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`); } @@ -423,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); @@ -454,7 +479,8 @@ export class ChatService extends Disposable implements IChatService { const isTransferred = this.transferredSessionData?.sessionId === sessionId; if (isTransferred) { - this.chatAgentService.toggleToolsAgentMode(this.transferredSessionData.toolsAgentModeEnabled); + // TODO + // this.chatAgentService.toggleToolsAgentMode(this.transferredSessionData.toolsAgentModeEnabled); this._transferredSessionData = undefined; } @@ -482,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); @@ -535,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; @@ -556,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}`; } @@ -678,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); @@ -872,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; @@ -1012,12 +1039,16 @@ export class ChatService extends Disposable implements IChatService { toWorkspace: toWorkspace, inputValue: transferredSessionData.inputValue, location: transferredSessionData.location, - toolsAgentModeEnabled: transferredSessionData.toolsAgentModeEnabled, + 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/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/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/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 8a692ee4cf0a..24869cbc2b61 100644 --- a/src/vs/workbench/contrib/chat/common/languageModelToolsService.ts +++ b/src/vs/workbench/contrib/chat/common/languageModelToolsService.ts @@ -37,6 +37,7 @@ export interface IToolInvocation { tokenBudget?: number; context: IToolInvocationContext | undefined; chatRequestId?: string; + chatInteractionId?: string; toolSpecificData?: IChatTerminalToolInvocationData; } @@ -67,6 +68,7 @@ export interface IToolResultTextPart { export interface IToolConfirmationMessages { title: string; message: string | IMarkdownString; + toolInput?: object; } export interface IPreparedToolInvocation { 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 84e7d62551b6..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,36 +43,36 @@ 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. */ @@ -89,6 +91,12 @@ export class OpenFailed extends FailedToResolveContentsStream { } } +/** + * 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 OpenFailed 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; } /** 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 512c29e98c23..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 { OpenFailed, NotPromptFile, ParseError, FolderReference } from '../../promptFileReferenceErrors.js'; +import { OpenFailed, NotPromptFile, ResolveError, FolderReference } from '../../promptFileReferenceErrors.js'; import { FileChangesEvent, FileChangeType, IFileService } from '../../../../../../platform/files/common/files.js'; /** @@ -81,7 +81,7 @@ 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/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 937c7e6b1bb0..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,24 +14,17 @@ 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 { OpenFailed, NotPromptFile, RecursiveReference, FolderReference, 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. @@ -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,7 +368,7 @@ 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) => { @@ -399,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 NotPromptFile); - }) - // 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; } @@ -439,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 OpenFailed) { - 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, + }); } /** @@ -546,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 { @@ -575,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 27b3a2b81208..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'; @@ -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 4797a66c721c..833dbc57c4ad 100644 --- a/src/vs/workbench/contrib/chat/test/common/mockChatService.ts +++ b/src/vs/workbench/contrib/chat/test/common/mockChatService.ts @@ -6,10 +6,10 @@ 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; @@ -90,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/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/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/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/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/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 f1879eac040f..0965dcaf6361 100644 --- a/src/vs/workbench/contrib/debug/browser/breakpointsView.ts +++ b/src/vs/workbench/contrib/debug/browser/breakpointsView.ts @@ -224,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/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/debugEditorContribution.ts b/src/vs/workbench/contrib/debug/browser/debugEditorContribution.ts index 34ef1b0806bd..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'; @@ -261,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; @@ -281,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]; 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/repl.ts b/src/vs/workbench/contrib/debug/browser/repl.ts index a7dbf5cdc76b..930812de0f99 100644 --- a/src/vs/workbench/contrib/debug/browser/repl.ts +++ b/src/vs/workbench/contrib/debug/browser/repl.ts @@ -155,7 +155,7 @@ export class Repl extends FilterViewPane implements IHistoryNavigationWidget { 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); 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 cbb4d0cbd4e3..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'; 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 d7ab04b698f4..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,7 +23,7 @@ import { DebugExtensionHostAction, DebugExtensionsContribution } from './debugEx import { ExtensionHostProfileService } from './extensionProfileService.js'; import { CleanUpExtensionsFolderAction, OpenExtensionsFolderAction } from './extensionsActions.js'; import { ExtensionsAutoProfiler } from './extensionsAutoProfiler.js'; -import { InstallFailedRemoteExtensionsContribution, RemoteExtensionsInitializerContribution } from './remoteExtensionsInit.js'; +import { InstallRemoteExtensionsContribution, RemoteExtensionsInitializerContribution } from './remoteExtensionsInit.js'; import { IExtensionHostProfileService, OpenExtensionHostProfileACtion, RuntimeExtensionsEditor, SaveExtensionHostProfileAction, StartExtensionHostProfileAction, StopExtensionHostProfileAction } from './runtimeExtensionsEditor.js'; // Singletons @@ -71,7 +71,7 @@ const workbenchRegistry = Registry.as(Workbench workbenchRegistry.registerWorkbenchContribution(ExtensionsContributions, LifecyclePhase.Restored); workbenchRegistry.registerWorkbenchContribution(ExtensionsAutoProfiler, LifecyclePhase.Eventually); workbenchRegistry.registerWorkbenchContribution(RemoteExtensionsInitializerContribution, LifecyclePhase.Restored); -workbenchRegistry.registerWorkbenchContribution(InstallFailedRemoteExtensionsContribution, LifecyclePhase.Restored); +workbenchRegistry.registerWorkbenchContribution(InstallRemoteExtensionsContribution, LifecyclePhase.Restored); workbenchRegistry.registerWorkbenchContribution(DebugExtensionsContribution, LifecyclePhase.Restored); // Register Commands diff --git a/src/vs/workbench/contrib/extensions/electron-sandbox/remoteExtensionsInit.ts b/src/vs/workbench/contrib/extensions/electron-sandbox/remoteExtensionsInit.ts index 9bdf631a0bc5..1b0fadda14f0 100644 --- a/src/vs/workbench/contrib/extensions/electron-sandbox/remoteExtensionsInit.ts +++ b/src/vs/workbench/contrib/extensions/electron-sandbox/remoteExtensionsInit.ts @@ -4,13 +4,17 @@ *--------------------------------------------------------------------------------------------*/ import { CancellationToken } from '../../../../base/common/cancellation.js'; +import { IConfigurationService } from '../../../../platform/configuration/common/configuration.js'; import { IEnvironmentService } from '../../../../platform/environment/common/environment.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'; @@ -26,17 +30,86 @@ import { IExtensionManagementServerService } from '../../../services/extensionMa import { IExtensionManifestPropertiesService } from '../../../services/extensions/common/extensionManifestPropertiesService.js'; import { IRemoteAgentService } from '../../../services/remote/common/remoteAgentService.js'; -export class InstallFailedRemoteExtensionsContribution implements IWorkbenchContribution { +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 + @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; 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/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 b23098134a1a..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, } }); } @@ -577,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 55da3ca89eb9..9f2458018bd7 100644 --- a/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts +++ b/src/vs/workbench/contrib/inlineChat/browser/inlineChatController.ts @@ -12,7 +12,7 @@ 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'; @@ -41,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'; @@ -54,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', @@ -494,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(); + } } })); @@ -802,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; @@ -1450,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 8071173c0483..75930b380dd8 100644 --- a/src/vs/workbench/contrib/inlineCompletions/browser/inlineCompletionLanguageStatusBarContribution.ts +++ b/src/vs/workbench/contrib/inlineCompletions/browser/inlineCompletionLanguageStatusBarContribution.ts @@ -5,24 +5,29 @@ 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, @@ -51,11 +56,20 @@ export class InlineCompletionLanguageStatusBarContribution extends Disposable { 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: localize('inlineSuggestions', "Inline Suggestions"), + detail: localize('inlineSuggestionsSmall', "Inline suggestions"), id: 'inlineSuggestions', label: { value: statusMap[status].label, shortValue: statusMap[status].shortLabel }, name: localize('inlineSuggestions', "Inline Suggestions"), 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/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 b8123bc8c092..000000000000 --- a/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/contribution.ts +++ /dev/null @@ -1,21 +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'; -import { registerWorkbenchContribution2, WorkbenchPhase } from '../../../../../common/contributions.js'; -import { ChatEditingNotebookFileSystemProviderContrib } from './chatEditingNotebookFileSystemProvider.js'; - - -registerNotebookContribution(NotebookChatEditorControllerContrib.ID, NotebookChatEditorControllerContrib); -registerSingleton(INotebookOriginalModelReferenceFactory, NotebookOriginalModelReferenceFactory, InstantiationType.Delayed); -registerSingleton(INotebookModelSynchronizerFactory, NotebookModelSynchronizerFactory, InstantiationType.Delayed); -registerSingleton(INotebookOriginalCellModelFactory, OriginalNotebookCellModelFactory, InstantiationType.Delayed); - -registerWorkbenchContribution2(ChatEditingNotebookFileSystemProviderContrib.ID, ChatEditingNotebookFileSystemProviderContrib, WorkbenchPhase.BlockStartup); 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 5669e5b1ed97..000000000000 --- a/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookChatActionsOverlay.ts +++ /dev/null @@ -1,323 +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'; -import { ChatEditingModifiedDocumentEntry } from '../../../../chat/browser/chatEditing/chatEditingModifiedDocumentEntry.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)); - const entry = session?.readEntry(model.uri, r); - if (!session || !entry || !(entry instanceof ChatEditingModifiedDocumentEntry)) { - 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 this._register(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 this._register(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 this._register(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 = this._register(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 32cce7d96854..000000000000 --- a/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookChatEditController.ts +++ /dev/null @@ -1,211 +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'; -import { ChatEditingModifiedDocumentEntry } from '../../../../chat/browser/chatEditing/chatEditingModifiedDocumentEntry.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, undefined)); - 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); - const entry = sessions.map(s => s.readEntry(model.uri, r)).find(r => !!r); - return entry instanceof ChatEditingModifiedDocumentEntry ? entry : undefined; - }).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 dbebccbe2f92..000000000000 --- a/src/vs/workbench/contrib/notebook/browser/contrib/chatEdit/notebookSynchronizer.ts +++ /dev/null @@ -1,500 +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'; -import { ChatEditingModifiedDocumentEntry } from '../../../../chat/browser/chatEditing/chatEditingModifiedDocumentEntry.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); - const entry = sessions.map(s => s.readEntry(model.uri, r)).find(r => !!r); - return entry instanceof ChatEditingModifiedDocumentEntry ? entry : undefined; - }).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/multicursor/notebookMulticursor.ts b/src/vs/workbench/contrib/notebook/browser/contrib/multicursor/notebookMulticursor.ts index 8ce231cbd249..116d5d3fab9d 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/multicursor/notebookMulticursor.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/multicursor/notebookMulticursor.ts @@ -1035,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 38164a2b381e..571038352440 100644 --- a/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookInlineVariables.ts +++ b/src/vs/workbench/contrib/notebook/browser/contrib/notebookVariables/notebookInlineVariables.ts @@ -12,6 +12,7 @@ import { isEqual } from '../../../../../../base/common/resources.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, ITextModel } from '../../../../../../editor/common/model.js'; import { ILanguageFeaturesService } from '../../../../../../editor/common/services/languageFeatures.js'; @@ -42,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, @@ -236,10 +239,18 @@ 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 @@ -248,46 +259,47 @@ 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; - let lineSegments = lineDecorations.get(lastMatchOutsideFunction.line); + let lineSegments = lineDecorations.get(lastMatchOutsideIgnored.line); if (!lineSegments) { lineSegments = []; - lineDecorations.set(lastMatchOutsideFunction.line, lineSegments); + lineDecorations.set(lastMatchOutsideIgnored.line, lineSegments); } if (!lineSegments.some(iv => iv.text === inlineVal)) { // de-dupe - lineSegments.push(new InlineSegment(lastMatchOutsideFunction.column, inlineVal)); + lineSegments.push(new InlineSegment(lastMatchOutsideIgnored.column, inlineVal)); } } @@ -414,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( @@ -428,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); })); @@ -462,6 +626,8 @@ export class NotebookInlineVariablesController extends Disposable implements INo this._clearNotebookInlineDecorations(); this.currentCancellationTokenSources.forEach(source => source.cancel()); this.currentCancellationTokenSources.clear(); + this.cellContentListeners.forEach(listener => listener.dispose()); + this.cellContentListeners.clear(); } } 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 2d9733f68ec2..b75579cab137 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookDeletedCellDecorator.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/inlineDiff/notebookDeletedCellDecorator.ts @@ -46,6 +46,10 @@ 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 this._notebookEditor.getLayoutInfo().height + info.offset; @@ -65,7 +69,7 @@ export class NotebookDeletedCellDecorator extends Disposable implements INoteboo const info = this.deletedCellInfos.get(deletedIndex); if (info) { - const prevIndex = info.previousIndex; + const prevIndex = info.previousIndex === -1 ? 0 : info.previousIndex; this._notebookEditor.setFocus({ start: prevIndex, end: prevIndex }); this._notebookEditor.setSelections([{ start: prevIndex, end: prevIndex }]); } 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 0b1668d0711b..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'; @@ -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/notebookDiffEditor.ts b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditor.ts index 81689f503bd0..639e2fcd80d3 100644 --- a/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/diff/notebookDiffEditor.ts @@ -289,6 +289,8 @@ export class NotebookTextDiffEditor extends EditorPane implements INotebookTextD } async toggleInlineView(): Promise { + this._layoutCancellationTokenSource?.dispose(); + this._inlineView = !this._inlineView; if (!this._lastLayoutProperties) { @@ -302,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 { 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/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 7234b9b4143b..b289cdb6142c 100644 --- a/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts +++ b/src/vs/workbench/contrib/notebook/browser/notebookEditor.ts @@ -332,6 +332,7 @@ export class NotebookEditor extends EditorPane implements INotebookEditorPane, I } this._handlePerfMark(perf, input, model.notebook); + this._onDidChangeControl.fire(); } catch (e) { this.logService.warn('NotebookEditorWidget#setInput failed', e); if (isEditorOpenError(e)) { 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/renderers/backLayerWebView.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/backLayerWebView.ts index f33e4a2dfbed..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; 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/webviewPreloads.ts b/src/vs/workbench/contrib/notebook/browser/view/renderers/webviewPreloads.ts index 5b7077bf1d11..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